aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/plane32.h
blob: 406c3f8a30f911e87530e574d4923b6e60a096f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#ifndef SCI_GRAPHICS_PLANE32_H
#define SCI_GRAPHICS_PLANE32_H

#include "common/array.h"
#include "common/rect.h"
#include "sci/engine/vm_types.h"
#include "sci/graphics/helpers.h"
#include "sci/graphics/lists32.h"
#include "sci/graphics/screen_item32.h"

namespace Sci {
enum PlaneType {
	kPlaneTypeColored            = 0,
	kPlaneTypePicture            = 1,
	kPlaneTypeTransparent        = 2,
	kPlaneTypeOpaque             = 3,
	kPlaneTypeTransparentPicture = 4
};

enum PlanePictureCodes {
	// Any value at or below 65531 means the plane is a kPlaneTypePicture
	kPlanePic                   = 65531,
	kPlanePicTransparentPicture = 65532,
	kPlanePicOpaque             = 65533,
	kPlanePicTransparent        = 65534,
	kPlanePicColored            = 65535
};

#pragma mark -
#pragma mark RectList

typedef StablePointerArray<Common::Rect, 200> RectListBase;
class RectList : public RectListBase {
public:
	void add(const Common::Rect &rect) {
		RectListBase::add(new Common::Rect(rect));
	}
};

#pragma mark -
#pragma mark DrawList

struct DrawItem {
	/**
	 * The screen item to draw.
	 */
	ScreenItem *screenItem;

	/**
	 * The target rectangle of the draw operation.
	 */
	Common::Rect rect;

	inline bool operator<(const DrawItem &other) const {
		return *screenItem < *other.screenItem;
	}
};

typedef StablePointerArray<DrawItem, 250> DrawListBase;
class DrawList : public DrawListBase {
private:
	inline static bool sortHelper(const DrawItem *a, const DrawItem *b) {
		return *a < *b;
	}
public:
	void add(ScreenItem *screenItem, const Common::Rect &rect);
	inline void sort() {
		pack();
		Common::sort(begin(), end(), sortHelper);
	}
};

class PlaneList;

#pragma mark -
#pragma mark Plane

/**
 * A plane is a grouped layer of screen items.
 */
class Plane {
private:
	/**
	 * A serial used for planes that are generated inside the graphics engine,
	 * rather than the interpreter.
	 */
	static uint16 _nextObjectId;

	/**
	 * A serial used to identify the creation order of planes, to ensure a
	 * stable sort order for planes with identical priorities.
	 */
	static uint32 _nextCreationId;

	/**
	 * The creation order number, which ensures a stable sort when planes with
	 * identical priorities are added to the plane list.
	 */
	uint32 _creationId;

	/**
	 * For planes that are used to render picture data, the resource ID of the
	 * picture to be displayed. This value may also be one of the special
	 * PlanePictureCodes, in which case the plane becomes a non-picture plane.
	 */
	GuiResourceId _pictureId;

	/**
	 * Whether or not the contents of picture planes should be drawn
	 * horizontally mirrored. Only applies to planes of type kPlaneTypePicture.
	 */
	bool _mirrored;

	/**
	 * Whether the picture ID for this plane has changed. This flag is set when
	 * the plane is created or updated from a VM object, and is cleared when the
	 * plane is synchronised to another plane (which calls changePic).
	 */
	bool _pictureChanged;

	/**
	 * Converts the dimensions of the game rect used by scripts to the
	 * dimensions of the plane rect used to render content to the screen.
	 * Coordinates with remainders are rounded up to the next whole pixel.
	 */
	void convertGameRectToPlaneRect();

	/**
	 * Sets the type of the plane according to its assigned picture resource ID.
	 */
	void setType();

public:
	/**
	 * The type of the plane.
	 */
	PlaneType _type;

	/**
	 * The color to use when erasing the plane. Only applies to planes of type
	 * kPlaneTypeColored.
	 */
	byte _back;

	/**
	 * Whether the priority of this plane has changed. This flag is set when the
	 * plane is updated from another plane and cleared when draw list
	 * calculation occurs.
	 */
	int _priorityChanged;

	/**
	 * A handle to the VM object corresponding to this plane. Some planes are
	 * generated purely within the graphics engine and have a numeric object
	 * value.
	 */
	reg_t _object;

	/**
	 * The rendering priority of the plane. Higher priorities are drawn above
	 * lower priorities.
	 */
	int16 _priority;

	/**
	 * Whether or not all screen items in this plane should be redrawn on the
	 * next frameout, instead of just the screen items marked as updated. This
	 * is set when visual changes to the plane itself are made that affect the
	 * rendering of the entire plane, and cleared once those changes are
	 * rendered by `redrawAll`.
	 */
	int _redrawAllCount;

