luv

Workshop wiki

simulation.lisp

luvcraft/simulation.lisp

system luvcraft/core · 85 definitions · on GitHub

The first-person view and the scalar reference player simulation.

Camera mathematics stays small and inspectable: five vec4 values form the prefix of the frame uniform block. The player controller is intentionally a small scalar reference simulation whose body is distinct from the view camera and whose AABB queries the voxel lattice directly. This is the behavior later dense body/contact domains and SIMD kernels must preserve, not their final storage layout.

in-package#:luvcraft
luv.arithmetic:define-quantity-constant+luvcraft-camera-near-distance+0.1:typesingle-float:quantity
:quantity:view-distance:unit:cell
luv.arithmetic:define-quantity-constant+luvcraft-camera-far-distance+180.0:typesingle-float:quantity
:quantity:view-distance:unit:cell
luv.arithmetic:define-quantity-constant+luvcraft-camera-vertical-field-of-view+1.2217305:typesingle-float:quantity
:quantity:camera-field-of-view:unit:radian
:documentation

The block-world camera's 70-degree vertical field of view.

luv.arithmetic:define-quantity-constant+luvcraft-camera-focused-vertical-field-of-view+0.87266463:typesingle-float:quantity
:quantity:camera-field-of-view:unit:radian
:documentation

The 50-degree vertical field of view used during modal focus.

defstruct
camera-pose
:constructormake-camera-pose

One complete, freely movable view pose used by a modal focus transition.

positionyawpitchfield-of-view
defclassfly-camera
position:initarg:position:initform
make-vec38.011.0-6.0
:typevec3:quantity
:quantity:world-position:unit:cell:tensor-order1
:accessorcamera-position
yaw:initarg:yaw:initform0.0:typereal:quantity
:quantity:camera-yaw:unit:radian
:accessorcamera-yaw
pitch:initarg:pitch:initform-0.28:typereal:quantity
:quantity:camera-pitch:unit:radian
:accessorcamera-pitch
field-of-view:initarg:field-of-view:initform+luvcraft-camera-vertical-field-of-view+:typereal:quantity
:quantity:camera-field-of-view:unit:radian
:accessorcamera-field-of-view
sensitivity:initarg:sensitivity:initform0.0025:typereal:quantity
:quantity:look-sensitivity:unit:radian
:accessorcamera-sensitivity
defuncamera-x
camera
vec3-x
camera-positioncamera
defuncamera-y
camera
vec3-y
camera-positioncamera
defuncamera-z
camera
vec3-z
camera-positioncamera
defun
valuecamera
setf
vec3-x
camera-positioncamera
value
defun
valuecamera
setf
vec3-y
camera-positioncamera
value
defun
valuecamera
setf
vec3-z
camera-positioncamera
value
defgenericcamera-basis
camera
defmethodcamera-basis
let*
yaw
pitch
forward
make-vec3
*
sinyaw
cospitch
sinpitch
*
cosyaw
cospitch
right
up
make-vec3
-
*
sinpitch
sinyaw
cospitch
-
*
sinpitch
cosyaw
valuesrightupforward
defmethodcamera-uniform-data

The five camera lanes of the frame uniform: position, basis, projection.

The environment lanes which complete the block are packed by frame-uniform-data from the session's sky clock and profile.

multiple-value-bind
rightupforward
camera-lanes-uniform-data
camera-positioncamera
rightupforwardwidthheight
defuncamera-lanes-uniform-data

Pack the five camera lanes for an eye at position with the unit basis right up forward, in whatever coordinate space those are given in.

The ordinary camera packs its own pose through this; a display drawn in a space of its own (a phone screen in the hand) packs the same camera re-expressed in that space.

let*
focal
aspect
/
coercewidth'single-float
height
projection
make-vec3
/focalaspect
focal
/far
-farnear
flet
uniform-lane
vectorfourth
list
coerce
vec3-xvector
'single-float
coerce
vec3-yvector
'single-float
coerce
vec3-zvector
'single-float
coercefourth'single-float
let*
data
make-array20:element-type'single-float:initial-contents
append
uniform-laneposition0.0
uniform-laneright0.0
uniform-laneup0.0
uniform-laneforward0.0
uniform-laneprojection
/
-
*farnear
-farnear
unless
error"Camera uniform data ~S does not satisfy ~S."
type-ofdata
data
defuncamera-pose-from-camera
camera
make-camera-pose
copy-camera-position
camera-positioncamera
defunshortest-angle-difference
targetcurrent
-
mod
+
-targetcurrent
pi
*2pi
pi
defunset-camera-pose
camerapose
let
target
camera-pose-positionpose
position
camera-positioncamera
setf
vec3-xposition
vec3-xtarget
vec3-yposition
vec3-ytarget
vec3-zposition
vec3-ztarget
camera-pose-yawpose
camera-pose-pitchpose
camera-pose-field-of-viewpose
camera
defunadvance-camera-focus
cameratargetseconds

