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

149 lines
6.7 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)
(framebuffer :documentation "The GL framebuffer this view renders to."
:reader framebuffer
:type (or fixnum null)
:initform nil)
(renderbuffer :documentation "The GL renderbuffer this view renders depth & stencil data to."
:reader renderbuffer
:type (or fixnum null)
:initform nil)
(render-texture :documentation "The GL render texture this view renders color data to."
:reader render-texture
:type (or fixnum 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))
;; create render texture & framebuffer
(unless (and (o! this render-texture) (gl:texture-resident-p (o! this render-texture))
(o! this renderbuffer) (gl:is-renderbuffer (o! this renderbuffer))
(o! this framebuffer) (gl:is-framebuffer (o! this framebuffer)))
;; ensure the old ones are deleted if they exist
(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))))
;; create render texture
(setf (o! this :slot render-texture) (gl:gen-texture))
(gl:bind-texture :texture-2d (o! this render-texture))
(gl:tex-image-2d :texture-2d 0 :rgba
*view-width* *view-height*
0 :rgba :unsigned-byte (cffi:null-pointer))
(gl:tex-parameter :texture-2d :texture-wrap-s :clamp-to-edge)
(gl:tex-parameter :texture-2d :texture-wrap-t :clamp-to-edge)
(gl:tex-parameter :texture-2d :texture-min-filter :linear)
(gl:tex-parameter :texture-2d :texture-mag-filter :linear)
(gl:bind-texture :texture-2d 0)
;; create renderbuffer
(setf (o! this :slot renderbuffer) (gl:gen-renderbuffer))
(gl:bind-renderbuffer :renderbuffer (o! this renderbuffer))
(gl:renderbuffer-storage :renderbuffer :depth24-stencil8 *view-width* *view-height*)
(gl:bind-renderbuffer :renderbuffer 0)
;; create framebuffer
(setf (o! this :slot framebuffer) (gl:gen-framebuffer))
(gl:bind-framebuffer :framebuffer (o! this framebuffer))
(gl:framebuffer-texture-2d :framebuffer :color-attachment0 :texture-2d (o! this render-texture) 0)
(gl:framebuffer-renderbuffer :framebuffer :depth-stencil-attachment :renderbuffer (o! this renderbuffer))
(gl:bind-framebuffer :framebuffer 0)
))
(defmethod activate :after ((this view) &key)
; Register
(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 in-view-p (drawable drawable-matrix view-matrix view-box)
"Determine if drawable is in the view defined by view-matrix and view-box."
(let ((drawable-culling-box (o! drawable culling-box))
box-a box-b)
(setf box-a (vxy-trunc (m* view-matrix
(m* drawable-matrix (vxy1 (car drawable-culling-box))))))
(setf box-b (vxy-trunc (m* view-matrix
(m* drawable-matrix (vxy1 (cdr drawable-culling-box))))))
;; 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>= (vmax box-a box-b) (car view-box))
(v<= (vmin box-a box-b) (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))))