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
31namespace Ultima {
32namespace Ultima8 {
33
34class Shape;
35
42struct 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
268inline 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
305inline 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
342inline 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: str.h:59
Definition: shape.h:36
Definition: rect.h:538
bool intersects(const ConcreteRect &r) const
Definition: rect.h:291
bool contains(T x, T y) const
Definition: rect.h:246
Definition: lobject.h:332
Definition: sort_item.h:127
Definition: sort_item.h:42