wh-engine/pixelart-scaling.lisp

85 lines
3.2 KiB
Common Lisp

;;; Implementation of smooth pixelart scaling.
;;; This is basically the equivalent of CSS `image-rendering: pixelated`
;; native render resolution
(defvar *render-width* 384)
(defvar *render-height* 256)
;; window resolution
(defvar *window-width* 1200)
(defvar *window-height* 800)
(defvar *render-tex* (gl:gen-texture))
(defvar *render-fb* (gl:gen-framebuffer))
(defvar *upscale-tex* (gl:gen-texture))
(defvar *upscale-fb* (gl:gen-framebuffer))
(defvar *upscale-width*)
(defvar *upscale-height*)
(defmacro with-bind-texture ((target texture) &body body)
`(progn
(gl:bind-texture ,target ,texture)
,@body
(gl:bind-texture ,target 0)))
(defmacro with-bind-framebuffer ((target framebuffer) &body body)
`(progn
(gl:bind-framebuffer ,target ,framebuffer)
,@body
(gl:bind-framebuffer ,target 0)))
(defun init-textures ()
"Initialize textures. Call this whenever the window or render resolution changes."
;; generate the render texture
(with-bind-texture (:texture-2d *render-tex*)
(gl:tex-image-2d
:texture-2d 0 :rgba
*render-width* *render-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 :nearest)
(gl:tex-parameter :texture-2d :texture-mag-filter :nearest))
(with-bind-framebuffer (:framebuffer *render-fb*)
(gl:framebuffer-texture-2d :framebuffer :color-attachment0 :texture-2d *render-tex* 0))
;; generate the upscale texture, which is scaled to an integer multiple of the render size
(setf
*upscale-width* (* (ceiling *window-width* *render-width*) *render-width*)
*upscale-height* (* (ceiling *window-height* *render-height*) *render-height*))
(with-bind-texture (:texture-2d *upscale-tex*)
(gl:tex-image-2d
:texture-2d 0 :rgba
*upscale-width* *upscale-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))
(with-bind-framebuffer (:framebuffer *upscale-fb*)
(gl:framebuffer-texture-2d :framebuffer :color-attachment0 :texture-2d *upscale-tex* 0)))
(defun before-render ()
(gl:bind-framebuffer :framebuffer *render-fb*)
(gl:viewport 0 0 *render-width* *render-height*))
(defun after-render ()
;; upscale with nearest-neighbour
(gl:bind-framebuffer :framebuffer *upscale-fb*)
(gl:viewport 0 0 *upscale-width* *upscale-height*)
(gl:disable :depth-test)
(gl:enable :texture-2d)
(gl:bind-texture :texture-2d *render-tex*)
(draw-full-screen-quad)
;; downscale with linear, render to window
(gl:bind-framebuffer :framebuffer 0)
(gl:viewport 0 0 *window-width* *window-height*)
(gl:bind-texture :texture-2d *upscale-tex*)
(draw-full-screen-quad)
(gl:bind-texture :texture-2d 0))
(defun draw-full-screen-quad ()
"Draw a quad from (-1,-1) to (1,1). Essentially draw a texture stretched across the entire viewport."
(not-implemented))