OSDN Git Service

FIX: entities under flyweight
[rulp/rulp.git] / layers / screens.lisp
1 ;;;; Ru*** roLeplay Playground virtual tabletop
2 ;;;; Copyright (C) 2022  Zull
3 ;;;;
4 ;;;; This program is free software: you can redistribute it and/or modify
5 ;;;; it under the terms of the GNU General Public License as published by
6 ;;;; the Free Software Foundation, either version 3 of the License, or
7 ;;;; (at your option) any later version.
8 ;;;;
9 ;;;; This program is distributed in the hope that it will be useful,
10 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 ;;;; GNU General Public License for more details.
13 ;;;;
14 ;;;; You should have received a copy of the GNU General Public License
15 ;;;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17 (in-package :rulp.layers)
18 ;; This is the most generic class in the program with model, this class define
19 ;; objects to be displayed on screen, whenever they are entities, planes text
20 ;; or icons.
21
22 (defgeneric render (screen x y width height)
23   (:documentation "display the screen by using the defined coordinates and size"))
24
25 (defgeneric texture (s))
26 (defgeneric surface (s))
27 (defgeneric screen-source (s))
28 (defgeneric screen-destination (s p)
29   (:documentation "returns a sdl2 rectangle where the surface in real space, it indicates
30 where the screen should be displayed"))
31
32 ;; space dependent means that the thing can change position based on what space is
33 ;; on. if it is indipendent (under no space) it will be default to real space (therefore
34 ;; pixel per pixel coordinates and dimensions)
35 ;; when instead the object is under something it will use it's space, thefore it will be
36 ;; set into a grid, which will be provided by the something itself.
37 (defclass screen ()
38   ((coordinate :accessor coordinate
39                :initarg :coordinate
40                :initform '(0 . 0)
41                :documentation "the position of the object in the space, space dependent"
42                :type list)
43    (size :accessor size
44          :initarg :size
45          :initform '(50 . 50)
46          :documentation "the size of the object, by default 50x50, space dependent"
47          :type list)
48    (texture :initform nil
49             :accessor texture)
50    (rotation :accessor rotation)
51    (surface :initform nil
52             :accessor surface)
53    (path :reader image-path
54          :initarg :image
55          :initform (merge-pathnames rulp.parameters:*rulp-share* "test.tga"))
56    (s-rect :initform nil
57            :documentation "this value contains the source rectangle to be used, don't
58 interact directly with this but use screen-source instead")
59    (d-rect :initform nil
60            :documentation "this value contains the destination rectangle to be used, don't
61 interact directly with this but use screen-destination instead")
62    ))
63
64 ;; Real space is the pixel grid of the screen, a position of (6 . 7) means
65 ;; the 6th pixel horizontally and 7th pixel vertically.
66 ;;
67 ;; while this is useful for planes and other screens, this is hurtful for
68 ;; entities which move in a grid. The optional p can be used for a plane, which
69 ;; can shift these 4 functions to be inside the grid.
70 ;;
71 ;; this is what's called entity or plane space. Here when the grid is
72 ;; set to 100 the position (6 . 7) means the 6th square horizontally and
73 ;; 7th square vertically, or 600 pixels horiz. and 700 pixels vert.
74 (defmethod x ((obj screen))
75   "returns the x position in real space (or in a grid of 1 pixel span)"
76   (car (slot-value obj 'coordinate)))
77 (defmethod y ((obj screen))
78   "returns the y position in real space (or in a grid of 1 pixel span)"
79   (cdr (slot-value obj 'coordinate)))
80 (defmethod w ((obj screen))
81   "returns the width in real space (or in a grid of 1 pixel span)"
82   (rulp.render:texture-width (texture obj)))
83 (defmethod h ((obj screen))
84   "returns the height in real space (or in a grid of 1 pixel span)"
85   (rulp.render:texture-height (texture obj))
86   ;; (cdr (slot-value obj 'size))
87   )
88
89 (defmethod (setf x) (value (obj screen))
90   (setf (car (slot-value obj 'coordinate)) value))
91 (defmethod (setf y) (value (obj screen))
92   (setf (cdr (slot-value obj 'coordinate)) value))
93 (defmethod (setf w) (value (obj screen))
94   (setf (car (slot-value obj 'size)) value))
95 (defmethod (setf h) (value (obj screen))
96   (setf (cdr (slot-value obj 'size)) value))
97
98 (defmethod initialize-instance :after ((s screen) &rest args)
99   (declare (ignore args))
100   (setf (slot-value s 'texture) (rulp.render:load-texture (slot-value s 'path)))
101   )
102
103 (defmethod screen-source ((s screen))
104   "return the source rectangle, the portion of texture to display (standard all)"
105   (slot-value s 's-rect))
106
107 ;; FIXME: save and use the make-rect. destroy and create a new one when
108 ;; info don't match
109 ;; (defmethod screen-destination ((s screen) (p t))
110 ;;   "Without a plane of reference screens are printed full size offset of x and y
111 ;; pixels from the upper left angle of the window"
112 ;;   (let ((rect (slot-value s 'd-rect)))
113 ;;     (if (and
114 ;;          rect
115 ;;          (equal (x s) (sdl2:rect-x rect))
116 ;;          (equal (y s) (sdl2:rect-y rect))
117 ;;          (equal (width s) (sdl2:rect-width rect))
118 ;;          (equal (height s) (sdl2:rect-height rect)))
119 ;;         (rect)
120 ;;         (progn (sdl2:free-rect (slot-value s 'd-rect))
121 ;;                (sdl2:make-rect (x s) (y s) (width s) (height s)))
122 ;;         ))
123 ;;   )
124
125 (defmethod render ((screen screen) x y width height)
126   (rulp.render:render-texture nil (list x y
127                                         (if (<= width 0)
128                                             (rulp.render:texture-width (texture screen))
129                                             width)
130                                         (if (<= height 0)
131                                             (rulp.render:texture-height (texture screen))
132                                             height)
133                                         )
134                               (texture screen))
135   )
136
137 ;; FIXME: find a way to store rectangles to later use
138 (defmethod screen-destination ((s screen) (p t))
139   "without a plane of reference, a screen is just printed full size from the
140 top left of the window"
141   (sdl2:make-rect (x s) (y s) (w s) (h s))
142   )
143
144 (defun screen-purge (screen)
145   "purge screens video data. This data is automatically generated during rendering"
146   (sdl2:destroy-texture (texture s))
147   (when (surface s)
148     (sdl2:free-surface (surface s))))
149
150 ;; Flyweight zone, screens should be accessed only from the function get screen
151 (defparameter *screen-list* nil)
152
153 (defun path-equal (value screen)
154   (equal value (image-path screen)))
155
156 (defun get-screen (path)
157   "A flyweight for screens. Given a path this function return an existing
158 texture or, if no other exists, create a new one and return it."
159   (if (member path *screen-list* :test #'path-equal)
160       (car (member path *screen-list* :test #'path-equal))
161       (car (push (make-instance 'screen :image path)
162                  *screen-list*))))
163
164 (defun empty-screen-list ()
165   (loop :for screen in *screen-list*
166         :do (rulp.render:destroy-texture (slot-value screen 'texture)))
167   (setf *screen-list* nil))