Figuring it out

Introspection

v:trace is used a lot across Trial

(defmethod glfw:mouse-scrolled ((context context) x y)
  (v:trace :trial.input "Mouse wheel: ~a ~a" x y)
  (%handle context 'mouse-scroll :pos (mouse-pos context) :delta y))

trial/context.lisp is useful:

(let ((c (context +main+)))
  (values
   (valid-p c)
   (vsync c)
   (title c)
   (width c)
   (height c)))
;; => T
;;    :OFF
;;    "Trial"
;;    1651
;;    1408

Note: The backend we use is glfw, so also take a look at trial/backends/glfw/context.lisp

Cameras

trial/camera.lisp

Every camera is a:

Every camera instance has a:

Cameras of interest:

Methods usually specialized for 3d cameras:

No matter the 3d camera setup, what am I always updating?

Update process:

Keymaps

In keymap.lisp we only specify actions. Meaning, ACTION-SETs are not relevant to the actual binding of EVENT<->KEY. The action sets are just a convenient way of grouping together actions (events) so we can decide what actions are enabled at any given time. Think of it like modality: action sets are 'layers' or 'modes'.

Keymap not being updated

If your keymap isn't being updated no matter how many times you (load-keymap :reset t), perhaps combined with a weird error when you try to press the button/key in the game window:

unknown type specifier: KEYS
   [Condition of type SIMPLE-ERROR]

Restarts:
 0: [ABORT] Don't handle #<KEY-PRESS :V> in #<CONTROLLER :CONTROLLER {10010D7CA3}>.

You may be a victim of ✨being dumb✨ and missing a paren:

(trigger spectate
  (keys :one-of (:v)))

If you use (keys ...), use two paren forms: ((:v))

Scene Graph

Shinmera nicely summed things up for me:

node      → anything that can be part of the scene graph
container → can hold other nodes
entity    → a node that has a global name

Add something to the scene graph

Given a thing

(define-shader-entity cube (vertex-entity transformed-entity listener)
  ((vertex-array :initform (// 'trial 'unit-cube))))

which we can enter, then we can also :name it, thereby becoming an entity

(enter (make-instance 'cube :name :bob) scene)

Regardless of whether it's an entity or not, its a node, and nodes go into the scene graph. We can query the node graph:

(node :bob T)
;; => #<CUBE :BOB {1000F5DD23}>
;;    T

(do-scene-graph (n (scene +main+))
  (format t "~%~A" n))
;; => #<PIPELINED-SCENE {1000D26C03}>
;;    #<CUBE {10017C7173}>
;;    #<CUBE :BOB {1000F5DD23}>
;;    #<CONTROLLER :CONTROLLER {10017C7153}>
;;
;;    NIL

Oddly enough, certain nodes can be found despite not being returned from do-scene-graph. Something to figure out or ask about later

(node :camera (scene +main+))
;; => #<FPS-CAMERA :CAMERA {10014A1993}>
;;    T

ENTER/LEAVE

enter and leave are methods responsible for registering and deregistering objects we add to a scene. For example, in Trial the base camera class is defined as such:

(defclass scene (bag event-loop)
  ((camera :initarg :camera :initform NIL :accessor camera)
   (name-map :initform (make-hash-table :test 'equal) :accessor name-map)))

And has an enter and leave method:

(defmethod enter ((camera camera) (scene scene))
  (register camera scene)
  (unless (camera scene)
    (setf (camera scene) camera)))

(defmethod leave ((camera camera) (scene scene))
  (deregister camera scene)
  (when (eq camera (camera scene))
    (setf (camera scene) NIL)))

So, a question: when you (enter (make-instance '3d-camera) scene), how many enter methods are happening for this one call?

Answer: 4

(compute-applicable-methods #'enter (list (make-instance '3d-camera)
                                          (scene +main+)))