Rendering bugs in ScummVM are often caused by buggy game scripts relying on the last ditch sort, which is not the same in ScummVM as in SSCI (since the SSCI last ditch sort relies on a different memory architecture and is super buggy). However, these bugs do not show up very frequently these days, so it is easy to forget all the places that need to be checked when debugging a rendering problem that appears to be caused by sorting failure. This commit breaks out the last ditch comparison formerly in Plane::calcLists to hopefully make it more visible to future programmers. Refs Trac#9957.
970 lines
28 KiB
C++
970 lines
28 KiB
C++
/* 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.
|
|
*
|
|
*/
|
|
|
|
#include "sci/console.h"
|
|
#include "sci/engine/features.h"
|
|
#include "sci/engine/kernel.h"
|
|
#include "sci/engine/selector.h"
|
|
#include "sci/engine/state.h"
|
|
#include "sci/graphics/frameout.h"
|
|
#include "sci/graphics/helpers.h"
|
|
#include "sci/graphics/lists32.h"
|
|
#include "sci/graphics/plane32.h"
|
|
#include "sci/graphics/remap32.h"
|
|
#include "sci/graphics/screen.h"
|
|
#include "sci/graphics/screen_item32.h"
|
|
|
|
namespace Sci {
|
|
#pragma mark DrawList
|
|
void DrawList::add(ScreenItem *screenItem, const Common::Rect &rect) {
|
|
DrawItem *drawItem = new DrawItem;
|
|
drawItem->screenItem = screenItem;
|
|
drawItem->rect = rect;
|
|
DrawListBase::add(drawItem);
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Plane
|
|
uint16 Plane::_nextObjectId = 20000;
|
|
uint32 Plane::_nextCreationId = 0;
|
|
|
|
Plane::Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId) :
|
|
_creationId(_nextCreationId++),
|
|
_pictureId(pictureId),
|
|
_mirrored(false),
|
|
_type(kPlaneTypeColored),
|
|
_back(0),
|
|
_priorityChanged(false),
|
|
_object(make_reg(0, _nextObjectId++)),
|
|
_redrawAllCount(g_sci->_gfxFrameout->getScreenCount()),
|
|
_created(g_sci->_gfxFrameout->getScreenCount()),
|
|
_updated(0),
|
|
_deleted(0),
|
|
_moved(0),
|
|
_gameRect(gameRect) {
|
|
convertGameRectToPlaneRect();
|
|
_priority = MAX(10000, g_sci->_gfxFrameout->getPlanes().getTopPlanePriority() + 1);
|
|
setType();
|
|
_screenRect = _planeRect;
|
|
}
|
|
|
|
Plane::Plane(reg_t object) :
|
|
_creationId(_nextCreationId++),
|
|
_type(kPlaneTypeColored),
|
|
_priorityChanged(false),
|
|
_object(object),
|
|
_redrawAllCount(g_sci->_gfxFrameout->getScreenCount()),
|
|
_created(g_sci->_gfxFrameout->getScreenCount()),
|
|
_updated(0),
|
|
_deleted(0),
|
|
_moved(0) {
|
|
SegManager *segMan = g_sci->getEngineState()->_segMan;
|
|
_vanishingPoint.x = readSelectorValue(segMan, object, SELECTOR(vanishingX));
|
|
_vanishingPoint.y = readSelectorValue(segMan, object, SELECTOR(vanishingY));
|
|
|
|
if (g_sci->_features->usesAlternateSelectors()) {
|
|
_gameRect.left = readSelectorValue(segMan, object, SELECTOR(left));
|
|
_gameRect.top = readSelectorValue(segMan, object, SELECTOR(top));
|
|
_gameRect.right = readSelectorValue(segMan, object, SELECTOR(right)) + 1;
|
|
_gameRect.bottom = readSelectorValue(segMan, object, SELECTOR(bottom)) + 1;
|
|
} else {
|
|
_gameRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
|
|
_gameRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
|
|
_gameRect.right = readSelectorValue(segMan, object, SELECTOR(inRight)) + 1;
|
|
_gameRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom)) + 1;
|
|
}
|
|
convertGameRectToPlaneRect();
|
|
|
|
_back = readSelectorValue(segMan, object, SELECTOR(back));
|
|
_priority = readSelectorValue(segMan, object, SELECTOR(priority));
|
|
_pictureId = readSelectorValue(segMan, object, SELECTOR(picture));
|
|
setType();
|
|
|
|
_mirrored = readSelectorValue(segMan, object, SELECTOR(mirrored));
|
|
_screenRect = _planeRect;
|
|
changePic();
|
|
}
|
|
|
|
Plane::Plane(const Plane &other) :
|
|
_creationId(other._creationId),
|
|
_pictureId(other._pictureId),
|
|
_mirrored(other._mirrored),
|
|
_type(other._type),
|
|
_back(other._back),
|
|
_object(other._object),
|
|
_priority(other._priority),
|
|
_planeRect(other._planeRect),
|
|
_gameRect(other._gameRect),
|
|
_screenRect(other._screenRect),
|
|
_screenItemList(other._screenItemList) {}
|
|
|
|
void Plane::operator=(const Plane &other) {
|
|
_creationId = other._creationId;
|
|
_gameRect = other._gameRect;
|
|
_planeRect = other._planeRect;
|
|
_vanishingPoint = other._vanishingPoint;
|
|
_pictureId = other._pictureId;
|
|
_type = other._type;
|
|
_mirrored = other._mirrored;
|
|
_priority = other._priority;
|
|
_back = other._back;
|
|
_screenRect = other._screenRect;
|
|
_priorityChanged = other._priorityChanged;
|
|
}
|
|
|
|
void Plane::init() {
|
|
_nextObjectId = 20000;
|
|
_nextCreationId = 0;
|
|
}
|
|
|
|
void Plane::convertGameRectToPlaneRect() {
|
|
const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
|
|
const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
|
|
const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
|
|
const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
|
|
|
|
const Ratio ratioX = Ratio(screenWidth, scriptWidth);
|
|
const Ratio ratioY = Ratio(screenHeight, scriptHeight);
|
|
|
|
_planeRect = _gameRect;
|
|
mulru(_planeRect, ratioX, ratioY, 1);
|
|
}
|
|
|
|
void Plane::printDebugInfo(Console *con) const {
|
|
const char *name;
|
|
if (_object.isNumber()) {
|
|
name = "-scummvm-";
|
|
} else {
|
|
name = g_sci->getEngineState()->_segMan->getObjectName(_object);
|
|
}
|
|
|
|
con->debugPrintf("%04x:%04x (%s): type %d, prio %d, ins %u, pic %d, mirror %d, back %d\n",
|
|
PRINT_REG(_object),
|
|
name,
|
|
_type,
|
|
_priority,
|
|
_creationId,
|
|
_pictureId,
|
|
_mirrored,
|
|
_back
|
|
);
|
|
con->debugPrintf(" game rect: (%d, %d, %d, %d), plane rect: (%d, %d, %d, %d)\n screen rect: (%d, %d, %d, %d)\n",
|
|
PRINT_RECT(_gameRect),
|
|
PRINT_RECT(_planeRect),
|
|
PRINT_RECT(_screenRect)
|
|
);
|
|
con->debugPrintf(" # screen items: %d\n", _screenItemList.size());
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Plane - Pic
|
|
|
|
void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX) {
|
|
|
|
uint16 celCount = 1000;
|
|
bool transparent = true;
|
|
for (uint16 celNo = 0; celNo < celCount; ++celNo) {
|
|
CelObjPic *celObj = new CelObjPic(pictureId, celNo);
|
|
if (celCount == 1000) {
|
|
celCount = celObj->_celCount;
|
|
}
|
|
if (!celObj->_transparent) {
|
|
transparent = false;
|
|
}
|
|
|
|
ScreenItem *screenItem = new ScreenItem(_object, celObj->_info);
|
|
screenItem->_pictureId = pictureId;
|
|
screenItem->_mirrorX = mirrorX;
|
|
screenItem->_priority = celObj->_priority;
|
|
screenItem->_fixedPriority = true;
|
|
if (position != nullptr) {
|
|
screenItem->_position = *position + celObj->_relativePosition;
|
|
} else {
|
|
screenItem->_position = celObj->_relativePosition;
|
|
}
|
|
_screenItemList.add(screenItem);
|
|
|
|
delete screenItem->_celObj;
|
|
screenItem->_celObj = celObj;
|
|
}
|
|
_type = (g_sci->_features->hasTransparentPicturePlanes() && transparent) ? kPlaneTypeTransparentPicture : kPlaneTypePicture;
|
|
}
|
|
|
|
GuiResourceId Plane::addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX, const bool deleteDuplicate) {
|
|
if (deleteDuplicate) {
|
|
deletePic(pictureId);
|
|
}
|
|
addPicInternal(pictureId, &position, mirrorX);
|
|
return _pictureId;
|
|
}
|
|
|
|
void Plane::changePic() {
|
|
_pictureChanged = false;
|
|
|
|
if (_type != kPlaneTypePicture && _type != kPlaneTypeTransparentPicture) {
|
|
return;
|
|
}
|
|
|
|
addPicInternal(_pictureId, nullptr, _mirrored);
|
|
}
|
|
|
|
void Plane::deletePic(const GuiResourceId pictureId) {
|
|
for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) {
|
|
ScreenItem *screenItem = *it;
|
|
if (screenItem->_pictureId == pictureId) {
|
|
screenItem->_created = 0;
|
|
screenItem->_updated = 0;
|
|
screenItem->_deleted = g_sci->_gfxFrameout->getScreenCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Plane::deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId) {
|
|
deletePic(oldPictureId);
|
|
_pictureId = newPictureId;
|
|
}
|
|
|
|
void Plane::deleteAllPics() {
|
|
for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) {
|
|
ScreenItem *screenItem = *it;
|
|
if (screenItem != nullptr && screenItem->_celInfo.type == kCelTypePic) {
|
|
if (screenItem->_created == 0) {
|
|
screenItem->_created = 0;
|
|
screenItem->_updated = 0;
|
|
screenItem->_deleted = g_sci->_gfxFrameout->getScreenCount();
|
|
} else {
|
|
_screenItemList.erase(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
_screenItemList.pack();
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Plane - Rendering
|
|
|
|
void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const {
|
|
const int nextPlaneIndex = planeList.findIndexByObject(_object) + 1;
|
|
const PlaneList::size_type planeCount = planeList.size();
|
|
|
|
for (DrawList::size_type i = 0; i < drawList.size(); ++i) {
|
|
for (PlaneList::size_type j = nextPlaneIndex; j < planeCount; ++j) {
|
|
if (
|
|
planeList[j]->_type != kPlaneTypeTransparent &&
|
|
planeList[j]->_type != kPlaneTypeTransparentPicture
|
|
) {
|
|
Common::Rect outRects[4];
|
|
int splitCount = splitRects(drawList[i]->rect, planeList[j]->_screenRect, outRects);
|
|
if (splitCount != -1) {
|
|
while (splitCount--) {
|
|
drawList.add(drawList[i]->screenItem, outRects[splitCount]);
|
|
}
|
|
|
|
drawList.erase_at(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
drawList.pack();
|
|
}
|
|
|
|
void Plane::breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const {
|
|
const int nextPlaneIndex = planeList.findIndexByObject(_object) + 1;
|
|
const PlaneList::size_type planeCount = planeList.size();
|
|
|
|
for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
|
|
for (PlaneList::size_type j = nextPlaneIndex; j < planeCount; ++j) {
|
|
if (
|
|
planeList[j]->_type != kPlaneTypeTransparent &&
|
|
planeList[j]->_type != kPlaneTypeTransparentPicture
|
|
) {
|
|
Common::Rect outRects[4];
|
|
int splitCount = splitRects(*eraseList[i], planeList[j]->_screenRect, outRects);
|
|
if (splitCount != -1) {
|
|
while (splitCount--) {
|
|
eraseList.add(outRects[splitCount]);
|
|
}
|
|
|
|
eraseList.erase_at(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
eraseList.pack();
|
|
}
|
|
|
|
void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) {
|
|
const ScreenItemList::size_type screenItemCount = _screenItemList.size();
|
|
const ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size();
|
|
|
|
for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) {
|
|
// Items can be added to ScreenItemList and we don't want to process
|
|
// those new items, but the list also can grow smaller, so we need
|
|
// to check that we are still within the upper bound of the list and
|
|
// quit if we aren't any more
|
|
if (i >= _screenItemList.size()) {
|
|
break;
|
|
}
|
|
|
|
ScreenItem *item = _screenItemList[i];
|
|
if (item == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
// NOTE: The original engine used an array without bounds checking
|
|
// so could just get the visible screen item directly; we need to
|
|
// verify that the index is actually within the valid range for
|
|
// the visible plane before accessing the item to avoid a range
|
|
// error.
|
|
const ScreenItem *visibleItem = nullptr;
|
|
if (i < visiblePlaneItemCount) {
|
|
visibleItem = visiblePlane._screenItemList[i];
|
|
}
|
|
|
|
// Keep erase rects for this screen item from drawing outside
|
|
// of its owner plane
|
|
Common::Rect visibleItemScreenRect;
|
|
if (visibleItem != nullptr) {
|
|
visibleItemScreenRect = visibleItem->_screenRect;
|
|
visibleItemScreenRect.clip(_screenRect);
|
|
}
|
|
|
|
if (item->_deleted) {
|
|
// Add item's rect to erase list
|
|
if (
|
|
visibleItem != nullptr &&
|
|
!visibleItemScreenRect.isEmpty()
|
|
) {
|
|
if (g_sci->_gfxRemap32->getRemapCount()) {
|
|
mergeToRectList(visibleItemScreenRect, eraseList);
|
|
} else {
|
|
eraseList.add(visibleItemScreenRect);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!item->_created && !item->_updated) {
|
|
continue;
|
|
}
|
|
|
|
item->calcRects(*this);
|
|
const Common::Rect itemScreenRect(item->_screenRect);
|
|
|
|
if (item->_created) {
|
|
// Add item to draw list
|
|
if(!itemScreenRect.isEmpty()) {
|
|
if (g_sci->_gfxRemap32->getRemapCount()) {
|
|
drawList.add(item, itemScreenRect);
|
|
mergeToRectList(itemScreenRect, eraseList);
|
|
} else {
|
|
drawList.add(item, itemScreenRect);
|
|
}
|
|
}
|
|
} else {
|
|
// Add old rect to erase list, new item to draw list
|
|
|
|
if (g_sci->_gfxRemap32->getRemapCount()) {
|
|
// If item and visibleItem don't overlap...
|
|
if (itemScreenRect.isEmpty() ||
|
|
visibleItem == nullptr ||
|
|
visibleItemScreenRect.isEmpty() ||
|
|
!visibleItemScreenRect.intersects(itemScreenRect)
|
|
) {
|
|
// ...add item to draw list, and old rect to erase list...
|
|
if (!itemScreenRect.isEmpty()) {
|
|
drawList.add(item, itemScreenRect);
|
|
mergeToRectList(itemScreenRect, eraseList);
|
|
}
|
|
if (visibleItem != nullptr && !visibleItemScreenRect.isEmpty()) {
|
|
mergeToRectList(visibleItemScreenRect, eraseList);
|
|
}
|
|
} else {
|
|
// ...otherwise, add bounding box of old+new to erase list,
|
|
// and item to draw list
|
|
Common::Rect extendedScreenRect = visibleItemScreenRect;
|
|
extendedScreenRect.extend(itemScreenRect);
|
|
|
|
drawList.add(item, itemScreenRect);
|
|
mergeToRectList(extendedScreenRect, eraseList);
|
|
}
|
|
} else {
|
|
// If no active remaps, just add item to draw list and old rect
|
|
// to erase list
|
|
|
|
// TODO: SCI3 update rects for VMD?
|
|
if (!itemScreenRect.isEmpty()) {
|
|
drawList.add(item, itemScreenRect);
|
|
}
|
|
if (visibleItem != nullptr && !visibleItemScreenRect.isEmpty()) {
|
|
eraseList.add(visibleItemScreenRect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove parts of eraselist/drawlist that are covered by other planes
|
|
breakEraseListByPlanes(eraseList, planeList);
|
|
breakDrawListByPlanes(drawList, planeList);
|
|
|
|
// We store the current size of the drawlist, as we want to loop
|
|
// over the currently inserted entries later.
|
|
DrawList::size_type drawListSizePrimary = drawList.size();
|
|
const RectList::size_type eraseListCount = eraseList.size();
|
|
|
|
if (getSciVersion() == SCI_VERSION_3) {
|
|
_screenItemList.sort();
|
|
bool pictureDrawn = false;
|
|
bool screenItemDrawn = false;
|
|
|
|
for (RectList::size_type i = 0; i < eraseListCount; ++i) {
|
|
const Common::Rect &rect = *eraseList[i];
|
|
|
|
for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) {
|
|
ScreenItem *item = _screenItemList[j];
|
|
|
|
if (item == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
if (rect.intersects(item->_screenRect)) {
|
|
const Common::Rect intersection = rect.findIntersectingRect(item->_screenRect);
|
|
if (!item->_deleted) {
|
|
if (pictureDrawn) {
|
|
if (item->_celInfo.type == kCelTypePic) {
|
|
if (screenItemDrawn || item->_celInfo.celNo == 0) {
|
|
mergeToDrawList(j, intersection, drawList);
|
|
}
|
|
} else {
|
|
if (!item->_updated && !item->_created) {
|
|
mergeToDrawList(j, intersection, drawList);
|
|
}
|
|
screenItemDrawn = true;
|
|
}
|
|
} else {
|
|
if (!item->_updated && !item->_created) {
|
|
mergeToDrawList(j, intersection, drawList);
|
|
}
|
|
if (item->_celInfo.type == kCelTypePic) {
|
|
pictureDrawn = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_screenItemList.unsort();
|
|
} else {
|
|
// Add all items overlapping the erase list to the draw list
|
|
for (RectList::size_type i = 0; i < eraseListCount; ++i) {
|
|
const Common::Rect &rect = *eraseList[i];
|
|
for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) {
|
|
ScreenItem *item = _screenItemList[j];
|
|
if (
|
|
item != nullptr &&
|
|
!item->_created && !item->_updated && !item->_deleted &&
|
|
rect.intersects(item->_screenRect)
|
|
) {
|
|
drawList.add(item, rect.findIntersectingRect(item->_screenRect));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_sci->_gfxRemap32->getRemapCount() == 0) {
|
|
// Add all items that overlap with items in the drawlist and have higher
|
|
// priority.
|
|
|
|
// We only loop over "primary" items in the draw list, skipping
|
|
// those that were added because of the erase list in the previous loop,
|
|
// or those to be added in this loop.
|
|
for (DrawList::size_type i = 0; i < drawListSizePrimary; ++i) {
|
|
const DrawItem *drawListEntry = nullptr;
|
|
if (i < drawList.size()) {
|
|
drawListEntry = drawList[i];
|
|
}
|
|
|
|
for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) {
|
|
ScreenItem *newItem = nullptr;
|
|
if (j < _screenItemList.size()) {
|
|
newItem = _screenItemList[j];
|
|
}
|
|
|
|
if (
|
|
drawListEntry != nullptr && newItem != nullptr &&
|
|
!newItem->_created && !newItem->_updated && !newItem->_deleted
|
|
) {
|
|
const ScreenItem *drawnItem = drawListEntry->screenItem;
|
|
|
|
if (newItem->hasPriorityAbove(*drawnItem) &&
|
|
drawListEntry->rect.intersects(newItem->_screenRect)
|
|
) {
|
|
mergeToDrawList(j, drawListEntry->rect.findIntersectingRect(newItem->_screenRect), drawList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
decrementScreenItemArrayCounts(&visiblePlane, false);
|
|
}
|
|
|
|
void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate) {
|
|
const ScreenItemList::size_type screenItemCount = _screenItemList.size();
|
|
for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) {
|
|
ScreenItem *item = _screenItemList[i];
|
|
|
|
if (item != nullptr) {
|
|
// update item in visiblePlane if item is updated
|
|
if (visiblePlane != nullptr && (
|
|
item->_updated || (forceUpdate && visiblePlane->_screenItemList.findByObject(item->_object) != nullptr))) {
|
|
*visiblePlane->_screenItemList[i] = *item;
|
|
}
|
|
|
|
if (item->_updated) {
|
|
item->_updated--;
|
|
}
|
|
|
|
// create new item in visiblePlane if item was added
|
|
if (item->_created) {
|
|
item->_created--;
|
|
if (visiblePlane != nullptr) {
|
|
visiblePlane->_screenItemList.add(new ScreenItem(*item));
|
|
}
|
|
}
|
|
|
|
// delete item from both planes if it was deleted
|
|
if (item->_deleted) {
|
|
item->_deleted--;
|
|
if (!item->_deleted) {
|
|
if (visiblePlane != nullptr && visiblePlane->_screenItemList.findByObject(item->_object) != nullptr) {
|
|
visiblePlane->_screenItemList.erase_at(i);
|
|
}
|
|
_screenItemList.erase_at(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_screenItemList.pack();
|
|
if (visiblePlane != nullptr) {
|
|
visiblePlane->_screenItemList.pack();
|
|
}
|
|
}
|
|
|
|
void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const {
|
|
const RectList::size_type higherEraseCount = higherEraseList.size();
|
|
|
|
if (_type == kPlaneTypeTransparent || _type == kPlaneTypeTransparentPicture) {
|
|
for (RectList::size_type i = 0; i < higherEraseCount; ++i) {
|
|
const Common::Rect &r = *higherEraseList[i];
|
|
const ScreenItemList::size_type screenItemCount = _screenItemList.size();
|
|
for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) {
|
|
const ScreenItem *item = _screenItemList[j];
|
|
if (item != nullptr && r.intersects(item->_screenRect)) {
|
|
mergeToDrawList(j, r, drawList);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (RectList::size_type i = 0; i < higherEraseCount; ++i) {
|
|
Common::Rect r = *higherEraseList[i];
|
|
if (r.intersects(_screenRect)) {
|
|
r.clip(_screenRect);
|
|
mergeToRectList(r, eraseList);
|
|
|
|
const ScreenItemList::size_type screenItemCount = _screenItemList.size();
|
|
for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) {
|
|
const ScreenItem *item = _screenItemList[j];
|
|
if (item != nullptr && r.intersects(item->_screenRect)) {
|
|
mergeToDrawList(j, r, drawList);
|
|
}
|
|
}
|
|
|
|
Common::Rect outRects[4];
|
|
const Common::Rect &r2 = *higherEraseList[i];
|
|
int splitCount = splitRects(r2, r, outRects);
|
|
if (splitCount > 0) {
|
|
while (splitCount--) {
|
|
higherEraseList.add(outRects[splitCount]);
|
|
}
|
|
}
|
|
higherEraseList.erase_at(i);
|
|
}
|
|
}
|
|
|
|
higherEraseList.pack();
|
|
}
|
|
}
|
|
|
|
void Plane::filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const {
|
|
const DrawList::size_type lowerDrawCount = lowerDrawList.size();
|
|
for (DrawList::size_type i = 0; i < lowerDrawCount; ++i) {
|
|
const Common::Rect &r = lowerDrawList[i]->rect;
|
|
const ScreenItemList::size_type screenItemCount = _screenItemList.size();
|
|
for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) {
|
|
const ScreenItem *item = _screenItemList[j];
|
|
if (item != nullptr && r.intersects(item->_screenRect)) {
|
|
mergeToDrawList(j, r, drawList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Plane::filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const {
|
|
const RectList::size_type lowerEraseCount = lowerEraseList.size();
|
|
for (RectList::size_type i = 0; i < lowerEraseCount; ++i) {
|
|
const Common::Rect &r = *lowerEraseList[i];
|
|
const ScreenItemList::size_type screenItemCount = _screenItemList.size();
|
|
for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) {
|
|
const ScreenItem *item = _screenItemList[j];
|
|
if (item != nullptr && r.intersects(item->_screenRect)) {
|
|
mergeToDrawList(j, r, drawList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Plane::mergeToDrawList(const ScreenItemList::size_type index, const Common::Rect &rect, DrawList &drawList) const {
|
|
RectList mergeList;
|
|
ScreenItem &item = *_screenItemList[index];
|
|
Common::Rect r = item._screenRect;
|
|
r.clip(rect);
|
|
mergeList.add(r);
|
|
|
|
for (RectList::size_type i = 0; i < mergeList.size(); ++i) {
|
|
r = *mergeList[i];
|
|
|
|
const DrawList::size_type drawCount = drawList.size();
|
|
for (DrawList::size_type j = 0; j < drawCount; ++j) {
|
|
const DrawItem &drawItem = *drawList[j];
|
|
if (item._object == drawItem.screenItem->_object) {
|
|
if (drawItem.rect.contains(r)) {
|
|
mergeList.erase_at(i);
|
|
break;
|
|
}
|
|
|
|
Common::Rect outRects[4];
|
|
int splitCount = splitRects(r, drawItem.rect, outRects);
|
|
if (splitCount != -1) {
|
|
while (splitCount--) {
|
|
mergeList.add(outRects[splitCount]);
|
|
}
|
|
|
|
mergeList.erase_at(i);
|
|
|
|
// proceed to the next rect
|
|
r = *mergeList[++i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mergeList.pack();
|
|
|
|
for (RectList::size_type i = 0; i < mergeList.size(); ++i) {
|
|
drawList.add(&item, *mergeList[i]);
|
|
}
|
|
}
|
|
|
|
void Plane::mergeToRectList(const Common::Rect &rect, RectList &eraseList) const {
|
|
RectList mergeList;
|
|
Common::Rect r;
|
|
mergeList.add(rect);
|
|
|
|
for (RectList::size_type i = 0; i < mergeList.size(); ++i) {
|
|
r = *mergeList[i];
|
|
|
|
const RectList::size_type eraseCount = eraseList.size();
|
|
for (RectList::size_type j = 0; j < eraseCount; ++j) {
|
|
const Common::Rect &eraseRect = *eraseList[j];
|
|
if (eraseRect.contains(r)) {
|
|
mergeList.erase_at(i);
|
|
break;
|
|
}
|
|
|
|
Common::Rect outRects[4];
|
|
int splitCount = splitRects(r, eraseRect, outRects);
|
|
if (splitCount != -1) {
|
|
while (splitCount--) {
|
|
mergeList.add(outRects[splitCount]);
|
|
}
|
|
|
|
mergeList.erase_at(i);
|
|
|
|
// proceed to the next rect
|
|
r = *mergeList[++i];
|
|
}
|
|
}
|
|
}
|
|
|
|
mergeList.pack();
|
|
|
|
for (RectList::size_type i = 0; i < mergeList.size(); ++i) {
|
|
eraseList.add(*mergeList[i]);
|
|
}
|
|
}
|
|
|
|
void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) {
|
|
const ScreenItemList::size_type screenItemCount = _screenItemList.size();
|
|
for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) {
|
|
ScreenItem *screenItem = _screenItemList[i];
|
|
if (screenItem != nullptr && !screenItem->_deleted) {
|
|
screenItem->calcRects(*this);
|
|
if (!screenItem->_screenRect.isEmpty()) {
|
|
mergeToDrawList(i, screenItem->_screenRect, drawList);
|
|
}
|
|
}
|
|
}
|
|
|
|
eraseList.clear();
|
|
|
|
if (!_screenRect.isEmpty() && _type != kPlaneTypePicture && _type != kPlaneTypeOpaque) {
|
|
eraseList.add(_screenRect);
|
|
}
|
|
breakEraseListByPlanes(eraseList, planeList);
|
|
breakDrawListByPlanes(drawList, planeList);
|
|
--_redrawAllCount;
|
|
decrementScreenItemArrayCounts(visiblePlane, true);
|
|
}
|
|
|
|
void Plane::setType() {
|
|
switch (_pictureId) {
|
|
case kPlanePicColored:
|
|
_type = kPlaneTypeColored;
|
|
break;
|
|
case kPlanePicTransparent:
|
|
_type = kPlaneTypeTransparent;
|
|
break;
|
|
case kPlanePicOpaque:
|
|
_type = kPlaneTypeOpaque;
|
|
break;
|
|
case kPlanePicTransparentPicture:
|
|
if (g_sci->_features->hasTransparentPicturePlanes()) {
|
|
_type = kPlaneTypeTransparentPicture;
|
|
break;
|
|
}
|
|
// fall through for games without transparent picture planes
|
|
default:
|
|
if (!g_sci->_features->hasTransparentPicturePlanes() || _type != kPlaneTypeTransparentPicture) {
|
|
_type = kPlaneTypePicture;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Plane::sync(const Plane *other, const Common::Rect &screenRect) {
|
|
if (other == nullptr) {
|
|
if (_pictureChanged) {
|
|
deleteAllPics();
|
|
setType();
|
|
changePic();
|
|
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
|
|
} else {
|
|
setType();
|
|
}
|
|
} else {
|
|
if (
|
|
_planeRect.top != other->_planeRect.top ||
|
|
_planeRect.left != other->_planeRect.left ||
|
|
_planeRect.right > other->_planeRect.right ||
|
|
_planeRect.bottom > other->_planeRect.bottom
|
|
) {
|
|
// the plane moved or got larger
|
|
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
|
|
_moved = g_sci->_gfxFrameout->getScreenCount();
|
|
} else if (_planeRect != other->_planeRect) {
|
|
// the plane got smaller
|
|
_moved = g_sci->_gfxFrameout->getScreenCount();
|
|
}
|
|
|
|
if (_priority != other->_priority) {
|
|
_priorityChanged = g_sci->_gfxFrameout->getScreenCount();
|
|
}
|
|
|
|
if (_pictureId != other->_pictureId || _mirrored != other->_mirrored || _pictureChanged) {
|
|
deleteAllPics();
|
|
setType();
|
|
changePic();
|
|
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
|
|
}
|
|
|
|
if (_back != other->_back) {
|
|
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
|
|
}
|
|
}
|
|
|
|
_deleted = 0;
|
|
if (_created == 0) {
|
|
_updated = g_sci->_gfxFrameout->getScreenCount();
|
|
}
|
|
|
|
convertGameRectToPlaneRect();
|
|
_screenRect = _planeRect;
|
|
// NOTE: screenRect originally was retrieved through globals
|
|
// instead of being passed into the function
|
|
clipScreenRect(screenRect);
|
|
}
|
|
|
|
void Plane::update(const reg_t object) {
|
|
SegManager *segMan = g_sci->getEngineState()->_segMan;
|
|
_vanishingPoint.x = readSelectorValue(segMan, object, SELECTOR(vanishingX));
|
|
_vanishingPoint.y = readSelectorValue(segMan, object, SELECTOR(vanishingY));
|
|
|
|
if (g_sci->_features->usesAlternateSelectors()) {
|
|
_gameRect.left = readSelectorValue(segMan, object, SELECTOR(left));
|
|
_gameRect.top = readSelectorValue(segMan, object, SELECTOR(top));
|
|
_gameRect.right = readSelectorValue(segMan, object, SELECTOR(right)) + 1;
|
|
_gameRect.bottom = readSelectorValue(segMan, object, SELECTOR(bottom)) + 1;
|
|
} else {
|
|
_gameRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
|
|
_gameRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
|
|
_gameRect.right = readSelectorValue(segMan, object, SELECTOR(inRight)) + 1;
|
|
_gameRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom)) + 1;
|
|
}
|
|
convertGameRectToPlaneRect();
|
|
|
|
_priority = readSelectorValue(segMan, object, SELECTOR(priority));
|
|
GuiResourceId pictureId = readSelectorValue(segMan, object, SELECTOR(picture));
|
|
if (_pictureId != pictureId) {
|
|
_pictureId = pictureId;
|
|
_pictureChanged = true;
|
|
}
|
|
|
|
_mirrored = readSelectorValue(segMan, object, SELECTOR(mirrored));
|
|
_back = readSelectorValue(segMan, object, SELECTOR(back));
|
|
}
|
|
|
|
void Plane::scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics) {
|
|
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
|
|
|
|
for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) {
|
|
if (*it != nullptr) {
|
|
ScreenItem &screenItem = **it;
|
|
if (!screenItem._deleted && (screenItem._celInfo.type != kCelTypePic || scrollPics)) {
|
|
screenItem._position.x += deltaX;
|
|
screenItem._position.y += deltaY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Plane::remapMarkRedraw() {
|
|
ScreenItemList::size_type screenItemCount = _screenItemList.size();
|
|
for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) {
|
|
ScreenItem *screenItem = _screenItemList[i];
|
|
if (
|
|
screenItem != nullptr &&
|
|
!screenItem->_deleted && !screenItem->_created &&
|
|
screenItem->getCelObj()._remap
|
|
) {
|
|
screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark PlaneList
|
|
|
|
void PlaneList::add(Plane *plane) {
|
|
for (iterator it = begin(); it != end(); ++it) {
|
|
if ((*it)->_priority > plane->_priority) {
|
|
insert(it, plane);
|
|
return;
|
|
}
|
|
}
|
|
|
|
push_back(plane);
|
|
}
|
|
|
|
void PlaneList::clear() {
|
|
for (iterator it = begin(); it != end(); ++it) {
|
|
delete *it;
|
|
}
|
|
|
|
PlaneListBase::clear();
|
|
}
|
|
|
|
void PlaneList::erase(Plane *plane) {
|
|
for (iterator it = begin(); it != end(); ++it) {
|
|
if (*it == plane) {
|
|
erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
PlaneList::iterator PlaneList::erase(iterator it) {
|
|
delete *it;
|
|
return PlaneListBase::erase(it);
|
|
}
|
|
|
|
int PlaneList::findIndexByObject(const reg_t object) const {
|
|
for (size_type i = 0; i < size(); ++i) {
|
|
if ((*this)[i] != nullptr && (*this)[i]->_object == object) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
Plane *PlaneList::findByObject(const reg_t object) const {
|
|
const_iterator planeIt = Common::find_if(begin(), end(), FindByObject<Plane *>(object));
|
|
|
|
if (planeIt == end()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return *planeIt;
|
|
}
|
|
|
|
int16 PlaneList::getTopPlanePriority() const {
|
|
if (size() > 0) {
|
|
return (*this)[size() - 1]->_priority;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int16 PlaneList::getTopSciPlanePriority() const {
|
|
int16 priority = 0;
|
|
|
|
for (const_iterator it = begin(); it != end(); ++it) {
|
|
if ((*it)->_priority >= 10000) {
|
|
break;
|
|
}
|
|
|
|
priority = (*it)->_priority;
|
|
}
|
|
|
|
return priority;
|
|
}
|
|
|
|
void PlaneList::remove_at(size_type index) {
|
|
delete PlaneListBase::remove_at(index);
|
|
}
|
|
|
|
} // End of namespace Sci
|