	/**
	 * Flags indicating the state of the plane.
	 * - `created` is set when the plane is first created, either from a VM
	 *   object or from within the engine itself
	 * - `updated` is set when the plane is updated from another plane and the
	 *   two planes' `planeRect`s do not match
	 * - `deleted` is set when the plane is deleted by a kernel call
	 * - `moved` is set when the plane has been moved or resized
	 */
	int _created, _updated, _deleted, _moved;

	/**
	 * The vanishing point for the plane. Used when automatically calculating
	 * the correct scaling of the plane's screen items according to their
	 * position.
	 */
	Common::Point _vanishingPoint;

	/**
	 * The position & dimensions of the plane in screen coordinates. This rect
	 * is not clipped to the screen, so may include coordinates that are
	 * offscreen.
	 */
	Common::Rect _planeRect;

	/**
	 * The position & dimensions of the plane in game script coordinates.
	 */
	Common::Rect _gameRect;

	/**
	 * The position & dimensions of the plane in screen coordinates. This rect
	 * is clipped to the screen.
	 */
	Common::Rect _screenRect;

	/**
	 * The list of screen items grouped within this plane.
	 */
	ScreenItemList _screenItemList;

public:
	/**
	 * Initialises static Plane members.
	 */
	static void init();

	// In SSCI this constructor signature did not accept a picture ID, but some
	// calls to construct planes with this signature immediately set the picture
	// ID and then called setType again, so it made more sense to just make the
	// picture ID a parameter instead.
	Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId = kPlanePicColored);

	Plane(const reg_t object);

	Plane(const Plane &other);

	void operator=(const Plane &other);

	inline bool operator<(const Plane &other) const {
		if (_priority < other._priority) {
			return true;
		}

		if (_priority == other._priority) {
			// This is different than SSCI; see ScreenItem::operator< for an
			// explanation
			return _creationId < other._creationId;
		}

		return false;
	}

	/**
	 * Clips the screen rect of this plane to fit within the given screen rect.
	 */
	inline void clipScreenRect(const Common::Rect &screenRect) {
		// LSL6 hires creates planes with invalid rects; SSCI does not care
		// about this, but `Common::Rect::clip` does, so we need to check
		// whether or not the rect is actually valid before clipping and only
		// clip valid rects
		if (_screenRect.isValidRect() && _screenRect.intersects(screenRect)) {
			_screenRect.clip(screenRect);
		} else {
			_screenRect.left = 0;
			_screenRect.top = 0;
			_screenRect.right = 0;
			_screenRect.bottom = 0;
		}
	}

	void printDebugInfo(Console *con) const;

	/**
	 * Compares the properties of the current plane against the properties of
	 * the `other` plane (which is the corresponding plane from the visible
	 * plane list) to discover which properties have been changed on this plane
	 * by a call to `update(reg_t)`.
	 *
	 * @note This method was called UpdatePlane in SSCI.
	 */
	void sync(const Plane *other, const Common::Rect &screenRect);

	/**
	 * Updates the plane to match the state of the plane object from the VM.
	 *
	 * @note This method was called UpdatePlane in SSCI.
	 */
	void update(const reg_t object);

	/**
	 * Modifies the position of all non-pic screen items by the given delta. If
	 * `scrollPics` is true, pic items are also repositioned.
	 */
	void scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics);

#pragma mark -
#pragma mark Plane - Pic
private:
	/**
	 * Adds all cels from the specified picture resource to the plane as screen
	 * items. If a position is provided, the screen items will be given that
	 * position; otherwise, the default relative positions for each cel will be
	 * taken from the picture resource data.
	 */
	inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX);

	/**
	 * Marks all screen items to be deleted that are within this plane and match
	 * the given picture ID.
	 */
	void deletePic(const GuiResourceId pictureId);

	/**
	 * Marks all screen items to be deleted that are within this plane and are
	 * picture cels.
	 */
	void deleteAllPics();

public:
	/**
	 * Marks all existing screen items matching the current picture to be
	 * deleted, then adds all cels from the new picture resource to the plane at
	 * the given position.
	 */
	GuiResourceId addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX, const bool deleteDuplicate = true);

	/**
	 * If the plane is a picture plane, re-adds all cels from its picture
	 * resource to the plane. Otherwise, just clears the _pictureChanged flag.
	 */
	void changePic();

	/**
	 * Marks all screen items to be deleted that are within this plane and match
	 * the given picture ID, then sets the picture ID of the plane to the new
	 * picture ID without adding any screen items.
	 */
	void deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId);

