Figuring it out

Start with package.lisp. Besides the documentation it's the only thing telling us about intended API usage.

Questions currently investigating:

Trace/log an object

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))

So we can (setf (v:repl-level) :trace) to see Mouse wheel scroll events, among other things.

Query the context

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

DESCRIBE the scene

(describe (scene +main+)) will pretty print some information about the scene:

#<PIPELINED-SCENE {1006196C13}>
  [standard-object]

Slots with :INSTANCE allocation:
  NODES                          = NIL
  PASSES                         = #(#<RENDER-PASS {1001C624F3}>)
  TEXTURES                       = #(#<TEXTURE 1276x1408 RGBA ALLOCATED {10013FFD23}>..
  QUEUE                          = #<TRIAL::QUEUE READ-INDEX: 0 WRITE-INDEX: 0 CAPACITY: 1024 {100139C593}>
  LISTENERS                      = {#1=#<DISPLAY-CONTROLLER :CONTROLLER {1001C62523}> #2=(#1# NIL)..
  LISTENER-QUEUE                 = (#<RENDER-PASS {1001C624F3}> #<GRID-FLOOR {1001C62503}>..
  CONTAINER                      = NIL
  %OBJECTS                       = #(#<DISPLAY-CONTROLLER :CONTROLLER {1001C62523}> #<CUBE {1001C62513}>..
  %OBJECT->INDEX                 = {#<DISPLAY-CONTROLLER :CONTROLLER {1001C62523}> 0..
  %COUNT                         = 3
  CAMERA                         = #<CAMERA :CAMERA {100139C5F3}>
  NAME-MAP                       = {:CONTROLLER #<DISPLAY-CONTROLLER :CONTROLLER {1001C62523}>..
  TO-PRELOAD                     = NIL

Shader Passes:
 ┌──────────────────┐
 │             COLOR├╴  0
 │ RENDER-PASS DEPTH├╴  1
 └──────────────────┘

Textures:
   0 #<TEXTURE 1276x1408 RGBA ALLOCATED {10013FFD23}>
   1 #<TEXTURE 1276x1408 DEPTH-STENCIL ALLOCATED {10013FFD33}>

Framebuffers:
   0 #<FRAMEBUFFER 1276x1408 COLOR-ATTACHMENT0 DEPTH-STENCIL-ATTACHMENT ALLOCATED {10013FFD43}>

Entity Tree:
 #<PIPELINED-SCENE {1006196C13}>
├─ #<DISPLAY-CONTROLLER :CONTROLLER {1001C62523}>
├─ #<CUBE {1001C62513}>
└─ #<GRID-FLOOR {1001C62503}>

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 by do-scene-graph. For example, given a camera object we enter'd:

(enter (make-instance 'camera) scene)

Then:

(node :camera T)
;; => #<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+)))

Double click

From input.lisp

(define-event mouse-double-click (mouse-button-event) :pool T)