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