Ease camera's full view pose toward target and return the remaining error.

let*
position
camera-positioncamera
target-position
camera-pose-positiontarget
flet
approach
currenttarget
+current
*
-targetcurrent
alpha
setf
vec3-xposition
approach
vec3-xposition
vec3-xtarget-position
vec3-yposition
approach
vec3-yposition
vec3-ytarget-position
vec3-zposition
approach
vec3-zposition
vec3-ztarget-position
+
*
shortest-angle-difference
camera-pose-yawtarget
alpha
approach
camera-pose-pitchtarget
approach
camera-pose-field-of-viewtarget
max
vec3-length
make-vec3
-
vec3-xtarget-position
vec3-xposition
-
vec3-ytarget-position
vec3-yposition
-
vec3-ztarget-position
vec3-zposition
abs
shortest-angle-difference
camera-pose-yawtarget
abs
-
camera-pose-pitchtarget
abs
-
camera-pose-field-of-viewtarget

The first player controller is intentionally a small scalar reference simulation. Its body is distinct from the view camera, and its AABB queries the voxel lattice directly. This is the behavior later dense body/contact domains and SIMD kernels must preserve, not their final storage layout.

luv.arithmetic:define-quantity-constant+player-physics-step+
/1d0120d0
:typedouble-float:quantity
:quantity:frame-duration:unit:second
luv.arithmetic:define-quantity-constant+player-collision-epsilon+1d-7:typedouble-float:quantity
:quantity:world-distance:unit:cell
luv.arithmetic:define-quantity-constant+player-step-height+1d0:typedouble-float:quantity
:quantity:world-distance:unit:cell
luv.arithmetic:define-quantity-constant+player-terminal-fall-speed+-50d0:typedouble-float:quantity
:quantity:world-velocity:unit
:cell1
:second-1
luv.arithmetic.lisp:define-lisp-arithmetic-function%predict-world-position
position:quantity:world-position:unit:cell:tensor-order1
velocity:quantity:world-velocity:unit
:cell1
:second-1
:tensor-order1
elapsed:quantity:frame-duration:unit:second
+position
luv.arithmetic.language:interpret
*velocityelapsed
:quantity:world-position:unit:cell:tensor-order1:character:difference
defclassblock-world-player
position:initarg:position:initform
make-vec30d00d00d0
:typevec3:quantity
:quantity:world-position:unit:cell:tensor-order1
:accessorplayer-position
velocity:initarg:velocity:initform
make-vec30d00d00d0
:typevec3:quantity
:quantity:world-velocity:unit
:cell1
:second-1
:tensor-order1
:accessorplayer-velocity
half-width:initarg:half-width:initform0.30d0:typedouble-float:quantity
:quantity:player-half-width:unit:cell
:readerplayer-half-width
height:initarg:height:initform1.80d0:typedouble-float:quantity
:quantity:player-height:unit:cell
:readerplayer-height
eye-height:initarg:eye-height:initform1.62d0:typedouble-float:quantity
:quantity:player-eye-height:unit:cell
:accessorplayer-eye-height
walk-speed:initarg:walk-speed:initform5.0d0:typedouble-float:quantity
:quantity:player-walk-speed:unit
:cell1
:second-1
:accessorplayer-walk-speed
ground-acceleration:initarg:ground-acceleration:initform45d0:typedouble-float:quantity
:quantity:player-acceleration:unit
:cell1
:second-2
:readerplayer-ground-acceleration
air-acceleration:initarg:air-acceleration:initform14d0:typedouble-float:quantity
:quantity:player-acceleration:unit
:cell1
:second-2
:readerplayer-air-acceleration
gravity:initarg:gravity:initform24d0:typedouble-float:quantity
:quantity:gravity-magnitude:unit
:cell1
:second-2
:accessorplayer-gravity
jump-speed:initarg:jump-speed:initform8.0d0:typedouble-float:quantity
:quantity:player-jump-speed:unit
:cell1
:second-1
:accessorplayer-jump-speed
grounded-p:initarg:grounded-p:initformnil:accessorplayer-grounded-p
movement-action:initformnil:accessorplayer-movement-action:documentation"The current destinational movement action, or NIL."
defparameter*player-frame-duration-declaration*
luv.arithmetic:make-represented-value-declaration:representation-type'double-float:quantity-specification
luv.arithmetic:make-declared-quantity-specification'
:quantity:frame-duration:unit:second
:source-form'
seconds:typedouble-float:quantity
:frame-duration:unit:second
defparameter*predict-player-position-realization*
luv.arithmetic.lisp:make-lisp-arithmetic-realization'%predict-world-position:parameter-representation-types'
vec3vec3double-float
:result-representation-type'vec3
defunpredict-player-position
playerseconds

