scummvm/engines/twine/renderer/redraw.cpp

762 lines
26 KiB
C++
Raw Normal View History

/* 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 "twine/renderer/redraw.h"
#include "common/memstream.h"
2021-02-10 18:41:27 +01:00
#include "common/system.h"
#include "common/textconsole.h"
2021-03-07 16:49:37 +01:00
#include "common/util.h"
#include "graphics/surface.h"
#include "twine/audio/sound.h"
#include "twine/debugger/debug_scene.h"
2020-10-23 13:44:38 +02:00
#include "twine/input.h"
#include "twine/menu/interface.h"
#include "twine/menu/menu.h"
2020-12-24 15:04:57 +01:00
#include "twine/parser/sprite.h"
#include "twine/renderer/renderer.h"
#include "twine/renderer/screens.h"
2020-12-24 15:04:57 +01:00
#include "twine/resources/hqr.h"
#include "twine/resources/resources.h"
2020-12-24 15:04:57 +01:00
#include "twine/scene/actor.h"
#include "twine/scene/animations.h"
#include "twine/scene/collision.h"
#include "twine/scene/grid.h"
#include "twine/scene/movements.h"
#include "twine/scene/scene.h"
#include "twine/shared.h"
#include "twine/text.h"
namespace TwinE {
2020-12-30 14:22:45 +01:00
void Redraw::addRedrawCurrentArea(const Common::Rect &redrawArea) {
const int32 area = (redrawArea.right - redrawArea.left) * (redrawArea.bottom - redrawArea.top);
2021-07-31 15:37:40 +02:00
for (int32 i = 0; i < _numOfRedrawBox; ++i) {
2021-03-19 14:45:20 +01:00
Common::Rect &rect = _currentRedrawList[i];
2020-12-30 14:22:45 +01:00
const int32 leftValue = MIN<int32>(redrawArea.left, rect.left);
const int32 rightValue = MAX<int32>(redrawArea.right, rect.right);
const int32 topValue = MIN<int32>(redrawArea.top, rect.top);
const int32 bottomValue = MAX<int32>(redrawArea.bottom, rect.bottom);
const int32 areaValue = (rightValue - leftValue) * (bottomValue - topValue);
const int32 areaValueDiff = ((rect.right - rect.left) * (rect.bottom - rect.top) + area);
if (areaValue < areaValueDiff) {
rect.left = leftValue;
rect.top = topValue;
rect.right = rightValue;
rect.bottom = MIN<int32>((_engine->height() - 1), bottomValue);
2020-12-30 14:22:45 +01:00
assert(rect.left <= rect.right);
assert(rect.top <= rect.bottom);
2020-10-14 14:20:38 +02:00
return;
}
2020-10-24 12:32:00 +02:00
}
2020-10-14 14:20:38 +02:00
2021-07-31 15:37:40 +02:00
Common::Rect &rect = _currentRedrawList[_numOfRedrawBox];
2020-12-30 14:22:45 +01:00
rect.left = redrawArea.left;
rect.top = redrawArea.top;
rect.right = redrawArea.right;
rect.bottom = MIN<int32>((_engine->height() - 1), redrawArea.bottom);
2020-10-14 14:20:38 +02:00
2020-12-30 14:22:45 +01:00
assert(rect.left <= rect.right);
assert(rect.top <= rect.bottom);
2020-10-14 14:20:38 +02:00
2021-07-31 15:37:40 +02:00
_numOfRedrawBox++;
2020-10-14 14:20:38 +02:00
}
2020-11-25 16:59:54 +01:00
void Redraw::addRedrawArea(const Common::Rect &rect) {
2020-12-30 14:22:45 +01:00
if (!rect.isValidRect()) {
return;
}
2020-11-25 16:59:54 +01:00
addRedrawArea(rect.left, rect.top, rect.right, rect.bottom);
}
void Redraw::addRedrawArea(int32 left, int32 top, int32 right, int32 bottom) {
if (left < 0) {
left = 0;
2020-10-24 12:42:45 +02:00
}
if (top < 0) {
top = 0;
2020-10-24 12:42:45 +02:00
}
if (right >= _engine->width()) {
right = (_engine->width() - 1);
2020-10-24 12:42:45 +02:00
}
if (bottom >= _engine->height()) {
bottom = (_engine->height() - 1);
2020-10-24 12:42:45 +02:00
}
2020-10-14 14:20:38 +02:00
2020-10-24 12:42:45 +02:00
if (left > right || top > bottom) {
2020-10-14 14:20:38 +02:00
return;
2020-10-24 12:42:45 +02:00
}
2020-10-14 14:20:38 +02:00
2021-07-31 15:37:40 +02:00
Common::Rect &rect = _nextRedrawList[_currNumOfRedrawBox];
2020-12-30 14:22:45 +01:00
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
2020-10-14 14:20:38 +02:00
2021-07-31 15:37:40 +02:00
_currNumOfRedrawBox++;
2020-10-14 14:20:38 +02:00
2020-12-30 14:22:45 +01:00
addRedrawCurrentArea(rect);
2020-10-14 14:20:38 +02:00
}
void Redraw::moveNextAreas() {
2021-07-31 15:37:40 +02:00
_numOfRedrawBox = 0;
2020-10-14 14:20:38 +02:00
2021-07-31 15:37:40 +02:00
for (int32 i = 0; i < _currNumOfRedrawBox; i++) {
2021-03-19 14:45:20 +01:00
addRedrawCurrentArea(_nextRedrawList[i]);
2020-10-14 14:20:38 +02:00
}
}
void Redraw::flipRedrawAreas() {
2021-07-31 15:37:40 +02:00
for (int32 i = 0; i < _numOfRedrawBox; i++) { // redraw areas on screen
2021-07-30 17:15:06 +02:00
_engine->copyBlockPhys(_currentRedrawList[i]);
2020-10-14 14:20:38 +02:00
}
2021-07-30 17:15:06 +02:00
moveNextAreas();
2020-10-14 14:20:38 +02:00
}
void Redraw::blitBackgroundAreas() {
2021-07-31 15:37:40 +02:00
for (int32 i = 0; i < _numOfRedrawBox; i++) {
_engine->blitWorkToFront(_currentRedrawList[i]);
2020-10-14 14:20:38 +02:00
}
}
void Redraw::sortDrawingList(DrawListStruct *list, int32 listSize) const {
if (listSize == 1) {
return;
}
for (int32 i = 0; i < listSize - 1; i++) {
for (int32 j = 0; j < listSize - 1 - i; j++) {
if (list[j + 1].posValue < list[j].posValue) {
2021-03-07 16:49:37 +01:00
SWAP(list[j + 1], list[j]);
2020-10-14 14:20:38 +02:00
}
}
}
}
void Redraw::addOverlay(OverlayType type, int16 info0, int16 x, int16 y, int16 info1, OverlayPosType posType, int16 lifeTime) {
2020-10-24 12:42:45 +02:00
for (int32 i = 0; i < ARRAYSIZE(overlayList); i++) {
2020-10-14 14:20:38 +02:00
OverlayListStruct *overlay = &overlayList[i];
if (overlay->info0 == -1) {
overlay->type = type;
overlay->info0 = info0;
overlay->x = x;
overlay->y = y;
2020-10-14 14:20:38 +02:00
overlay->info1 = info1;
overlay->posType = posType;
2021-07-31 15:44:15 +02:00
overlay->lifeTime = _engine->_lbaTime + lifeTime * 50;
break;
2020-10-14 14:20:38 +02:00
}
}
}
2020-11-26 22:23:30 +01:00
void Redraw::updateOverlayTypePosition(int16 x1, int16 y1, int16 x2, int16 y2) {
const int16 newX = x2 - x1;
const int16 newY = y2 - y1;
2020-10-14 14:20:38 +02:00
2020-10-24 12:42:45 +02:00
for (int32 i = 0; i < ARRAYSIZE(overlayList); i++) {
2020-10-14 14:20:38 +02:00
OverlayListStruct *overlay = &overlayList[i];
if (overlay->posType == OverlayPosType::koFollowActor) {
overlay->x = newX;
overlay->y = newY;
2020-10-14 14:20:38 +02:00
}
}
}
2021-07-31 15:31:49 +02:00
int32 Redraw::fillActorDrawingList(DrawListStruct *drawList, bool bgRedraw) {
int32 drawListPos = 0;
2021-07-31 16:16:13 +02:00
for (int32 a = 0; a < _engine->_scene->_sceneNumActors; a++) {
2021-07-24 13:32:05 +02:00
ActorStruct *actor = _engine->_scene->getActor(a);
2021-07-31 16:47:38 +02:00
actor->_dynamicFlags.bIsVisible = 0; // reset visible state
2020-10-14 14:20:38 +02:00
2021-07-31 16:47:38 +02:00
if (_engine->_grid->_useCellingGrid != -1 && actor->_pos.y > _engine->_scene->_sceneZones[_engine->_grid->_cellingGridIdx].maxs.y) {
2020-12-13 13:08:00 +01:00
continue;
}
// no redraw required
2021-07-31 16:47:38 +02:00
if (actor->_staticFlags.bIsBackgrounded && !bgRedraw) {
2020-12-13 13:08:00 +01:00
// get actor position on screen
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(actor->pos() - _engine->_grid->_camera);
2020-12-13 13:08:00 +01:00
// check if actor is visible on screen, otherwise don't display it
if (projPos.x > -50 && projPos.x < _engine->width() + 40 && projPos.y > -30 && projPos.y < _engine->height() + 100) {
2021-07-31 16:47:38 +02:00
actor->_dynamicFlags.bIsVisible = 1;
2020-12-13 13:08:00 +01:00
}
continue;
}
// if the actor isn't set as hidden
2021-07-31 16:47:38 +02:00
if (actor->_entity == -1 || actor->_staticFlags.bIsHidden) {
2020-12-13 13:08:00 +01:00
continue;
}
// get actor position on screen
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(actor->pos() - _engine->_grid->_camera);
2020-10-14 14:20:38 +02:00
if ((actor->_staticFlags.bUsesClipping && projPos.x > -112 && projPos.x < _engine->width() + 112 && projPos.y > -50 && projPos.y < _engine->height() + 171) ||
((!actor->_staticFlags.bUsesClipping) && projPos.x > -50 && projPos.x < _engine->width() + 40 && projPos.y > -30 && projPos.y < _engine->height() + 100)) {
2020-10-14 14:20:38 +02:00
2021-07-31 16:47:38 +02:00
int32 tmpVal = actor->_pos.z + actor->_pos.x - _engine->_grid->_camera.x - _engine->_grid->_camera.z;
2020-10-14 14:20:38 +02:00
2020-12-13 13:08:00 +01:00
// if actor is above another actor
2021-07-31 16:47:38 +02:00
if (actor->_standOn != -1) {
const ActorStruct *standOnActor = _engine->_scene->getActor(actor->_standOn);
tmpVal = standOnActor->_pos.x - _engine->_grid->_camera.x + standOnActor->_pos.z - _engine->_grid->_camera.z + 2;
2020-12-13 13:08:00 +01:00
}
2020-10-14 14:20:38 +02:00
2021-07-31 16:47:38 +02:00
if (actor->_staticFlags.bIsSpriteActor) {
2021-07-31 15:31:49 +02:00
drawList[drawListPos].type = DrawListType::DrawActorSprites;
drawList[drawListPos].actorIdx = a;
2021-07-31 16:47:38 +02:00
if (actor->_staticFlags.bUsesClipping) {
tmpVal = actor->_lastPos.x - _engine->_grid->_camera.x + actor->_lastPos.z - _engine->_grid->_camera.z;
2020-12-13 13:08:00 +01:00
}
} else {
2021-07-31 15:31:49 +02:00
drawList[drawListPos].type = 0;
drawList[drawListPos].actorIdx = a;
2020-12-13 13:08:00 +01:00
}
2020-10-14 14:20:38 +02:00
2021-07-31 15:31:49 +02:00
drawList[drawListPos].posValue = tmpVal;
2020-10-14 14:20:38 +02:00
2020-12-13 13:08:00 +01:00
drawListPos++;
2020-10-14 14:20:38 +02:00
2020-12-13 13:08:00 +01:00
// if use shadows
2021-07-31 16:47:38 +02:00
if (_engine->_cfgfile.ShadowMode != 0 && !(actor->_staticFlags.bDoesntCastShadow)) {
if (actor->_standOn != -1) {
2021-07-31 16:50:19 +02:00
_engine->_actor->_shadowCoord.x = actor->_pos.x;
_engine->_actor->_shadowCoord.y = actor->_pos.y - 1;
_engine->_actor->_shadowCoord.z = actor->_pos.z;
2020-12-13 13:08:00 +01:00
} else {
2021-07-31 16:47:38 +02:00
_engine->_movements->getShadowPosition(actor->pos());
2020-10-14 14:20:38 +02:00
}
2020-12-13 13:08:00 +01:00
2021-07-31 15:31:49 +02:00
drawList[drawListPos].posValue = tmpVal - 1; // save the shadow entry in the _drawList
drawList[drawListPos].type = DrawListType::DrawShadows;
drawList[drawListPos].actorIdx = 0;
2021-07-31 16:50:19 +02:00
drawList[drawListPos].x = _engine->_actor->_shadowCoord.x;
drawList[drawListPos].y = _engine->_actor->_shadowCoord.y;
drawList[drawListPos].z = _engine->_actor->_shadowCoord.z;
drawList[drawListPos].offset = 1;
2020-12-13 13:08:00 +01:00
drawListPos++;
2020-10-14 14:20:38 +02:00
}
2021-07-31 16:16:13 +02:00
if (_inSceneryView && a == _engine->_scene->_currentlyFollowedActor) {
_sceneryViewX = projPos.x;
_sceneryViewY = projPos.y;
2021-02-10 18:37:55 +01:00
}
2020-10-14 14:20:38 +02:00
}
}
2020-12-13 14:09:31 +01:00
return drawListPos;
}
2020-10-14 14:20:38 +02:00
2021-07-31 15:31:49 +02:00
int32 Redraw::fillExtraDrawingList(DrawListStruct *drawList, int32 drawListPos) {
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
2021-07-31 16:47:38 +02:00
ExtraListStruct *extra = &_engine->_extra->_extraList[i];
2020-12-13 13:08:00 +01:00
if (extra->info0 == -1) {
continue;
}
if (extra->type & ExtraType::TIME_IN) {
2021-07-31 15:44:15 +02:00
if (_engine->_lbaTime - extra->spawnTime > 35) {
extra->spawnTime = _engine->_lbaTime;
2020-12-13 13:08:00 +01:00
extra->type &= ~ExtraType::TIME_IN;
_engine->_sound->playSample(Samples::ItemPopup, 1, extra->pos);
2020-12-13 13:08:00 +01:00
}
continue;
}
2021-07-31 15:44:15 +02:00
if ((extra->type & ExtraType::TIME_OUT) || (extra->type & ExtraType::FLASH) || (extra->payload.lifeTime + extra->spawnTime - 150 < _engine->_lbaTime) || (!((_engine->_lbaTime + extra->spawnTime) & 8))) {
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(extra->pos - _engine->_grid->_camera);
2020-12-13 13:08:00 +01:00
if (projPos.x > -50 && projPos.x < _engine->width() + 40 && projPos.y > -30 && projPos.y < _engine->height() + 100) {
2021-07-31 16:16:13 +02:00
const int16 tmpVal = extra->pos.x - _engine->_grid->_camera.x + extra->pos.z - _engine->_grid->_camera.z;
2021-07-31 15:31:49 +02:00
drawList[drawListPos].posValue = tmpVal;
drawList[drawListPos].actorIdx = i;
drawList[drawListPos].type = DrawListType::DrawExtras;
2020-12-13 13:08:00 +01:00
drawListPos++;
2021-07-31 15:44:15 +02:00
if (_engine->_cfgfile.ShadowMode == 2 && !(extra->info0 & EXTRA_SPECIAL_MASK)) {
2021-07-24 14:10:37 +02:00
_engine->_movements->getShadowPosition(extra->pos);
2020-12-13 13:08:00 +01:00
2021-07-31 15:31:49 +02:00
drawList[drawListPos].posValue = tmpVal - 1;
drawList[drawListPos].actorIdx = 0;
drawList[drawListPos].type = DrawListType::DrawShadows;
2021-07-31 16:50:19 +02:00
drawList[drawListPos].x = _engine->_actor->_shadowCoord.x;
drawList[drawListPos].y = _engine->_actor->_shadowCoord.y;
drawList[drawListPos].z = _engine->_actor->_shadowCoord.z;
2021-07-31 15:31:49 +02:00
drawList[drawListPos].offset = 0;
2020-12-13 13:08:00 +01:00
drawListPos++;
2020-10-14 14:20:38 +02:00
}
}
}
}
2020-12-13 14:09:31 +01:00
return drawListPos;
}
2020-10-14 14:20:38 +02:00
void Redraw::processDrawListShadows(const DrawListStruct &drawCmd) {
2020-12-13 22:51:09 +01:00
// get actor position on screen
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(drawCmd.x - _engine->_grid->_camera.x, drawCmd.y - _engine->_grid->_camera.y, drawCmd.z - _engine->_grid->_camera.z);
2020-10-14 14:20:38 +02:00
int32 spriteWidth = _engine->_resources->_spriteShadowPtr.surface(drawCmd.offset).w;
int32 spriteHeight = _engine->_resources->_spriteShadowPtr.surface(drawCmd.offset).h;
2020-10-14 14:20:38 +02:00
2020-12-13 22:51:09 +01:00
// calculate sprite size and position on screen
Common::Rect renderRect;
renderRect.left = projPos.x - (spriteWidth / 2);
renderRect.top = projPos.y - (spriteHeight / 2);
renderRect.right = projPos.x + (spriteWidth / 2);
renderRect.bottom = projPos.y + (spriteHeight / 2);
2020-10-14 14:20:38 +02:00
2020-12-13 22:51:09 +01:00
_engine->_interface->setClip(renderRect);
2020-10-14 14:20:38 +02:00
_engine->_grid->drawSprite(renderRect.left, renderRect.top, _engine->_resources->_spriteShadowPtr, drawCmd.offset);
2020-10-14 14:20:38 +02:00
2020-12-25 13:08:38 +01:00
const int32 tmpX = (drawCmd.x + BRICK_HEIGHT) / BRICK_SIZE;
const int32 tmpY = drawCmd.y / BRICK_HEIGHT;
const int32 tmpZ = (drawCmd.z + BRICK_HEIGHT) / BRICK_SIZE;
2020-10-14 14:20:38 +02:00
2020-12-13 22:51:09 +01:00
_engine->_grid->drawOverModelActor(tmpX, tmpY, tmpZ);
2020-12-13 14:09:31 +01:00
2021-07-31 15:44:15 +02:00
addRedrawArea(_engine->_interface->_clip);
2020-12-13 14:09:31 +01:00
2021-06-03 19:35:27 +02:00
_engine->_debugScene->drawClip(renderRect);
2020-12-13 22:51:09 +01:00
}
2020-12-13 14:09:31 +01:00
void Redraw::processDrawListActors(const DrawListStruct &drawCmd, bool bgRedraw) {
2020-12-22 11:24:57 +01:00
const int32 actorIdx = drawCmd.actorIdx;
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
2021-07-31 16:47:38 +02:00
if (actor->_previousAnimIdx >= 0) {
const AnimData &animData = _engine->_resources->_animData[actor->_previousAnimIdx];
_engine->_animations->setModelAnimation(actor->_animPosition, animData, _engine->_resources->_bodyData[actor->_entity], &actor->_animTimerData);
}
2020-12-22 11:24:57 +01:00
2021-07-31 16:47:38 +02:00
const IVec3 &delta = actor->pos() - _engine->_grid->_camera;
Common::Rect renderRect;
2021-07-31 16:47:38 +02:00
if (!_engine->_renderer->renderIsoModel(delta.x, delta.y, delta.z, ANGLE_0, actor->_angle, ANGLE_0, _engine->_resources->_bodyData[actor->_entity], renderRect)) {
return;
}
2020-10-14 14:20:38 +02:00
if (_engine->_interface->setClip(renderRect)) {
2021-07-31 16:47:38 +02:00
actor->_dynamicFlags.bIsVisible = 1;
2020-12-13 14:09:31 +01:00
2021-07-31 16:47:38 +02:00
const int32 tempX = (actor->_pos.x + BRICK_HEIGHT) / BRICK_SIZE;
int32 tempY = actor->_pos.y / BRICK_HEIGHT;
const int32 tempZ = (actor->_pos.z + BRICK_HEIGHT) / BRICK_SIZE;
2020-12-22 11:24:57 +01:00
if (actor->brickShape() != ShapeType::kNone) {
tempY++;
}
2020-12-13 14:09:31 +01:00
_engine->_grid->drawOverModelActor(tempX, tempY, tempZ);
2020-10-14 14:20:38 +02:00
2021-07-31 16:50:19 +02:00
if (_engine->_actor->_cropBottomScreen) {
_engine->_interface->_clip.bottom = _engine->_actor->_cropBottomScreen + 10;
}
2020-12-13 22:51:09 +01:00
2021-07-31 15:44:15 +02:00
addRedrawArea(_engine->_interface->_clip);
2021-07-31 16:47:38 +02:00
if (actor->_staticFlags.bIsBackgrounded && bgRedraw) {
2021-07-31 15:44:15 +02:00
_engine->blitFrontToWork(_engine->_interface->_clip);
2020-12-13 22:51:09 +01:00
}
2020-12-22 11:25:07 +01:00
2021-07-31 15:44:15 +02:00
_engine->_debugScene->drawClip(_engine->_interface->_clip);
2020-12-13 22:51:09 +01:00
}
}
2020-10-14 14:20:38 +02:00
void Redraw::processDrawListActorSprites(const DrawListStruct &drawCmd, bool bgRedraw) {
2020-12-13 22:51:09 +01:00
int32 actorIdx = drawCmd.actorIdx;
2020-12-22 11:24:57 +01:00
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
2021-07-31 16:47:38 +02:00
const SpriteData &spriteData = _engine->_resources->_spriteData[actor->_entity];
// TODO: using the raw pointer and not the SpriteData surface here is a workaround for issue https://bugs.scummvm.org/ticket/12024
2021-07-31 16:47:38 +02:00
const uint8 *spritePtr = _engine->_resources->_spriteTable[actor->_entity];
2020-12-13 22:51:09 +01:00
// get actor position on screen
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(actor->pos() - _engine->_grid->_camera);
2020-12-13 22:51:09 +01:00
const int32 spriteWidth = spriteData.surface().w;
const int32 spriteHeight = spriteData.surface().h;
2020-12-13 22:51:09 +01:00
// calculate sprite position on screen
2021-07-31 16:47:38 +02:00
const SpriteDim *dim = _engine->_resources->_spriteBoundingBox.dim(actor->_entity);
Common::Rect renderRect;
renderRect.left = projPos.x + dim->x;
renderRect.top = projPos.y + dim->y;
2020-12-13 22:51:09 +01:00
renderRect.right = renderRect.left + spriteWidth;
renderRect.bottom = renderRect.top + spriteHeight;
2021-07-31 16:47:38 +02:00
if (actor->_staticFlags.bUsesClipping) {
const Common::Rect rect(_projPosScreen.x + actor->_cropLeft, _projPosScreen.y + actor->_cropTop, _projPosScreen.x + actor->_cropRight, _projPosScreen.y + actor->_cropBottom);
2020-12-13 22:51:09 +01:00
_engine->_interface->setClip(rect);
} else {
_engine->_interface->setClip(renderRect);
}
2020-12-13 14:09:31 +01:00
2021-07-31 15:44:15 +02:00
if (_engine->_interface->_clip.isValidRect()) {
_engine->_grid->drawSprite(0, renderRect.left, renderRect.top, spritePtr);
2020-12-13 14:09:31 +01:00
2021-07-31 16:47:38 +02:00
actor->_dynamicFlags.bIsVisible = 1;
2020-12-13 14:09:31 +01:00
2021-07-31 16:47:38 +02:00
if (actor->_staticFlags.bUsesClipping) {
const int32 tmpX = (actor->_lastPos.x + BRICK_HEIGHT) / BRICK_SIZE;
const int32 tmpY = actor->_lastPos.y / BRICK_HEIGHT;
const int32 tmpZ = (actor->_lastPos.z + BRICK_HEIGHT) / BRICK_SIZE;
2020-12-13 22:51:09 +01:00
_engine->_grid->drawOverSpriteActor(tmpX, tmpY, tmpZ);
} else {
2021-07-31 16:47:38 +02:00
const int32 tmpX = (actor->_pos.x + actor->_boudingBox.maxs.x + BRICK_HEIGHT) / BRICK_SIZE;
int32 tmpY = actor->_pos.y / BRICK_HEIGHT;
const int32 tmpZ = (actor->_pos.z + actor->_boudingBox.maxs.z + BRICK_HEIGHT) / BRICK_SIZE;
2020-12-22 11:24:57 +01:00
if (actor->brickShape() != ShapeType::kNone) {
2020-12-13 22:51:09 +01:00
tmpY++;
2020-12-13 14:09:31 +01:00
}
2020-10-14 14:20:38 +02:00
2020-12-13 22:51:09 +01:00
_engine->_grid->drawOverSpriteActor(tmpX, tmpY, tmpZ);
}
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
addRedrawArea(_engine->_interface->_clip);
2020-10-14 14:20:38 +02:00
2021-07-31 16:47:38 +02:00
if (actor->_staticFlags.bIsBackgrounded && bgRedraw) {
2021-07-31 15:44:15 +02:00
_engine->blitFrontToWork(_engine->_interface->_clip);
2020-12-13 22:51:09 +01:00
}
2020-12-13 14:09:31 +01:00
2021-06-03 19:35:27 +02:00
_engine->_debugScene->drawClip(renderRect);
2020-12-13 22:51:09 +01:00
}
}
2020-12-13 14:09:31 +01:00
void Redraw::processDrawListExtras(const DrawListStruct &drawCmd) {
2021-03-23 18:44:18 +01:00
int32 extraIdx = drawCmd.actorIdx;
2021-07-31 16:47:38 +02:00
ExtraListStruct *extra = &_engine->_extra->_extraList[extraIdx];
2020-10-14 14:20:38 +02:00
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(extra->pos - _engine->_grid->_camera);
2020-10-14 14:20:38 +02:00
Common::Rect renderRect;
if (extra->info0 & EXTRA_SPECIAL_MASK) {
_engine->_extra->drawExtraSpecial(extraIdx, projPos.x, projPos.y, renderRect);
2020-12-13 22:51:09 +01:00
} else {
2021-07-31 15:44:15 +02:00
const SpriteData &spritePtr = _engine->_resources->_spriteData[extra->info0];
2020-12-24 00:09:40 +01:00
const int32 spriteWidth = spritePtr.surface().w;
const int32 spriteHeight = spritePtr.surface().h;
2020-10-14 14:20:38 +02:00
2020-12-13 22:51:09 +01:00
// calculate sprite position on screen
2021-07-31 15:44:15 +02:00
const SpriteDim *dim = _engine->_resources->_spriteBoundingBox.dim(extra->info0);
renderRect.left = projPos.x + dim->x;
renderRect.top = projPos.y + dim->y;
2020-12-13 22:51:09 +01:00
renderRect.right = renderRect.left + spriteWidth;
renderRect.bottom = renderRect.top + spriteHeight;
2020-12-24 00:11:17 +01:00
_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
2020-12-13 22:51:09 +01:00
}
2020-10-14 14:20:38 +02:00
2020-12-13 22:51:09 +01:00
_engine->_interface->setClip(renderRect);
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
if (_engine->_interface->_clip.isValidRect()) {
2020-12-25 13:08:38 +01:00
const int32 tmpX = (drawCmd.x + BRICK_HEIGHT) / BRICK_SIZE;
const int32 tmpY = drawCmd.y / BRICK_HEIGHT;
const int32 tmpZ = (drawCmd.z + BRICK_HEIGHT) / BRICK_SIZE;
2020-10-14 14:20:38 +02:00
2020-12-13 22:51:09 +01:00
_engine->_grid->drawOverModelActor(tmpX, tmpY, tmpZ);
2021-07-31 15:44:15 +02:00
addRedrawArea(_engine->_interface->_clip);
2020-10-14 14:20:38 +02:00
2020-12-13 22:51:09 +01:00
// show clipping area
2021-07-31 21:08:09 +02:00
//drawRectBorders(renderRect);
2020-12-13 22:51:09 +01:00
}
}
2021-07-31 15:31:49 +02:00
void Redraw::processDrawList(DrawListStruct *drawList, int32 drawListPos, bool bgRedraw) {
2020-12-13 22:51:09 +01:00
for (int32 pos = 0; pos < drawListPos; ++pos) {
2021-07-31 15:31:49 +02:00
const DrawListStruct &drawCmd = drawList[pos];
2020-12-13 22:51:09 +01:00
const uint32 flags = drawCmd.type;
// Drawing actors
if (flags < DrawListType::DrawShadows) {
if (flags == 0) {
processDrawListActors(drawCmd, bgRedraw);
2020-10-14 14:20:38 +02:00
}
2020-12-13 14:09:31 +01:00
}
2020-12-13 22:51:09 +01:00
// Drawing shadows
2021-07-31 16:50:19 +02:00
else if (flags == DrawListType::DrawShadows && !_engine->_actor->_cropBottomScreen) {
2020-12-13 22:51:09 +01:00
processDrawListShadows(drawCmd);
}
// Drawing unknown
else if (flags < DrawListType::DrawActorSprites) {
// TODO reverse this part of the code
warning("Not yet reversed part of the rendering code");
}
// Drawing sprite actors, doors and entities
else if (flags == DrawListType::DrawActorSprites) {
processDrawListActorSprites(drawCmd, bgRedraw);
}
// Drawing extras
else if (flags == DrawListType::DrawExtras) {
processDrawListExtras(drawCmd);
}
2020-10-14 14:20:38 +02:00
2020-12-13 14:09:31 +01:00
_engine->_interface->resetClip();
2020-12-13 22:51:09 +01:00
}
2020-12-13 14:09:31 +01:00
}
2020-10-14 14:20:38 +02:00
2020-12-13 15:27:43 +01:00
void Redraw::renderOverlays() {
for (int32 i = 0; i < OVERLAY_MAX_ENTRIES; i++) {
2020-10-14 14:20:38 +02:00
OverlayListStruct *overlay = &overlayList[i];
if (overlay->info0 != -1) {
// process position overlay
switch (overlay->posType) {
case OverlayPosType::koNormal:
2021-07-31 15:44:15 +02:00
if (_engine->_lbaTime >= overlay->lifeTime) {
2020-10-14 14:20:38 +02:00
overlay->info0 = -1;
continue;
}
break;
case OverlayPosType::koFollowActor: {
ActorStruct *actor2 = _engine->_scene->getActor(overlay->info1);
2020-10-14 14:20:38 +02:00
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(actor2->_pos.x - _engine->_grid->_camera.x, actor2->_pos.y + actor2->_boudingBox.maxs.y - _engine->_grid->_camera.y, actor2->_pos.z - _engine->_grid->_camera.z);
2020-10-14 14:20:38 +02:00
overlay->x = projPos.x;
overlay->y = projPos.y;
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
if (_engine->_lbaTime >= overlay->lifeTime) {
2020-10-14 14:20:38 +02:00
overlay->info0 = -1;
continue;
}
break;
}
2020-10-14 14:20:38 +02:00
}
// process overlay type
switch (overlay->type) {
case OverlayType::koSprite: {
2021-07-31 15:44:15 +02:00
const SpriteData &spritePtr = _engine->_resources->_spriteData[overlay->info0];
2020-12-24 00:09:40 +01:00
const int32 spriteWidth = spritePtr.surface().w;
const int32 spriteHeight = spritePtr.surface().h;
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
const SpriteDim *dim = _engine->_resources->_spriteBoundingBox.dim(overlay->info0);
Common::Rect renderRect;
renderRect.left = dim->x + overlay->x;
renderRect.top = dim->y + overlay->y;
2020-11-25 18:20:38 +01:00
renderRect.right = renderRect.left + spriteWidth;
renderRect.bottom = renderRect.top + spriteHeight;
2020-10-14 14:20:38 +02:00
2020-12-24 00:11:17 +01:00
_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
addRedrawArea(_engine->_interface->_clip);
break;
}
case OverlayType::koNumber: {
char text[10];
snprintf(text, sizeof(text), "%d", overlay->info0);
2020-10-14 14:20:38 +02:00
2020-11-02 23:17:45 +01:00
const int32 textLength = _engine->_text->getTextSize(text);
const int32 textHeight = 48;
2020-10-14 14:20:38 +02:00
Common::Rect renderRect;
2020-11-25 18:20:38 +01:00
renderRect.left = overlay->x - (textLength / 2);
renderRect.top = overlay->y - 24;
renderRect.right = overlay->x + (textLength / 2);
renderRect.bottom = overlay->y + textHeight;
2020-10-14 14:20:38 +02:00
2020-11-25 18:20:38 +01:00
_engine->_interface->setClip(renderRect);
2020-10-14 14:20:38 +02:00
_engine->_text->setFontColor(overlay->info1);
2020-10-14 14:20:38 +02:00
2020-11-25 18:20:38 +01:00
_engine->_text->drawText(renderRect.left, renderRect.top, text);
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
addRedrawArea(_engine->_interface->_clip);
break;
}
case OverlayType::koNumberRange: {
2021-07-31 15:44:15 +02:00
const int32 range = _engine->_collision->getAverageValue(overlay->info1, overlay->info0, 100, overlay->lifeTime - _engine->_lbaTime - 50);
2020-10-14 14:20:38 +02:00
2020-11-02 23:17:45 +01:00
char text[10];
2020-10-14 14:20:38 +02:00
sprintf(text, "%d", range);
2020-11-02 23:17:45 +01:00
const int32 textLength = _engine->_text->getTextSize(text);
const int32 textHeight = 48;
2020-10-14 14:20:38 +02:00
Common::Rect renderRect;
2020-11-25 18:20:38 +01:00
renderRect.left = overlay->x - (textLength / 2);
renderRect.top = overlay->y - 24;
renderRect.right = overlay->x + (textLength / 2);
renderRect.bottom = overlay->y + textHeight;
2020-10-14 14:20:38 +02:00
2020-11-25 18:20:38 +01:00
_engine->_interface->setClip(renderRect);
2020-10-14 14:20:38 +02:00
2021-01-08 19:44:19 +01:00
_engine->_text->setFontColor(COLOR_GOLD);
2020-10-14 14:20:38 +02:00
2020-11-25 18:20:38 +01:00
_engine->_text->drawText(renderRect.left, renderRect.top, text);
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
addRedrawArea(_engine->_interface->_clip);
break;
}
case OverlayType::koInventoryItem: {
2020-11-02 23:17:45 +01:00
const int32 item = overlay->info0;
2020-11-25 16:59:54 +01:00
const Common::Rect rect(10, 10, 69, 69);
2020-10-14 14:20:38 +02:00
2021-01-30 14:35:12 +01:00
_engine->_interface->drawFilledRect(rect, COLOR_BLACK);
2020-11-25 16:59:54 +01:00
_engine->_interface->setClip(rect);
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
const BodyData &bodyPtr = _engine->_resources->_inventoryTable[item];
2021-03-19 14:45:20 +01:00
_overlayRotation += 1; // overlayRotation += 8;
_engine->_renderer->renderInventoryItem(40, 40, bodyPtr, _overlayRotation, 16000);
2021-07-31 21:08:09 +02:00
_engine->_menu->drawRectBorders(rect);
2020-11-25 16:59:54 +01:00
addRedrawArea(rect);
_engine->_gameState->initEngineProjections();
break;
}
case OverlayType::koText: {
char text[256];
_engine->_text->getMenuText((TextId)overlay->info0, text, sizeof(text));
2020-10-14 14:20:38 +02:00
2020-11-02 23:17:45 +01:00
const int32 textLength = _engine->_text->getTextSize(text);
const int32 textHeight = 48;
2020-10-14 14:20:38 +02:00
Common::Rect renderRect;
2020-11-25 18:20:38 +01:00
renderRect.left = overlay->x - (textLength / 2);
renderRect.top = overlay->y - (textHeight / 2);
2020-11-25 18:20:38 +01:00
renderRect.right = overlay->x + (textLength / 2);
renderRect.bottom = overlay->y + textHeight;
2020-10-14 14:20:38 +02:00
renderRect.clip(_engine->rect());
2020-10-14 14:20:38 +02:00
2020-11-25 18:20:38 +01:00
_engine->_interface->setClip(renderRect);
2020-10-14 14:20:38 +02:00
2021-07-31 16:47:38 +02:00
_engine->_text->setFontColor(_engine->_scene->getActor(overlay->info1)->_talkColor);
2020-10-14 14:20:38 +02:00
2020-11-25 18:20:38 +01:00
_engine->_text->drawText(renderRect.left, renderRect.top, text);
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
addRedrawArea(_engine->_interface->_clip);
break;
}
2020-10-14 14:20:38 +02:00
}
}
}
2020-12-13 15:27:43 +01:00
}
void Redraw::redrawEngineActions(bool bgRedraw) {
int32 tmp_projPosX = _projPosScreen.x;
int32 tmp_projPosY = _projPosScreen.y;
2020-12-13 15:27:43 +01:00
_engine->_interface->resetClip();
if (bgRedraw) {
_engine->freezeTime();
2021-07-31 16:16:13 +02:00
if (_engine->_scene->_needChangeScene != SCENE_CEILING_GRID_FADE_1 && _engine->_scene->_needChangeScene != SCENE_CEILING_GRID_FADE_2) {
2021-07-31 15:37:40 +02:00
_engine->_screens->fadeOut(_engine->_screens->_paletteRGBA);
2020-12-13 15:27:43 +01:00
}
_engine->_screens->clearScreen();
2020-12-13 15:27:43 +01:00
_engine->_grid->redrawGrid();
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(-_engine->_grid->_camera);
_projPosScreen.x = projPos.x;
_projPosScreen.y = projPos.y;
updateOverlayTypePosition(tmp_projPosX, tmp_projPosY, _projPosScreen.x, _projPosScreen.y);
_engine->saveFrontBuffer();
2020-12-13 15:27:43 +01:00
2021-07-31 16:16:13 +02:00
if (_engine->_scene->_needChangeScene != SCENE_CEILING_GRID_FADE_1 && _engine->_scene->_needChangeScene != SCENE_CEILING_GRID_FADE_2) {
2021-07-31 15:37:40 +02:00
_engine->_screens->fadeIn(_engine->_screens->_paletteRGBA);
2020-12-13 15:27:43 +01:00
}
} else {
blitBackgroundAreas();
}
static DrawListStruct drawList[150];
2021-07-31 15:31:49 +02:00
int32 drawListPos = fillActorDrawingList(drawList, bgRedraw);
drawListPos = fillExtraDrawingList(drawList, drawListPos);
sortDrawingList(drawList, drawListPos);
2020-12-13 15:27:43 +01:00
2021-07-31 15:37:40 +02:00
_currNumOfRedrawBox = 0;
2021-07-31 15:31:49 +02:00
processDrawList(drawList, drawListPos, bgRedraw);
2020-12-13 15:27:43 +01:00
2021-07-31 15:44:15 +02:00
if (_engine->_cfgfile.Debug) {
_engine->_debugScene->renderDebugView();
2020-12-13 15:27:43 +01:00
}
renderOverlays();
2020-10-14 14:20:38 +02:00
_engine->_interface->resetClip();
2020-10-14 14:20:38 +02:00
2021-07-24 13:52:54 +02:00
// make ceiling grid fade
2020-10-14 14:20:38 +02:00
// need to be here to fade after drawing all actors in scene
2021-07-31 16:16:13 +02:00
if (_engine->_scene->_needChangeScene == SCENE_CEILING_GRID_FADE_2) {
2021-07-31 15:37:40 +02:00
_engine->crossFade(_engine->_screens->_paletteRGBA);
2021-07-31 16:16:13 +02:00
_engine->_scene->_needChangeScene = SCENE_CEILING_GRID_FADE_1;
2020-10-14 14:20:38 +02:00
}
if (bgRedraw) {
moveNextAreas();
_engine->unfreezeTime();
2020-10-14 14:20:38 +02:00
} else {
flipRedrawAreas();
}
2021-07-31 15:37:40 +02:00
if (_engine->_screens->_lockPalette) {
if (_engine->_screens->_useAlternatePalette) {
_engine->_screens->fadeToPal(_engine->_screens->_paletteRGBA);
2020-10-14 14:20:38 +02:00
} else {
2021-07-31 15:37:40 +02:00
_engine->_screens->fadeToPal(_engine->_screens->_mainPaletteRGBA);
2020-10-14 14:20:38 +02:00
}
2021-07-31 15:37:40 +02:00
_engine->_screens->_lockPalette = false;
2020-10-14 14:20:38 +02:00
}
2021-07-31 15:37:40 +02:00
if (_inSceneryView) {
zoomScreenScale();
}
2020-10-14 14:20:38 +02:00
}
void Redraw::drawBubble(int32 actorIdx) {
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
2020-10-14 14:20:38 +02:00
// get actor position on screen
const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(actor->_pos.x - _engine->_grid->_camera.x, actor->_pos.y + actor->_boudingBox.maxs.y - _engine->_grid->_camera.y, actor->_pos.z - _engine->_grid->_camera.z);
2020-10-14 14:20:38 +02:00
if (actorIdx != _bubbleActor) {
_bubbleSpriteIndex = _bubbleSpriteIndex ^ 1;
_bubbleActor = actorIdx;
}
2020-10-14 14:20:38 +02:00
2021-07-31 15:44:15 +02:00
const SpriteData &spritePtr = _engine->_resources->_spriteData[_bubbleSpriteIndex];
2020-12-24 00:09:40 +01:00
const int32 spriteWidth = spritePtr.surface().w;
const int32 spriteHeight = spritePtr.surface().h;
2020-10-14 14:20:38 +02:00
// calculate sprite position on screen
Common::Rect renderRect;
if (_bubbleSpriteIndex == SPRITEHQR_DIAG_BUBBLE_RIGHT) {
renderRect.left = projPos.x + 10;
} else {
renderRect.left = projPos.x - 10 - spriteWidth;
}
renderRect.top = projPos.y - 20;
2020-11-25 18:20:38 +01:00
renderRect.right = spriteWidth + renderRect.left - 1;
renderRect.bottom = spriteHeight + renderRect.top - 1;
2020-10-14 14:20:38 +02:00
2020-11-25 18:20:38 +01:00
_engine->_interface->setClip(renderRect);
2021-06-03 18:43:07 +02:00
_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
_engine->_interface->resetClip();
2020-10-14 14:20:38 +02:00
}
void Redraw::zoomScreenScale() {
2021-07-31 15:44:15 +02:00
Graphics::ManagedSurface zoomWorkVideoBuffer(_engine->_workVideoBuffer);
2021-02-10 18:41:27 +01:00
const int maxW = zoomWorkVideoBuffer.w;
const int maxH = zoomWorkVideoBuffer.h;
const int left = CLIP<int>(_sceneryViewX - maxW / 4, 0, maxW / 2);
const int top = CLIP<int>(_sceneryViewY - maxH / 4, 0, maxH / 2);
const Common::Rect srcRect(left, top, left + maxW / 2, top + maxH / 2);
const Common::Rect& destRect = zoomWorkVideoBuffer.getBounds();
2021-07-31 15:44:15 +02:00
zoomWorkVideoBuffer.transBlitFrom(_engine->_frontVideoBuffer, srcRect, destRect);
2021-02-10 18:41:27 +01:00
g_system->copyRectToScreen(zoomWorkVideoBuffer.getPixels(), zoomWorkVideoBuffer.pitch, 0, 0, zoomWorkVideoBuffer.w, zoomWorkVideoBuffer.h);
g_system->updateScreen();
2020-10-14 14:20:38 +02:00
}
} // namespace TwinE