Window events, math, and asdf

Window resize events are now sent from the renderer to lisp.
On the lisp side, some math code has been written, but not yet
integrated into the project.
This commit is contained in:
FroggyGreen 2024-05-13 19:05:28 -05:00
parent 1ec76f56c8
commit 207de345f7
13 changed files with 638 additions and 60 deletions

View File

@ -1,11 +1,14 @@
# dependencies: Steel Bank Common Lisp (SBCL)
PROJECT_DIR := $(PWD)
all: sof
sof:
rm -rf bin
mkdir -p bin
sbcl --load src/main.lisp \
--eval "(sb-ext:save-lisp-and-die \"bin/sof\" :toplevel 'main :executable t)"
sbcl --load "$(PROJECT_DIR)/sof.asd" \
--eval "(asdf:load-system :sof)" \
--eval "(sb-ext:save-lisp-and-die \"bin/sof\" :toplevel 'sof-game:main :executable t)"
.PHONY: all sof

View File

@ -28,7 +28,8 @@ add_library(sofrenderer SHARED
"src/renderer.c"
"src/containers/darray.c"
"src/containers/dblist.c"
"src/containers/hashtable.c")
"src/containers/hashtable.c"
"src/containers/ringqueue.c")
target_include_directories(sofrenderer PRIVATE "${PROJECT_SOURCE_DIR}/src")
set_target_properties(sofrenderer PROPERTIES
C_STANDARD 99

View File

@ -0,0 +1,82 @@
/*
Copyright (C) 2024 Frosch
This file is part of Shape of Fantasy.
Shape of Fantasy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Shape of Fantasy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Shape of Fantasy. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ringqueue.h"
#include "logger.h"
#include <string.h>
u8 init_ring_queue(ring_queue* q, void* buffer, u64 capacity, u64 stride)
{
if (!stride)
{
trace_log(LOG_ERROR, "Failed to init ring queue; stride '%d' must be nonzero.", stride);
return 0;
}
if (!capacity || ((capacity + 1) & capacity))
{
trace_log(LOG_ERROR, "Failed to init ring queue; capacity '%d' must be a power of two minus one and nonzero.", capacity);
return 0;
}
q->in = 0;
q->out = 0;
q->capacity = capacity;
q->stride = stride;
q->buffer = buffer;
return 1;
}
void shutdown_ring_queue(ring_queue* q)
{
q->buffer = NULL;
q->in = 0;
q->out = 0;
q->capacity = 0;
q->stride = 0;
}
u8 ring_queue_pushback(ring_queue* q, const void* src)
{
if (is_ring_queue_full(q))
{
trace_log(LOG_ERROR, "Ring queue pushback failed; the queue is full.");
return 0;
}
u64 address = (u64)q->buffer;
void* dst = (void*)(address + q->in * q->stride);
memcpy(dst, src, q->stride);
q->in = (q->in + 1) & q->capacity;
return 1;
}
u8 ring_queue_popfront(ring_queue* q, void** dst)
{
if (is_ring_queue_empty(q))
{
trace_log(LOG_ERROR, "Ring queue popfront failed; the queue is empty.");
return 0;
}
if (dst != NULL)
{
u64 address = (u64)q->buffer;
void* src = (void*)(address + q->out * q->stride);
*dst = src;
}
q->out = (q->out + 1) & q->capacity;
return 1;
}

View File

@ -0,0 +1,32 @@
#include "defines.h"
#include <stddef.h>
/* Ring Queue:
* A FIFO data structure. The queue does not manage memory internally.
*
* int a = 5;
* RingQueuePushback(q, &a); // 'a' is copied to the buffer (which must be allocated externally).
* void* b;
* RingQueuePopfront(q, &b); // 'b' is (void*)((U64)buffer + q->out * q->stride)
*
* A ring queue consists of the following:
* void* buffer - Pointer to the buffer which holds the queue elements. The buffer must have at least (capacity + 1) * stride amount of allocated bytes.
* U64 in, out - Indices to help with pushing and popping.
* U64 capacity - The total number. Must be a power of 2 minus 1 and nonzero.
* U64 stride - Size of an element in bytes. Must be nonzero.
*/
typedef struct ring_queue
{
void* buffer;
u64 in, out, capacity, stride;
} ring_queue;
u8 init_ring_queue(ring_queue* q, void* buffer, u64 capacity, u64 stride);
void shutdown_ring_queue(ring_queue* q);
u8 ring_queue_pushback(ring_queue* q, const void* src);
u8 ring_queue_popfront(ring_queue* q, void** dst);
static inline u8 is_ring_queue_full(const ring_queue* q) { return q->in == ((q->out + q->capacity) & q->capacity); }
static inline u8 is_ring_queue_empty(const ring_queue* q) { return q->in == q->out; }
static inline void* ring_queue_peak(ring_queue* q) { return is_ring_queue_empty(q) ? NULL : (void*)((u64)q->buffer + q->out * q->stride); }

View File

@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with Sha
#include "containers/darray.h"
#include "containers/dblist.h"
#include "containers/hashtable.h"
#include "containers/ringqueue.h"
#include <stdio.h>
#include <stdlib.h>
@ -139,6 +140,10 @@ typedef struct swap_chain_support_details
} swap_chain_support_details;
static GLFWwindow* win = 0;
static ring_queue window_event_queue;
#define MAX_WINDOW_EVENTS_QUEUE_CAPACITY 512 - 1
static window_event window_event_buffer[MAX_WINDOW_EVENTS_QUEUE_CAPACITY + 1];
static vulkan_context context;
#ifdef RENDERER_DEBUG
static const char* validation_layers[] = { "VK_LAYER_KHRONOS_validation" };
@ -149,19 +154,48 @@ static const char* device_extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
// Window
//
static void resize_framebuffer_callback(GLFWwindow* window, int width, int height)
u8 can_pop_window_event()
{
return !is_ring_queue_empty(&window_event_queue);
}
void pop_window_event(window_event* event)
{
void* ptr;
ring_queue_popfront(&window_event_queue, &ptr);
memcpy(event, ptr, sizeof(window_event));
}
static void window_resize_framebuffer_callback(GLFWwindow* window, int width, int height)
{
context.is_framebuffer_resized = 1;
window_event event =
{
.id = WINDOW_EVENT_ID_RESIZE,
.handle =
{
.window_resize =
{
.width = width,
.height = height
}
}
};
ring_queue_pushback(&window_event_queue, &event);
}
u8 init_window(const char* name, int width, int height)
{
init_ring_queue(&window_event_queue, window_event_buffer, MAX_WINDOW_EVENTS_QUEUE_CAPACITY, sizeof(window_event));
if (!glfwInit()) return 0;
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
win = glfwCreateWindow(width, height, name, NULL, NULL);
if (!win) return 0;
glfwSetFramebufferSizeCallback(win, resize_framebuffer_callback);
glfwSetFramebufferSizeCallback(win, window_resize_framebuffer_callback);
return 1;
}
@ -171,6 +205,8 @@ void shutdown_window()
glfwDestroyWindow(win);
glfwTerminate();
win = 0;
shutdown_ring_queue(&window_event_queue);
}
u8 is_window_open()

