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 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (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, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 #ifndef SCI_GRAPHICS_PLANE32_H 24 #define SCI_GRAPHICS_PLANE32_H 25 26 #include "common/array.h" 27 #include "common/rect.h" 28 #include "sci/engine/features.h" 29 #include "sci/engine/vm_types.h" 30 #include "sci/graphics/helpers.h" 31 #include "sci/graphics/lists32.h" 32 #include "sci/graphics/screen_item32.h" 33 34 namespace Sci { 35 enum PlaneType { 36 kPlaneTypeColored = 0, 37 kPlaneTypePicture = 1, 38 kPlaneTypeTransparent = 2, 39 kPlaneTypeOpaque = 3, 40 kPlaneTypeTransparentPicture = 4 41 }; 42 43 enum PlanePictureCodes { 44 // Any value at or below 65531 means the plane is a kPlaneTypePicture 45 kPlanePic = 65531, 46 kPlanePicTransparentPicture = 65532, 47 kPlanePicOpaque = 65533, 48 kPlanePicTransparent = 65534, 49 kPlanePicColored = 65535 50 }; 51 52 #pragma mark - 53 #pragma mark RectList 54 55 typedef StablePointerArray<Common::Rect, 200> RectListBase; 56 class RectList : public RectListBase { 57 public: add(const Common::Rect & rect)58 void add(const Common::Rect &rect) { 59 RectListBase::add(new Common::Rect(rect)); 60 } 61 }; 62 63 #pragma mark - 64 #pragma mark DrawList 65 66 struct DrawItem { 67 /** 68 * The screen item to draw. 69 */ 70 ScreenItem *screenItem; 71 72 /** 73 * The target rectangle of the draw operation. 74 */ 75 Common::Rect rect; 76 77 inline bool operator<(const DrawItem &other) const { 78 return *screenItem < *other.screenItem; 79 } 80 }; 81 82 typedef StablePointerArray<DrawItem, 250> DrawListBase; 83 class DrawList : public DrawListBase { 84 private: sortHelper(const DrawItem * a,const DrawItem * b)85 inline static bool sortHelper(const DrawItem *a, const DrawItem *b) { 86 return *a < *b; 87 } 88 public: 89 void add(ScreenItem *screenItem, const Common::Rect &rect); sort()90 inline void sort() { 91 pack(); 92 Common::sort(begin(), end(), sortHelper); 93 } 94 }; 95 96 class PlaneList; 97 98 #pragma mark - 99 #pragma mark Plane 100 101 /** 102 * A plane is a grouped layer of screen items. 103 */ 104 class Plane { 105 private: 106 /** 107 * A serial used for planes that are generated inside the graphics engine, 108 * rather than the interpreter. 109 */ 110 static uint16 _nextObjectId; 111 112 /** 113 * A serial used to identify the creation order of planes, to ensure a 114 * stable sort order for planes with identical priorities. 115 */ 116 static uint32 _nextCreationId; 117 118 /** 119 * The creation order number, which ensures a stable sort when planes with 120 * identical priorities are added to the plane list. 121 */ 122 uint32 _creationId; 123 124 /** 125 * For planes that are used to render picture data, the resource ID of the 126 * picture to be displayed. This value may also be one of the special 127 * PlanePictureCodes, in which case the plane becomes a non-picture plane. 128 */ 129 GuiResourceId _pictureId; 130 131 /** 132 * Whether or not the contents of picture planes should be drawn 133 * horizontally mirrored. Only applies to planes of type kPlaneTypePicture. 134 */ 135 bool _mirrored; 136 137 /** 138 * Whether the picture ID for this plane has changed. This flag is set when 139 * the plane is created or updated from a VM object, and is cleared when the 140 * plane is synchronised to another plane (which calls changePic). 141 */ 142 bool _pictureChanged; 143 144 /** 145 * Converts the dimensions of the game rect used by scripts to the 146 * dimensions of the plane rect used to render content to the screen. 147 * Coordinates with remainders are rounded up to the next whole pixel. 148 */ 149 void convertGameRectToPlaneRect(); 150 151 /** 152 * Sets the type of the plane according to its assigned picture resource ID. 153 */ 154 void setType(); 155 156 public: 157 /** 158 * The type of the plane. 159 */ 160 PlaneType _type; 161 162 /** 163 * The color to use when erasing the plane. Only applies to planes of type 164 * kPlaneTypeColored. 165 */ 166 byte _back; 167 168 /** 169 * Whether the priority of this plane has changed. This flag is set when the 170 * plane is updated from another plane and cleared when draw list 171 * calculation occurs. 172 */ 173 int _priorityChanged; 174 175 /** 176 * A handle to the VM object corresponding to this plane. Some planes are 177 * generated purely within the graphics engine and have a numeric object 178 * value. 179 */ 180 reg_t _object; 181 182 /** 183 * The rendering priority of the plane. Higher priorities are drawn above 184 * lower priorities. 185 */ 186 int16 _priority; 187 188 /** 189 * Whether or not all screen items in this plane should be redrawn on the 190 * next frameout, instead of just the screen items marked as updated. This 191 * is set when visual changes to the plane itself are made that affect the 192 * rendering of the entire plane, and cleared once those changes are 193 * rendered by `redrawAll`. 194 */ 195 int _redrawAllCount; 196 197 /** 198 * Flags indicating the state of the plane. 199 * - `created` is set when the plane is first created, either from a VM 200 * object or from within the engine itself 201 * - `updated` is set when the plane is updated from another plane and the 202 * two planes' `planeRect`s do not match 203 * - `deleted` is set when the plane is deleted by a kernel call 204 * - `moved` is set when the plane has been moved or resized 205 */ 206 int _created, _updated, _deleted, _moved; 207 208 /** 209 * The vanishing point for the plane. Used when automatically calculating 210 * the correct scaling of the plane's screen items according to their 211 * position. 212 */ 213 Common::Point _vanishingPoint; 214 215 /** 216 * The position & dimensions of the plane in screen coordinates. This rect 217 * is not clipped to the screen, so may include coordinates that are 218 * offscreen. 219 */ 220 Common::Rect _planeRect; 221 222 /** 223 * The position & dimensions of the plane in game script coordinates. 224 */ 225 Common::Rect _gameRect; 226 227 /** 228 * The position & dimensions of the plane in screen coordinates. This rect 229 * is clipped to the screen. 230 */ 231 Common::Rect _screenRect; 232 233 /** 234 * The list of screen items grouped within this plane. 235 */ 236 ScreenItemList _screenItemList; 237 238 public: 239 /** 240 * Initialises static Plane members. 241 */ 242 static void init(); 243 244 // In SSCI this constructor signature did not accept a picture ID, but some 245 // calls to construct planes with this signature immediately set the picture 246 // ID and then called setType again, so it made more sense to just make the 247 // picture ID a parameter instead. 248 Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId = kPlanePicColored); 249 250 Plane(const reg_t object); 251 252 Plane(const Plane &other); 253 254 void operator=(const Plane &other); 255 256 inline bool operator<(const Plane &other) const { 257 if (_priority < other._priority) { 258 return true; 259 } 260 261 if (_priority == other._priority) { 262 // This is different than SSCI; see ScreenItem::operator< for an 263 // explanation 264 return _creationId < other._creationId; 265 } 266 267 return false; 268 } 269 270 /** 271 * Clips the screen rect of this plane to fit within the given screen rect. 272 */ clipScreenRect(const Common::Rect & screenRect)273 inline void clipScreenRect(const Common::Rect &screenRect) { 274 // LSL6 hires creates planes with invalid rects; SSCI does not care 275 // about this, but `Common::Rect::clip` does, so we need to check 276 // whether or not the rect is actually valid before clipping and only 277 // clip valid rects 278 if (_screenRect.isValidRect() && _screenRect.intersects(screenRect)) { 279 _screenRect.clip(screenRect); 280 } else { 281 _screenRect.left = 0; 282 _screenRect.top = 0; 283 _screenRect.right = 0; 284 _screenRect.bottom = 0; 285 } 286 } 287 288 void printDebugInfo(Console *con) const; 289 290 /** 291 * Compares the properties of the current plane against the properties of 292 * the `other` plane (which is the corresponding plane from the visible 293 * plane list) to discover which properties have been changed on this plane 294 * by a call to `update(reg_t)`. 295 * 296 * @note This method was called UpdatePlane in SSCI. 297 */ 298 void sync(const Plane *other, const Common::Rect &screenRect); 299 300 /** 301 * Updates the plane to match the state of the plane object from the VM. 302 * 303 * @note This method was called UpdatePlane in SSCI. 304 */ 305 void update(const reg_t object); 306 307 /** 308 * Modifies the position of all non-pic screen items by the given delta. If 309 * `scrollPics` is true, pic items are also repositioned. 310 */ 311 void scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics); 312 313 /** 314 * The default plane is the first plane generated inside the graphics engine. 315 */ isDefaultPlane()316 inline bool isDefaultPlane() { 317 return _object.isNumber() && _object.getOffset() == (uint32)g_sci->_features->detectPlaneIdBase(); 318 } 319 320 #pragma mark - 321 #pragma mark Plane - Pic 322 private: 323 /** 324 * Adds all cels from the specified picture resource to the plane as screen 325 * items. If a position is provided, the screen items will be given that 326 * position; otherwise, the default relative positions for each cel will be 327 * taken from the picture resource data. 328 */ 329 inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX); 330 331 /** 332 * Marks all screen items to be deleted that are within this plane and match 333 * the given picture ID. 334 */ 335 void deletePic(const GuiResourceId pictureId); 336 337 /** 338 * Marks all screen items to be deleted that are within this plane and are 339 * picture cels. 340 */ 341 void deleteAllPics(); 342 343 public: 344 /** 345 * Marks all existing screen items matching the current picture to be 346 * deleted, then adds all cels from the new picture resource to the plane at 347 * the given position. 348 */ 349 GuiResourceId addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX, const bool deleteDuplicate = true); 350 351 /** 352 * If the plane is a picture plane, re-adds all cels from its picture 353 * resource to the plane. Otherwise, just clears the _pictureChanged flag. 354 */ 355 void changePic(); 356 357 /** 358 * Marks all screen items to be deleted that are within this plane and match 359 * the given picture ID, then sets the picture ID of the plane to the new 360 * picture ID without adding any screen items. 361 */ 362 void deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId); 363 364 #pragma mark - 365 #pragma mark Plane - Rendering 366 private: 367 /** 368 * Splits all rects in the given draw list at the edges of all 369 * higher-priority, non-transparent, intersecting planes. 370 */ 371 void breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const; 372 373 /** 374 * Splits all rects in the given erase list at the edges of higher-priority, 375 * non-transparent, intersecting planes. 376 */ 377 void breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const; 378 379 /** 380 * Adds the screen item at `index` into `drawList`, ensuring it is only 381 * drawn within the bounds of `rect`. If an existing draw list entry exists 382 * for this screen item, it will be modified. Otherwise, a new entry will be 383 * added. 384 */ 385 void mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const; 386 387 /** 388 * Merges `rect` with an existing rect in `eraseList`, if possible. 389 * Otherwise, adds the rect as a new entry to `eraseList`. 390 */ 391 void mergeToRectList(const Common::Rect &rect, RectList &eraseList) const; 392 393 public: 394 /** 395 * Calculates the location and dimensions of dirty rects of the screen items 396 * in this plane and adds them to the given draw and erase lists, and 397 * synchronises this plane's list of screen items to the given visible 398 * plane. 399 */ 400 void calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList); 401 402 /** 403 * Synchronises changes to screen items from the current plane to the 404 * visible plane and deletes screen items from the current plane that have 405 * been marked as deleted. If `forceUpdate` is true, all screen items on the 406 * visible plane will be updated, even if they are not marked as having 407 * changed. 408 */ 409 void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate); 410 411 /** 412 * This method is called from the highest priority plane to the lowest 413 * priority plane. 414 * 415 * Adds screen items from this plane to the draw list that must be redrawn 416 * because they intersect entries in the `higherEraseList`. 417 * 418 * If this plane is opaque, all intersecting erase rects in `lowerEraseList` 419 * are removed, as they would be completely overwritten by the contents of 420 * this plane. 421 * 422 * If this plane is transparent, erase rects from the `lowerEraseList` are 423 * added to the erase list for this plane, so that lower planes. 424 * 425 * @param drawList The draw list for this plane. 426 * @param eraseList The erase list for this plane. 427 * @param higherEraseList The erase list for a plane above this plane. 428 */ 429 void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const; 430 431 /** 432 * This method is called from the lowest priority plane to the highest 433 * priority plane. 434 * 435 * Adds screen items from this plane to the draw list that must be drawn 436 * because the lower plane is being redrawn and potentially transparent 437 * screen items from this plane would draw over the lower priority plane's 438 * screen items. 439 * 440 * This method applies only to transparent planes. 441 * 442 * @param drawList The draw list for this plane. 443 * @param eraseList The erase list for a plane below this plane. 444 */ 445 void filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const; 446 447 /** 448 * This method is called from the lowest priority plane to the highest 449 * priority plane. 450 * 451 * Adds screen items from this plane to the draw list that must be drawn 452 * because the lower plane is being redrawn and potentially transparent 453 * screen items from this plane would draw over the lower priority plane's 454 * screen items. 455 * 456 * This method applies only to transparent planes. 457 * 458 * @param drawList The draw list for this plane. 459 * @param lowerDrawList The draw list for a plane below this plane. 460 */ 461 void filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const; 462 463 /** 464 * Updates all of the plane's non-deleted screen items and adds them to the 465 * given draw and erase lists. 466 */ 467 void redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList); 468 469 /** 470 * Marks all non-deleted remapped screen items within the plane as needing 471 * to be updated during the next frameout. 472 */ 473 void remapMarkRedraw(); 474 }; 475 476 #pragma mark - 477 #pragma mark PlaneList 478 479 typedef Common::Array<Plane *> PlaneListBase; 480 class PlaneList : public PlaneListBase { 481 private: sortHelper(const Plane * a,const Plane * b)482 inline static bool sortHelper(const Plane *a, const Plane *b) { 483 return *a < *b; 484 } 485 486 using PlaneListBase::push_back; 487 488 public: 489 // A method for finding the index of a plane inside a PlaneList is used 490 // because entries in the main plane list and visible plane list of 491 // GfxFrameout are synchronised by index 492 int findIndexByObject(const reg_t object) const; 493 Plane *findByObject(const reg_t object) const; 494 495 /** 496 * Gets the priority of the top plane in the plane list. 497 */ 498 int16 getTopPlanePriority() const; 499 500 /** 501 * Gets the priority of the top plane in the plane list created by a game 502 * script. 503 */ 504 int16 getTopSciPlanePriority() const; 505 506 void add(Plane *plane); 507 void clear(); 508 iterator erase(iterator it); 509 void erase(Plane *plane); sort()510 inline void sort() { 511 Common::sort(begin(), end(), sortHelper); 512 } 513 void remove_at(size_type index); 514 }; 515 516 } // End of namespace Sci 517 518 #endif 519