ScummVM API documentation
sort_item.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef ULTIMA8_WORLD_SORTITEM_H
23 #define ULTIMA8_WORLD_SORTITEM_H
24 
25 #include "common/rect.h"
26 #include "common/str.h"
27 #include "ultima/ultima8/misc/box.h"
28 
29 //#define SORTITEM_OCCLUSION_EXPERIMENTAL 1
30 
31 namespace Ultima {
32 namespace Ultima8 {
33 
34 class Shape;
35 
42 struct SortItem {
43  SortItem() : _next(nullptr), _prev(nullptr), _itemNum(0),
44  _shape(nullptr), _order(-1), _depends(), _shapeNum(0),
45  _frame(0), _flags(0), _extFlags(0), _sr(),
46  _x(0), _y(0), _z(0), _xLeft(0),
47  _yFar(0), _zTop(0), _sxLeft(0), _sxRight(0), _sxTop(0),
48  _syTop(0), _sxBot(0), _syBot(0),_fbigsq(false), _flat(false),
49  _occl(false), _solid(false), _draw(false), _roof(false),
50  _noisy(false), _anim(false), _trans(false), _fixed(false),
51  _land(false), _occluded(false), _sprite(false),
52  _invitem(false) { }
53 
54  SortItem *_next;
55  SortItem *_prev;
56 
57  uint16 _itemNum; // Owner item number
58 
59  const Shape *_shape;
60  uint32 _shapeNum;
61  uint32 _frame;
62  uint32 _flags; // Item flags
63  uint32 _extFlags; // Item extended flags
64 
65  Common::Rect32 _sr; // Screenspace rect for shape frame
66  /*
67  Bounding Box layout
68 
69  1
70  / \
71  / \ 1 = Left Far Top LFT --+
72  2 3 2 = Left Near Top LNT -++
73  | \ / | 3 = Right Far Top RFT +-+
74  | \ / | 4 = Right Near Top RNT +++
75  | 4 | 5 = Left Near Bot LNB -+-
76  | | | 6 = Right Far Bot RFB +--
77  5 | 6 7 = Right Near Bot RNB ++-
78  \ | / 8 = Left Far Bot LFB --- (not shown)
79  \ | /
80  7
81 
82  */
83 
84  int32 _x, _xLeft; // Worldspace bounding box x (xright = x)
85  int32 _y, _yFar; // Worldspace bounding box y (ynear = y)
86  int32 _z, _zTop; // Worldspace bounding box z (_zTop = z)
87 
88  int32 _sxLeft; // Screenspace bounding box left extent (LNT x coord)
89  int32 _sxRight; // Screenspace bounding box right extent (RFT x coord)
90 
91  int32 _sxTop; // Screenspace bounding box top x coord (LFT x coord)
92  int32 _syTop; // Screenspace bounding box top extent (LFT y coord)
93 
94  int32 _sxBot; // Screenspace bounding box bottom x coord (RNB x coord) ss origin
95  int32 _syBot; // Screenspace bounding box bottom extent (RNB y coord) ss origin
96 
97 #ifdef SORTITEM_OCCLUSION_EXPERIMENTAL
98  SortItem *_xAdjoin; // Item sharing a right x edge with the left x edge - used for occlusion
99  SortItem *_yAdjoin; // Item sharing a near y edge with the far y edge - used for occlusion
100  uint16 _groupNum; // Identifier for a member of an occlusion group
101 #endif // SORTITEM_OCCLUSION_EXPERIMENTAL
102 
103 
104  bool _fbigsq : 1; // Needs 1 bit 0
105  bool _flat : 1; // Needs 1 bit 1
106  bool _occl : 1; // Needs 1 bit 2
107  bool _solid : 1; // Needs 1 bit 3
108  bool _draw : 1; // Needs 1 bit 4
109  bool _roof : 1; // Needs 1 bit 5
110  bool _noisy : 1; // Needs 1 bit 6
111  bool _anim : 1; // Needs 1 bit 7
112  bool _trans : 1; // Needs 1 bit 8
113  bool _fixed : 1;
114  bool _land : 1;
115  bool _sprite : 1; // Always-on-top sprite, for Crusader (U8 sprites appear in z order)
116  bool _invitem : 1; // Crusader inventory item, should appear above other things
117 
118  bool _occluded : 1; // Set true if occluded
119 
120  int32 _order; // Rendering _order. -1 is not yet drawn
121 
122  // Note that PriorityQueue could be used here, BUT there is no guarantee that it's implementation
123  // will be friendly to insertions
124  // Alternatively i could use Common::List, BUT there is no guarantee that it will keep won't delete
125  // the unused nodes after doing a clear
126  // So the only reasonable solution is to write my own list
127  struct DependsList {
128  struct Node {
129  Node *_next;
130  Node *_prev;
131  SortItem *val;
132  Node() : _next(nullptr), _prev(nullptr), val(nullptr) { }
133  };
134 
135  Node *list;
136  Node *tail;
137  Node *unused;
138 
139  struct iterator {
140  Node *n;
141  SortItem *&operator *() {
142  return n->val;
143  }
144  iterator(Node *node) : n(node) { }
145  iterator &operator++() {
146  n = n->_next;
147  return *this;
148  }
149  bool operator != (const iterator &o) const {
150  return n != o.n;
151  }
152  };
153 
154  iterator begin() const {
155  return iterator(list);
156  }
157  iterator end() const {
158  return iterator(nullptr);
159  }
160 
161  void clear() {
162  if (tail) {
163  tail->_next = unused;
164  unused = list;
165  tail = nullptr;
166  list = nullptr;
167  }
168  }
169 
170  void push_back(SortItem *other) {
171  if (!unused) unused = new Node();
172  Node *nn = unused;
173  unused = unused->_next;
174  nn->val = other;
175 
176  // Put it at the end
177  if (tail) tail->_next = nn;
178  if (!list) list = nn;
179  nn->_next = nullptr;
180  nn->_prev = tail;
181  tail = nn;
182  }
183 
184  void insert_sorted(SortItem *other) {
185  if (!unused) unused = new Node();
186  Node *nn = unused;
187  unused = unused->_next;
188  nn->val = other;
189 
190  for (Node *n = list; n != nullptr; n = n->_next) {
191  // Get the insert point... which is before the first item that has higher z than us
192  if (other->listLessThan(*(n->val))) {
193  nn->_next = n;
194  nn->_prev = n->_prev;
195  n->_prev = nn;
196  if (nn->_prev) nn->_prev->_next = nn;
197  else list = nn;
198  return;
199  }
200  }
201 
202  // No suitable, so put at end
203  if (tail) tail->_next = nn;
204  if (!list) list = nn;
205  nn->_next = nullptr;
206  nn->_prev = tail;
207  tail = nn;
208  }
209 
210  DependsList() : list(nullptr), tail(nullptr), unused(nullptr) { }
211 
212  ~DependsList() {
213  clear();
214  while (unused) {
215  Node *n = unused->_next;
216  delete unused;
217  unused = n;
218  }
219  }
220  };
221 
222  // All this Items dependencies (i.e. all objects behind)
223  DependsList _depends;
224 
225  // Functions
226 
227  // Set worldspace bounds and calculate screenspace at center point
228  void setBoxBounds(const Box &box, int32 sx, int32 sy);
229 
230  inline Box getBoxBounds() const {
231  Box box;
232  box._x = _x;
233  box._y = _y;
234  box._z = _z;
235  box._xd = _x - _xLeft;
236  box._yd = _y - _yFar;
237  box._zd = _zTop - _z;
238  return box;
239  }
240 
241  // Check if the given point is inside the screenpace bounds.
242  inline bool contains(int32 sx, int32 sy) const;
243 
244  // Screenspace check to see if this overlaps si2
245  inline bool overlap(const SortItem &si2) const;
246 
247  // Screenspace check to see if this occludes si2. Assumes this is above of si2
248  inline bool occludes(const SortItem &si2) const;
249 
250  // Screenspace check to see if this is below si2. Assumes this overlaps si2
251  bool below(const SortItem &si2) const;
252 
253  // Comparison for the sorted lists
254  inline bool listLessThan(const SortItem &si2) const {
255  const SortItem &si1 = *this;
256  if (si1._sprite != si2._sprite)
257  return si1._sprite < si2._sprite;
258 
259  if (si1._z != si2._z)
260  return si1._z < si2._z;
261 
262  return si1._flat > si2._flat;
263  }
264 
265  Common::String dumpInfo() const;
266 };
267 
268 inline bool SortItem::contains(int32 sx, int32 sy) const {
269  if (!_sr.contains(sx, sy))
270  return false;
271 
272  const int point_top_diff[2] = { _sxTop - sx, _syTop - sy };
273  const int point_bot_diff[2] = { _sxBot - sx, _syBot - sy };
274 
275  // This function is a bit of a hack. It uses dot products between
276  // points and the lines. Nothing is normalized since that isn't
277  // important
278 
279  // 'normal' of top left line ( 2,-1) of the bounding box
280  const int32 dot_top_left = point_top_diff[0] + point_top_diff[1] * 2;
281 
282  // 'normal' of top right line ( 2, 1) of the bounding box
283  const int32 dot_top_right = -point_top_diff[0] + point_top_diff[1] * 2;
284 
285  // 'normal' of bot left line (-2,-1) of the bounding box
286  const int32 dot_bot_left = point_bot_diff[0] - point_bot_diff[1] * 2;
287 
288  // 'normal' of bot right line (-2, 1) of the bounding box
289  const int32 dot_bot_right = -point_bot_diff[0] - point_bot_diff[1] * 2;
290 
291  const bool right_clear = _sxRight < sx;
292  const bool left_clear = _sxLeft > sx;
293  const bool top_left_clear = dot_top_left > 0;
294  const bool top_right_clear = dot_top_right > 0;
295  const bool bot_left_clear = dot_bot_left > 0;
296  const bool bot_right_clear = dot_bot_right > 0;
297 
298  const bool clear = right_clear || left_clear ||
299  (bot_right_clear || bot_left_clear) ||
300  (top_right_clear || top_left_clear);
301 
302  return !clear;
303 }
304 
305 inline bool SortItem::overlap(const SortItem &si2) const {
306  if (!_sr.intersects(si2._sr))
307  return false;
308 
309  const int point_top_diff[2] = { _sxTop - si2._sxBot, _syTop - si2._syBot };
310  const int point_bot_diff[2] = { _sxBot - si2._sxTop, _syBot - si2._syTop };
311 
312  // This function is a bit of a hack. It uses dot products between
313  // points and the lines. Nothing is normalized since that isn't
314  // important
315 
316  // 'normal' of top left line ( 2,-1) of the bounding box
317  const int32 dot_top_left = point_top_diff[0] + point_top_diff[1] * 2;
318 
319  // 'normal' of top right line ( 2, 1) of the bounding box
320  const int32 dot_top_right = -point_top_diff[0] + point_top_diff[1] * 2;
321 
322  // 'normal' of bot left line (-2,-1) of the bounding box
323  const int32 dot_bot_left = point_bot_diff[0] - point_bot_diff[1] * 2;
324 
325  // 'normal' of bot right line (-2, 1) of the bounding box
326  const int32 dot_bot_right = -point_bot_diff[0] - point_bot_diff[1] * 2;
327 
328  const bool right_clear = _sxRight <= si2._sxLeft;
329  const bool left_clear = _sxLeft >= si2._sxRight;
330  const bool top_left_clear = dot_top_left >= 0;
331  const bool top_right_clear = dot_top_right >= 0;
332  const bool bot_left_clear = dot_bot_left >= 0;
333  const bool bot_right_clear = dot_bot_right >= 0;
334 
335  const bool clear = right_clear || left_clear ||
336  (bot_right_clear || bot_left_clear) ||
337  (top_right_clear || top_left_clear);
338 
339  return !clear;
340 }
341 
342 inline bool SortItem::occludes(const SortItem &si2) const {
343  if (!_sr.contains(si2._sr))
344  return false;
345 
346  const int point_top_diff[2] = { _sxTop - si2._sxTop, _syTop - si2._syTop };
347  const int point_bot_diff[2] = { _sxBot - si2._sxBot, _syBot - si2._syBot };
348 
349  // This function is a bit of a hack. It uses dot products between
350  // points and the lines. Nothing is normalized since that isn't
351  // important
352 
353  // 'normal' of top left line ( 2, -1) of the bounding box
354  const int32 dot_top_left = point_top_diff[0] + point_top_diff[1] * 2;
355 
356  // 'normal' of top right line ( 2, 1) of the bounding box
357  const int32 dot_top_right = -point_top_diff[0] + point_top_diff[1] * 2;
358 
359  // 'normal' of bot left line (-2,-1) of the bounding box
360  const int32 dot_bot_left = point_bot_diff[0] - point_bot_diff[1] * 2;
361 
362  // 'normal' of bot right line (-2, 1) of the bounding box
363  const int32 dot_bot_right = -point_bot_diff[0] - point_bot_diff[1] * 2;
364 
365 
366  const bool right_res = _sxRight >= si2._sxRight;
367  const bool left_res = _sxLeft <= si2._sxLeft;
368  const bool top_left_res = dot_top_left <= 0;
369  const bool top_right_res = dot_top_right <= 0;
370  const bool bot_left_res = dot_bot_left <= 0;
371  const bool bot_right_res = dot_bot_right <= 0;
372 
373  return right_res && left_res && bot_right_res && bot_left_res &&
374  top_right_res && top_left_res;
375 }
376 
377 } // End of namespace Ultima8
378 } // End of namespace Ultima
379 
380 #endif
Definition: rect.h:526
Definition: str.h:59
Definition: box.h:36
Definition: detection.h:27
Definition: sort_item.h:42
Definition: shape.h:36
Definition: sort_item.h:127