Predict unobstructed motion through the checked storage boundary. #GZ53LD

check-typeplayerblock-world-player
check-typesecondsdouble-float
funcall*predict-player-position-function*
player-positionplayer
player-velocityplayer
seconds
defunplayer-x
player
vec3-x
player-positionplayer
defunplayer-y
player
vec3-y
player-positionplayer
defunplayer-z
player
vec3-z
player-positionplayer
defunplayer-velocity-x
player
vec3-x
player-velocityplayer
defunplayer-velocity-y
player
vec3-y
player-velocityplayer
defunplayer-velocity-z
player
vec3-z
player-velocityplayer
defun
valueplayer
setf
vec3-x
player-positionplayer
value
defun
valueplayer
setf
vec3-y
player-positionplayer
value
defun
valueplayer
setf
vec3-z
player-positionplayer
value
defun
valueplayer
setf
vec3-x
player-velocityplayer
value
defun
valueplayer
setf
vec3-y
player-velocityplayer
value
defun
valueplayer
setf
vec3-z
player-velocityplayer
value
defunmake-player-for-camera
camera
make-instance'block-world-player:position
make-vec3
coerce
camera-xcamera
'double-float
-
coerce
camera-ycamera
'double-float
1.62d0
coerce
camera-zcamera
'double-float
defunsync-camera-to-player
cameraplayer
let
position
camera-positioncamera
setf
vec3-xposition
player-xplayer
vec3-yposition
vec3-zposition
player-zplayer
camera
defunworld-terrain-solid-p
worldxyz

Treat absent horizontal terrain and the lower world boundary as solid.

multiple-value-bind
blockstatus
if
eqstatus:resident
let
height
chunk-shape-height
voxel-space-chunk-shape
block-world-spaceworld

A body is the small scalar contact abstraction which every walking thing in the world shares: an upright axis-aligned box, standing on its own position, swept one axis at a time against solid voxel cells. The player controller remains its reference implementation, and a critter is the same sweep with different dimensions and a different mind above it, so the contact code below is written against this protocol rather than against either participant.

defgenericbody-position
body
:documentation

body's world position at the centre of its base.

defgenericbody-velocity
body
:documentation

body's world velocity.

defgenericbody-half-width
body
:documentation

Half the width of body's square horizontal footprint.

defgenericbody-height
body
:documentation

How far body's box rises above its position.

defgenericbody-grounded-p
body
:documentation

Whether body rested on solid terrain after its last step.

defgeneric
grounded-pbody
defgenericbody-walk-speed
body
:documentation

body's intended level-ground speed in cells per second.

defgenericbody-ground-acceleration
body
:documentation

How quickly body changes horizontal velocity on the ground.

defgenericbody-air-acceleration
body
:documentation

How quickly body changes horizontal velocity in the air.

defgenericbody-gravity
body
:documentation

body's downward acceleration magnitude.

defgenericbody-jump-speed
body
:documentation

body's upward speed when climbing a one-cell step.

defgenericbody-movement-action
body
:documentation

body's current destinational movement action, or NIL.