View File

@ -1,9 +1,8 @@
//
// resources:
// Resources
//
// vulkan-tutorial.com
// vkguide.dev
//
#pragma once
@ -17,6 +16,32 @@
typedef enum
{
WINDOW_EVENT_ID_RESIZE,
} window_event_id;
typedef struct window_resize_event
{
int width;
int height;
} window_resize_event;
typedef union window_event_handle
{
window_resize_event window_resize;
} window_event_handle;
typedef struct window_event
{
window_event_id id;
window_event_handle handle;
} window_event;
typedef enum
{
VERTEX_INPUT_TYPE_POS2,
VERTEX_INPUT_TYPE_POS2_POS2_COLOR4
@ -73,6 +98,8 @@ typedef enum
//
//
u8 can_pop_window_event();
void pop_window_event(window_event* event);
u8 init_window(const char* name, int width, int height);
void shutdown_window();
u8 is_window_open();

22
sof.asd Normal file
View File

@ -0,0 +1,22 @@
(in-package #:cl-user)
(defpackage #:sof-asd
(:use :cl :asdf))
(in-package :sof-asd)
(defsystem "sof"
:version "0.0.1"
:description "A 3D roguelike/tabletop inspired adventure game."
:author "Frosch"
:license "GPLv3"
:depends-on (#:cffi-libffi)
:components
((:module "src"
:components
((:file "package")
(:file "main")
(:module "math"
:components
((:file "vec")
(:file "quat")))))))

View File

@ -12,10 +12,7 @@ You should have received a copy of the GNU General Public License along with Sha
|#
(defpackage :shape-of-fantasy-game
(:use :common-lisp))
(ql:quickload :cffi)
(in-package :sof-game)
;;;
;;; Renderer bindings
@ -28,6 +25,20 @@ You should have received a copy of the GNU General Public License along with Sha
(unless (cffi:foreign-library-loaded-p 'libsofrenderer) (cffi:use-foreign-library libsofrenderer))
(cffi:defcenum window-event-id
:window-event-id-resize)
(cffi:defcstruct %window-resize-event
(width :int)
(height :int))
(cffi:defcunion %window-event-handle
(window-resize (:struct %window-resize-event)))
(cffi:defcstruct %window-event
(id window-event-id)
(handle (:union %window-event-handle)))
(cffi:defcenum vertex-input-type
:vertex-input-type-pos2
:vertex-input-type-pos2-pos2-color4)
@ -173,6 +184,11 @@ You should have received a copy of the GNU General Public License along with Sha
:mesh-pipeline-mode-main
:mesh-pipeline-mode-wireframe)
(cffi:defcfun ("can_pop_window_event" pop-window-event?) :boolean )
(cffi:defcfun ("pop_window_event" pop-window-event) :void
(event (:pointer (:struct %window-event))))
(cffi:defcfun ("init_window" init-window) :boolean
(name :string)
(width :int)
@ -277,6 +293,18 @@ You should have received a copy of the GNU General Public License along with Sha
(shutdown-vulkan)
(shutdown-window))
(defun process-window-resize (handle)
(cffi:with-foreign-slots ((window-resize) handle (:union %window-event-handle))
(format t "~a, ~a" (second window-resize) (fourth window-resize))))
(defun process-window-input ()
(poll-window-input)
(loop while (pop-window-event?) do
(cffi:with-foreign-object (event '(:struct %window-event))
(pop-window-event event)
(cffi:with-foreign-slots ((id handle) event (:struct %window-event))
(cond ((eq id :window-event-id-resize) (process-window-resize handle)))))))
(defun update-game () )
(defun render-game (l)
@ -285,20 +313,13 @@ You should have received a copy of the GNU General Public License along with Sha
(when (draw-mesh-pipeline "ui-pane")
(when (end-render-frame) t))))
;; Only compile this ONCE!
#+slynk
(defun send-prompt ()
(slynk-mrepl:send-prompt))
(defun run-game ()
#+slynk
(send-prompt)
(let ((lag 0.0) (prev-time (get-internal-real-time)) (curr-time 0))
(loop :while (and *game-running?* (window-open?)) :do
(poll-window-input)
(loop while (and *game-running?* (window-open?)) do
(process-window-input)
;; In the sly repl, (if (slynk-mrepl:send-prompt) (sof-game:main)
#+slynk
(with-simple-restart (continue-game "Continue")
(let ((connection (or slynk::*emacs-connection* (slynk::default-connection))))
@ -309,7 +330,7 @@ You should have received a copy of the GNU General Public License along with Sha
(setf prev-time curr-time)
(setf dt (if (< dt *max-frame-time*) dt *max-frame-time*))
(setf lag (+ lag dt))
(loop :while (>= lag *frame-time*) :do
(loop while (>= lag *frame-time*) do
(update-game)
(setf lag (- lag *frame-time*)))

141
src/math/mat.lisp Normal file
View File

@ -0,0 +1,141 @@
#|
Copyright (C) 2024 Frosch
This file is part of Shape of Fantasy.
Shape of Fantasy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Shape of Fantasy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Shape of Fantasy. If not, see <https://www.gnu.org/licenses/>.
|#
;;; matrices with column-major order notation
;;;
;;; Resources
;;;
;;; https://www.songho.ca/math/quaternion/quaternion.html
;;; https://www.songho.ca/opengl/gl_transform.html
;;; https://developer.nvidia.com/content/depth-precision-visualized
(in-package :sof-mat)
(defmacro fill-mat (value dim)
`(vector
,@(loop for i from 0 below dim
collect `(vector
,@(loop for j from 0 below dim
collect value)))))
(defun fill-mat2 (value) (fill-mat value 2))
(defun fill-mat3 (value) (fill-mat value 3))
(defun fill-mat4 (value) (fill-mat value 4))
(defmacro diag->mat (d dim)
`(vector
,@(loop for i from 0 below dim
collect `(vector
,@(loop for j from 0 below dim
collect (if (= i j) `(aref ,d ,i) 0))))))
(defun diag->mat2 (d) (diag->mat d 2))
(defun diag->mat3 (d) (diag->mat d 3))
(defun diag->mat4 (d) (diag->mat d 4))
(defconstant +id-mat2+ (diag->mat2 #(1 1)))
(defconstant +id-mat3+ (diag->mat3 #(1 1 1)))
(defconstant +id-mat4+ (diag->mat4 #(1 1 1 1)))
(defmacro transpose-mat (m dim)
`(vector
,@(loop for i from 0 below dim
collect `(vector
,@(loop for j from 0 below dim
collect `(aref (aref ,m ,j) ,i))))))
(defun transpose-mat2 (m) (transpose-mat m 2))
(defun transpose-mat3 (m) (transpose-mat m 3))
(defun transpose-mat4 (m) (transpose-mat m 4))
(defmacro mult-mat (m1 m2 dim)
`(vector
,@(loop for j from 0 below dim
collect `(vector
,@(loop for i from 0 below dim
collect `(loop for k from 0 below ,dim
sum (* (aref (aref ,m1 k) ,i) (aref (aref ,m2 ,j) k))))))))
(defun mult-mat2 (m1 m2) (mult-mat m1 m2 2))
(defun mult-mat3 (m1 m2) (mult-mat m1 m2 3))
(defun mult-mat4 (m1 m2) (mult-mat m1 m2 4))
(defmacro mult-mat-diag (m d dim)
`(vector
,@(loop for i from 0 below dim
collect `(scaled-vec3 (aref ,m ,i) (aref ,d ,i)))))
(defun mult-mat2-diag (m d) (mult-mat-diag m d 2))
(defun mult-mat3-diag (m d) (mult-mat-diag m d 3))
(defun mult-mat3-diag (m d) (mult-mat-diag m d 4))
(defmacro mult-mat-vec (m v dim)
`(vector
,@(loop for i from 0 below dim
collect `(loop for k from 0 below ,dim
sum (* (aref (aref ,m k) ,i) (aref ,v k))))))
(defun mult-mat2-vec (m v) (mult-mat-vec m v 2))
(defun mult-mat3-vec (m v) (mult-mat-vec m v 3))
(defun mult-mat4-vec (m v) (mult-mat-vec m v 4))
(defun versor->mat3 (vrs)
(let* ((w (aref vrs 0)) (x (aref vrs 1)) (y (aref vrs 2)) (z (aref vrs 3))
(xx (* x x)) (yy (* y y)) (zz (* z z))
(wx (* w x)) (wy (* w y)) (wz (* z w))
(xy (* x y)) (xz (* x z)) (yz (* y z)))
(vector
(vector
(- 1 (* 2 (+ yy zz)))
(* 2 (+ xy wz))
(* 2 (- xz wy)))
(vector
(* 2 (- xy wz))
(- 1 (* 2 (+ xx zz)))
(* 2 (+ yz wx)))
(vector
(* 2 (+ xz wy))
(* 2 (- yz wx))
(- 1 (* 2 (+ xx yy)))))))
(defun look-at (camera-pos target-pos world-up-dir)
"Retrieve the view matrix of a camera."
(let*
((forward (normalize-vec3 (- camera-pos target-pos)))
(left (normalize-vec3 (cross-vec3 world-up-dir forward)))
(up (cross-vec3 forward left)))
(vector
(vector 1 (- (dot-vec3 left camera-pos)) (- (dot-vec3 up camera-pos)) (- (dot-vec3 forward camera-pos)))
(vector 0 (aref left 0) (aref up 0) (aref forward 0))
(vector 0 (aref left 1) (aref up 1) (aref forward 1))
(vector 0 (aref left 2) (aref up 2) (aref forward 2)))))
(defun perspective-projection (fov ratio near far)
"Let cot = cotangent(fov/2). This maps a frustrum [-cot, cot]x[-cot/ratio, cot/ratio]x[near, far] (x and y dimensions are given on the near plane) to the rectangular prism [-1, 1]x[1, -1]x[1, 0] (Vulkan clip space).
We are projecting onto the near plane and choosing a reverse-z convention: higher depth values correspond to closer positions."
(let ((cot-fov (/ 1 (tan (/ fov 2)))))
(vector
(vector 0 0 0 (/ (* near far) (- far near)))
(vector 0 cot-fov 0 0)
(vector 0 0 (- (/ cot-fov ratio)) 0)
(vector 1 0 0 (/ near (- near far))))))
(defun orthographic-projecion (width height near far)
"This maps a rectangular prism [0, width]x[0, height]x[near, far] to the rectangular prism [-1, 1]x[1, -1]x[1, 0] (Vulkan clip space).
We are choosing a reverse-z convention: higher depth values correspond to closer positions."
(vector
(vector 1 -1 1 (/ far (- far near)))
(vector 0 (/ 2 width) 0 0)
(vector 0 0 (- (/ 2 height)) 0)
(vector 0 0 0 (/ 1 (- near far)))))

View File

@ -1,37 +0,0 @@
#|
Copyright (C) 2024 Frosch
This file is part of Shape of Fantasy.
Shape of Fantasy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Shape of Fantasy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Shape of Fantasy. If not, see <https://www.gnu.org/licenses/>.
|#
(defpackage :shape-of-fantasy-math
(:use :common-lisp))
(in-package :shape-of-fantasy-math)
(defun add-vec2 (v1 v2)
(vector
(+ (aref v1 0) (aref v2 0))
(+ (aref v1 1) (aref v2 1))))
(defun add-vec3 (v1 v2)
(vector
(+ (aref v1 0) (aref v2 0))
(+ (aref v1 1) (aref v2 1))
(+ (aref v1 2) (aref v2 2))))
(defun add-vec4 (v1 v2)
(vector
(+ (aref v1 0) (aref v2 0))
(+ (aref v1 1) (aref v2 1))
(+ (aref v1 2) (aref v2 2))
(+ (aref v1 3) (aref v2 3))))

73
src/math/quat.lisp Normal file
View File

@ -0,0 +1,73 @@
#|
Copyright (C) 2024 Frosch
This file is part of Shape of Fantasy.
Shape of Fantasy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Shape of Fantasy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Shape of Fantasy. If not, see <https://www.gnu.org/licenses/>.
|#
;;;
;;; Resources
;;;
;;; https://www.songho.ca/math/quaternion/quaternion.html
(in-package :sof-quat)
(defun mult-quat (q1 q2)
(vector
(- (* (aref q1 0) (aref q2 0))
(+ (* (aref q1 1) (aref q2 1)) (* (aref q1 2) (aref q2 2)) (* (aref q1 3) (aref q2 3))))
(-
(+ (* (aref q1 0) (aref q2 1)) (* (aref q1 1) (aref q2 0)) (* (aref q1 2) (aref q2 3)))
(* (aref q1 3) (aref q2 2)))
(-
(+ (* (aref q1 0) (aref q2 2)) (* (aref q1 2) (aref q2 0)) (* (aref q1 3) (aref q2 1)))
(* (aref q1 1) (aref q2 3)))
(-
(+ (* (aref q1 0) (aref q2 3)) (* (aref q1 3) (aref q2 0)) (* (aref q1 1) (aref q2 2)))
(* (aref q1 2) (aref q2 1)))))
(defun conjugate-quat (q)
(vector (aref q 0) (- (aref q 1)) (- (aref q 2)) (- (aref q 3))))
(defmacro quat->versor (q)
"Normalize a quaternion. A unit quaternion is also called a versor."
`(normalize-vec4 ,q))
(defun axis-angle->versor (axis angle)
"Convert an axis-angle representation of a rotation into a versor."
(let* ((sine-angle (sin angle))
(cosine-angle (cos angle))
(v (scale-vec3 (normalize-vec3 axis) sine-angle)))
(concatenate 'vector (vector cosine-angle) v)))
(defconstant +id-versor+ (vector 1 0 0 0))
(defun slerp-versor (vrs1 vrs2 l)
"Spherical interpolation along the shortest path. If the inputs are versors, the output is a versor."
(let ((cosine-angle (dot-vec4 vrs1 vrs2)) (vrs vrs2))
(when (< cosine-angle 0)
(setf
cosine-angle (- cosine-angle)
(aref vrs 0) (- (aref vrs2 0))
(aref vrs 1) (- (aref vrs2 1))
(aref vrs 2) (- (aref vrs2 2))
(aref vrs 3) (- (aref vrs2 3))))
;; handle when cosine-angle is close to 1
(when (> cosine-angle 0.99)
(return-from slerp-versor
(quat->versor (lerp-vec4 vrs1 vrs l))))
(let* ((sine-angle (sqrt (- 1 (* cosine-angle cosine-angle))))
(angle (acos cosine-angle))
(l1 (/ (sin (* angle (- 1 l))) sine-angle))
(l2 (/ (sin (* angle l)) sine-angle)))
(vector
(+ (* l1 (aref vrs1 0)) (* l2 (aref vrs 0)))
(+ (* l1 (aref vrs1 1)) (* l2 (aref vrs 1)))
(+ (* l1 (aref vrs1 2)) (* l2 (aref vrs 2)))
(+ (* l1 (aref vrs1 3)) (* l2 (aref vrs 3)))))))

87
src/math/vec.lisp Normal file
View File

@ -0,0 +1,87 @@
#|
Copyright (C) 2024 Frosch
This file is part of Shape of Fantasy.
Shape of Fantasy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Shape of Fantasy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Shape of Fantasy. If not, see <https://www.gnu.org/licenses/>.
|#
(in-package :sof-vec)
(defmacro fill-vec (value dim)
`(vector
,@(loop for i from 0 below dim
collect value)))
(defun fill-vec2 (value) (fill-vec value 2))
(defun fill-vec3 (value) (fill-vec value 3))
(defun fill-vec4 (value) (fill-vec value 4))
(defmacro scale-vec (v value dim)
`(vector
,@(loop for i from 0 below dim
collect `(* (aref ,v ,i) ,value))))
(defun scale-vec2 (v value) (scale-vec v value 2))
(defun scale-vec3 (v value) (scale-vec v value 3))
(defun scale-vec4 (v value) (scale-vec v value 4))
(defmacro binary-op-vec (op v1 v2 dim)
`(vector
,@(loop for i from 0 below dim
collect `(,op (aref ,v1 ,i) (aref ,v2 ,i)))))
(defun add-vec2 (v1 v2) (binary-op-vec + v1 v2 2))
(defun add-vec3 (v1 v2) (binary-op-vec + v1 v2 3))
(defun add-vec4 (v1 v2) (binary-op-vec + v1 v2 4))
(defun sub-vec2 (v1 v2) (binary-op-vec - v1 v2 2))
(defun sub-vec3 (v1 v2) (binary-op-vec - v1 v2 3))
(defun sub-vec4 (v1 v2) (binary-op-vec - v1 v2 4))
(defun mult-vec2 (v1 v2) (binary-op-vec * v1 v2 2))
(defun mult-vec3 (v1 v2) (binary-op-vec * v1 v2 3))
(defun mult-vec4 (v1 v2) (binary-op-vec * v1 v2 4))
(defun div-vec2 (v1 v2) (binary-op-vec / v1 v2 2))
(defun div-vec3 (v1 v2) (binary-op-vec / v1 v2 3))
(defun div-vec4 (v1 v2) (binary-op-vec / v1 v2 4))
(defmacro dot-vec (v1 v2 dim)
`(+
,@(loop for i from 0 below dim
collect `(* (aref ,v1 ,i) (aref ,v2 ,i)))))
(defun dot-vec2 (v1 v2) (dot-vec v1 v2 2))
(defun dot-vec3 (v1 v2) (dot-vec v1 v2 3))
(defun dot-vec4 (v1 v2) (dot-vec v1 v2 4))
(defun cross-vec3 (v1 v2)
(vector
(- (* (aref v1 1) (aref v2 2)) (* (aref v1 2) (aref v2 1)))
(- (* (aref v1 2) (aref v2 0)) (* (aref v1 0) (aref v2 2)))
(- (* (aref v1 0) (aref v2 1)) (* (aref v1 1) (aref v2 0)))))
(defmacro normalize-vec (v dim)
`(let ((scale (sqrt (dot-vec ,v ,v ,dim))))
(vector
,@(loop for i from 0 below dim
collect `(/ (aref ,v ,i) scale)))))
(defun normalize-vec2 (v) (normalize-vec v 2))
(defun normalize-vec3 (v) (normalize-vec v 3))
(defun normalize-vec4 (v) (normalize-vec v 4))
(defmacro lerp-vec (v1 v2 L dim)
`(vector
,@(loop for i from 0 below dim
collect `(+ (* ,L (aref ,v1 ,i)) (* (- 1 ,L) (aref ,v2 ,i))))))
(defun lerp-vec2 (v1 v2 L) (lerp-vec v1 v2 L 2))
(defun lerp-vec3 (v1 v2 L) (lerp-vec v1 v2 L 3))
(defun lerp-vec4 (v1 v2 L) (lerp-vec v1 v2 L 4))

90
src/package.lisp Normal file
View File

@ -0,0 +1,90 @@
(defpackage #:sof-vec
(:use #:cl)
(:export
#:fill-vec2
#:fill-vec3
#:fill-vec4
#:scale-vec2
#:scale-vec3
#:scale-vec4
#:add-vec2
#:add-vec3
#:add-vec4
#:sub-vec2
#:sub-vec3
#:sub-vec4
#:mult-vec2
#:mult-vec3
#:mult-vec4
#:div-vec2
#:div-vec3
#:div-vec4
#:dot-vec2
#:dot-vec3
#:dot-vec4
#:cross-vec3
#:normalize-vec2
#:normalize-vec3
#:normalize-vec4
#:lerp-vec2
#:lerp-vec3
#:lerp-vec4))
(defpackage #:sof-quat
(:use
#:cl
#:sof-vec)
(:export
#:mult-quat
#:conjugate-quat
#:quat->versor
#:axis-angle->versor
#:+id-versor+
#:slerp-versor))
(defpackage #:sof-mat
(:use
#:cl
#:sof-vec
#:sof-quat)
(:export
#:fill-mat2
#:fill-mat3
#:fill-mat4
#:diag->mat2
#:diag->mat3
#:diag->mat4
#:+id-mat2+
#:+id-mat3+
#:+id-mat4+
#:transpose-mat2
#:transpose-mat3
#:transpose-mat4
#:mult-mat2
#:mult-mat3
#:mult-mat4
#:mult-mat2-diag
#:mult-mat3-diag
#:mult-mat4-diag
#:mult-mat2-vec
#:mult-mat3-vec
#:mult-mat4-vec
#:versor->mat3
#:look-at
#:perspective-projection
#:orthographic-projection))
(defpackage #:sof-transform
(:use
#:cl
#:sof-vec
#:sof-quat
#:sof-mat))
(defpackage #:sof-game
(:use
#:cl
#:cffi
#:sof-transform)
(:export
#:main))