wh-engine/wh-engine/render/view.lisp

120 lines
5.0 KiB
Common Lisp

;;;; wh-engine/render/view.lisp
(in-package wh-engine/render)
(defclass view (component)
((render-pass :documentation "The render pass this view should be drawn in."
:reader render-pass
:type fixnum
:initarg :render-pass
:initform 0)
(render-mask :documentation "Only include actors with at least one of these tags."
:accessor render-mask
:type (proper-list symbol)
:initarg :render-mask
:initform '(:default))
(cull-p :documentation "Whether or not to skip rendering out-of-frame objects."
:accessor cull-p
:type boolean
:initarg :cull-p
:initform t)
(render-target :documentation "The render target this view renders to."
:reader render-target
:type (or render-target null)
:initform nil))
(:documentation "Defines a view into the scene, and rendering settings for objects drawn by the view."))
(defmethod (setf render-pass) (new-val (this view))
"The render pass this view should be drawn in."
(setf (o! this :slot render-pass) new-val)
(sort-world-views))
(defmethod resume :after ((this view))
(unless (o! this render-target)
(setf (o! this :slot render-target) (new! render-target :width *view-width* :height *view-height*))))
(defmethod activate :after ((this view) &key)
; Register
(when (o! this actor tree-active-p)
(pushnew (make-weak-pointer this) *world-views*)
(sort-world-views)))
(defmethod destroy :before ((this view))
(unless (o! this destroyed-p)
;; Unregister
(setf *world-views* (delete this *world-views* :key #'weak-pointer-value))
;; Destroy buffers
(when (o! this framebuffer)
(gl:delete-framebuffers (list (o! this framebuffer))))
(when (o! this render-texture)
(gl:delete-texture (o! this render-texture)))
(when (o! this renderbuffer)
(gl:delete-renderbuffers (list (o! this renderbuffer))))
))
(defmethod view-matrix ((this view))
"The world-to-view-space transformation matrix for this object."
;; view-space = local-space, scaled by ppu, then offset so [-width/2..width/2] -> [0..width]
;; (Y+ is still up in view-space)
(m* (mat *view-ppu* 0 (/ *view-width* 2)
0 *view-ppu* (/ *view-height* 2)
0 0 1)
(o! this actor local-matrix)))
(defmethod world-matrix ((this view))
"The view-to-world-space transformation matrix for this object."
(minv (o! this view-matrix)))
(defmethod view-point ((this view) point)
"Transform point from world space to view space."
(declare (type vec2 point))
(vxy-trunc (m* (o! this view-matrix) (vxy1 point))))
(defmethod render-view ((this view) drawables)
"Render everything in this view, given all drawables in the world."
(let ((view-matrix (o! this view-matrix)))
;; Apply view matrix
(gl:matrix-mode :modelview)
(gl:load-transpose-matrix (opengl-matrix view-matrix))
(loop for drawable-ptr in drawables
for drawable = (deref-pointer drawable-ptr)
when (and drawable (ensure-live drawable))
when (and (o! drawable active-p) (o! drawable actor tree-active-p)
(some (lambda (x) (o! drawable actor (has-tag x))) (o! this render-mask)))
do (o! this (render-drawable drawable view-matrix)))
))
(defun transform-box (box drawable-matrix view-matrix)
(with-vec2 (ax ay) (car box)
(with-vec2 (bx by) (cdr box)
(let ((box-a (vxy-trunc (n*m view-matrix
(m* drawable-matrix (vec3 ax ay 1)))))
(box-b (vxy-trunc (n*m view-matrix
(m* drawable-matrix (vec3 bx by 1)))))
(box-ab (vxy-trunc (n*m view-matrix
(m* drawable-matrix (vec3 ax by 1)))))
(box-ba (vxy-trunc (n*m view-matrix
(m* drawable-matrix (vec3 bx ay 1))))))
(values (vmin box-a box-b box-ab box-ba)
(vmax box-a box-b box-ab box-ba))))))
(defun in-view-p (drawable drawable-matrix view-matrix view-box)
"Determine if drawable is in the view defined by view-matrix and view-box."
(multiple-value-bind (box-min box-max)
(transform-box (o! drawable culling-box) drawable-matrix view-matrix)
;; If it's in view at all, either its top-right corner is >= bottom-left of view,
;; or its bottom-left is <= top-right of view
(or (v>= box-max (car view-box))
(v<= box-min (cdr view-box)))))
(defmethod render-drawable ((this view) drawable view-matrix)
"Render drawable with the precomputed view-matrix."
(let ((drawable-matrix (o! drawable actor world-matrix)))
(when (or (not (o! this cull-p)) (in-view-p drawable drawable-matrix view-matrix
(cons (vec2 0 0) (vec2 *view-width* *view-height*))))
(gl:push-matrix)
(gl:translate 0 0 (o! drawable actor z-layer))
(gl:mult-transpose-matrix (opengl-matrix drawable-matrix))
(o! drawable (draw this))
(gl:pop-matrix))))