defgeneric
actionbody
defmethodbody-position
player-positionbody
defmethodbody-velocity
player-velocitybody
defmethodbody-grounded-p
player-grounded-pbody
defmethod
grounded-p
setf
player-grounded-pbody
grounded-p
defmethodbody-ground-acceleration
player-ground-accelerationbody
defmethodbody-air-acceleration
player-air-accelerationbody
defmethodbody-gravity
player-gravitybody
defmethodbody-movement-action
player-movement-actionbody
defmethod
setf
player-movement-actionbody
action
defunbody-x
body
vec3-x
defunbody-y
body
vec3-y
defunbody-z
body
vec3-z
defunmap-body-overlapping-blocks
functionbodyworld
let
half-width
multiple-value-bind
minimum-xmaximum-x
body-overlap-indices
-
body-xbody
half-width
+
body-xbody
half-width
multiple-value-bind
minimum-ymaximum-y
multiple-value-bind
minimum-zmaximum-z
body-overlap-indices
-
body-zbody
half-width
+
body-zbody
half-width
loopforxfromminimum-xtomaximum-xdo
loopforyfromminimum-ytomaximum-ydo
loopforzfromminimum-ztomaximum-zwhendo
funcallfunctionxyz
defunmove-body-axis
bodyworldaxisdistance

Move body along axis and clamp its AABB against solid voxel cells.

when
return-frommove-body-axisnil
defunmove-toward
valuetargetmaximum-change
cond
<valuetarget
mintarget
+valuemaximum-change
>valuetarget
maxtarget
-valuemaximum-change
tvalue
defunbody-position-clear-p
bodyworldxyz

Return true when body's AABB would be clear standing at X, Y, Z.

let
half-width
multiple-value-bind
minimum-xmaximum-x
body-overlap-indices
-xhalf-width
+xhalf-width
multiple-value-bind
minimum-ymaximum-y
multiple-value-bind
minimum-zmaximum-z
body-overlap-indices
-zhalf-width
+zhalf-width
loopforblock-xfromminimum-xtomaximum-xnever
loopforblock-yfromminimum-ytomaximum-ythereis
loopforblock-zfromminimum-ztomaximum-zthereis
world-terrain-solid-pworldblock-xblock-yblock-z
defunstep-walking-body
bodyworldtarget-xtarget-zseconds&keyjump-p

Advance body by one collision-checked walking step toward horizontal velocity.

target-x and target-z are velocities, not coordinates. This is the common physical motor beneath held player input and discrete Move To actions: the caller decides where to go; the body accelerates, collides, steps, and falls.

let*
maximum-change
*accelerationseconds
grounded-p
velocity
setf
vec3-xvelocity
move-toward
vec3-xvelocity
target-xmaximum-change
vec3-zvelocity
move-toward
vec3-zvelocity
target-zmaximum-change
when
andjump-pgrounded-p
setf
vec3-yvelocity
nil
let*
distance-x
*
vec3-xvelocity
seconds
attempted-x
+
body-xbody
distance-x
collided-x-p
move-body-axisbodyworld:xdistance-x
distance-z
*
vec3-zvelocity
seconds
attempted-z
+
body-zbody
distance-z
collided-z-p
move-body-axisbodyworld:zdistance-z
clear-above-x-p
andcollided-x-p
body-position-clear-pbodyworldattempted-xstep-y
body-zbody
clear-above-z-p
andcollided-z-p
body-position-clear-pbodyworld
body-xbody
step-yattempted-z
when
andgrounded-p
notjump-p
or
not
zeroptarget-x
not
zeroptarget-z
orclear-above-x-pclear-above-z-p
setf
vec3-yvelocity
nil
decf
vec3-yvelocity
*seconds
setf
vec3-yvelocity
nil
move-body-axisbodyworld:y
*
vec3-yvelocity
seconds
body
zdefun
step-block-world-player:zone:simulation/player-step
playerworldcameraintentseconds&keyjump-p
sync-camera-pt

Advance the scalar player controller by one small physics step.

INTENT is what the player is trying to do rather than what they pressed: the controller asks for directions and a hurry, and never learns that there is a keyboard.

let*
yaw
forward-amount
movement-intent-axisintent:forward:backward
right-amount
movement-intent-axisintent:right:left
length
sqrt
+
*forward-amountforward-amount
*right-amountright-amount
forward-amount
if
plusplength
/forward-amountlength
0d0
right-amount
if
plusplength
/right-amountlength
0d0
speed
*
ifsprinting-p1.65d01d0
target-x
*speed
+
*
sinyaw
forward-amount
*
cosyaw
right-amount
target-z
*speed
+
*
cosyaw
forward-amount
*
-
sinyaw
right-amount
step-walking-bodyplayerworldtarget-xtarget-zseconds:jump-pjump-p
whensync-camera-p
player