#pragma mark -
#pragma mark Plane - Rendering
private:
	/**
	 * Splits all rects in the given draw list at the edges of all
	 * higher-priority, non-transparent, intersecting planes.
	 */
	void breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const;

	/**
	 * Splits all rects in the given erase list at the edges of higher-priority,
	 * non-transparent, intersecting planes.
	 */
	void breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const;

	/**
	 * Adds the screen item at `index` into `drawList`, ensuring it is only
	 * drawn within the bounds of `rect`. If an existing draw list entry exists
	 * for this screen item, it will be modified. Otherwise, a new entry will be
	 * added.
	 */
	void mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const;

	/**
	 * Merges `rect` with an existing rect in `eraseList`, if possible.
	 * Otherwise, adds the rect as a new entry to `eraseList`.
	 */
	void mergeToRectList(const Common::Rect &rect, RectList &eraseList) const;

public:
	/**
	 * Calculates the location and dimensions of dirty rects of the screen items
	 * in this plane and adds them to the given draw and erase lists, and
	 * synchronises this plane's list of screen items to the given visible
	 * plane.
	 */
	void calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList);

	/**
	 * Synchronises changes to screen items from the current plane to the
	 * visible plane and deletes screen items from the current plane that have
	 * been marked as deleted. If `forceUpdate` is true, all screen items on the
	 * visible plane will be updated, even if they are not marked as having
	 * changed.
	 */
	void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate);

	/**
	 * This method is called from the highest priority plane to the lowest
	 * priority plane.
	 *
	 * Adds screen items from this plane to the draw list that must be redrawn
	 * because they intersect entries in the `higherEraseList`.
	 *
	 * If this plane is opaque, all intersecting erase rects in `lowerEraseList`
	 * are removed, as they would be completely overwritten by the contents of
	 * this plane.
	 *
	 * If this plane is transparent, erase rects from the `lowerEraseList` are
	 * added to the erase list for this plane, so that lower planes.
	 *
	 * @param drawList The draw list for this plane.
	 * @param eraseList The erase list for this plane.
	 * @param higherEraseList The erase list for a plane above this plane.
	 */
	void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const;

	/**
	 * This method is called from the lowest priority plane to the highest
	 * priority plane.
	 *
	 * Adds screen items from this plane to the draw list that must be drawn
	 * because the lower plane is being redrawn and potentially transparent
	 * screen items from this plane would draw over the lower priority plane's
	 * screen items.
	 *
	 * This method applies only to transparent planes.
	 *
	 * @param drawList The draw list for this plane.
	 * @param eraseList The erase list for a plane below this plane.
	 */
	void filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const;

	/**
	 * This method is called from the lowest priority plane to the highest
	 * priority plane.
	 *
	 * Adds screen items from this plane to the draw list that must be drawn
	 * because the lower plane is being redrawn and potentially transparent
	 * screen items from this plane would draw over the lower priority plane's
	 * screen items.
	 *
	 * This method applies only to transparent planes.
	 *
	 * @param drawList The draw list for this plane.
	 * @param lowerDrawList The draw list for a plane below this plane.
	 */
	void filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const;

	/**
	 * Updates all of the plane's non-deleted screen items and adds them to the
	 * given draw and erase lists.
	 */
	void redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList);

	/**
	 * Marks all non-deleted remapped screen items within the plane as needing
	 * to be updated during the next frameout.
	 */
	void remapMarkRedraw();
};

#pragma mark -
#pragma mark PlaneList

typedef Common::Array<Plane *> PlaneListBase;
class PlaneList : public PlaneListBase {
private:
	inline static bool sortHelper(const Plane *a, const Plane *b) {
		return *a < *b;
	}

	using PlaneListBase::push_back;

public:
	// A method for finding the index of a plane inside a PlaneList is used
	// because entries in the main plane list and visible plane list of
	// GfxFrameout are synchronised by index
	int findIndexByObject(const reg_t object) const;
	Plane *findByObject(const reg_t object) const;

	/**
	 * Gets the priority of the top plane in the plane list.
	 */
	int16 getTopPlanePriority() const;

	/**
	 * Gets the priority of the top plane in the plane list created by a game
	 * script.
	 */
	int16 getTopSciPlanePriority() const;

	void add(Plane *plane);
	void clear();
	iterator erase(iterator it);
	void erase(Plane *plane);
	inline void sort() {
		Common::sort(begin(), end(), sortHelper);
	}
	void remove_at(size_type index);
};

} // End of namespace Sci

#endif