Initial commit
This commit is contained in:
979
engines/twine/renderer/redraw.cpp
Normal file
979
engines/twine/renderer/redraw.cpp
Normal file
@@ -0,0 +1,979 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/renderer/redraw.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/debugger/debug_state.h"
|
||||
#include "twine/input.h"
|
||||
#include "twine/menu/interface.h"
|
||||
#include "twine/menu/menu.h"
|
||||
#include "twine/parser/body.h"
|
||||
#include "twine/parser/sprite.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/renderer/screens.h"
|
||||
#include "twine/resources/hqr.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/scene/animations.h"
|
||||
#include "twine/scene/collision.h"
|
||||
#include "twine/scene/extra.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 {
|
||||
|
||||
Redraw::Redraw(TwinEEngine *engine) : _engine(engine), _bubbleSpriteIndex(SPRITEHQR_DIAG_BUBBLE_LEFT) {}
|
||||
|
||||
void Redraw::addRedrawCurrentArea(const Common::Rect &redrawArea) {
|
||||
const int32 area = (redrawArea.right - redrawArea.left) * (redrawArea.bottom - redrawArea.top);
|
||||
|
||||
for (int32 i = 0; i < _nbOptPhysBox; ++i) {
|
||||
Common::Rect &rect = _currentRedrawList[i];
|
||||
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);
|
||||
|
||||
assert(rect.left <= rect.right);
|
||||
assert(rect.top <= rect.bottom);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect &rect = _currentRedrawList[_nbOptPhysBox];
|
||||
rect.left = redrawArea.left;
|
||||
rect.top = redrawArea.top;
|
||||
rect.right = redrawArea.right;
|
||||
rect.bottom = MIN<int32>((_engine->height() - 1), redrawArea.bottom);
|
||||
|
||||
assert(rect.left <= rect.right);
|
||||
assert(rect.top <= rect.bottom);
|
||||
|
||||
_nbOptPhysBox++;
|
||||
}
|
||||
|
||||
void Redraw::addPhysBox(const Common::Rect &rect) {
|
||||
if (!rect.isValidRect()) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
}
|
||||
if (right >= _engine->width()) {
|
||||
right = (_engine->width() - 1);
|
||||
}
|
||||
if (bottom >= _engine->height()) {
|
||||
bottom = (_engine->height() - 1);
|
||||
}
|
||||
|
||||
if (left > right || top > bottom) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Rect &rect = _nextRedrawList[_nbPhysBox];
|
||||
rect.left = left;
|
||||
rect.top = top;
|
||||
rect.right = right;
|
||||
rect.bottom = bottom;
|
||||
|
||||
_nbPhysBox++;
|
||||
|
||||
addRedrawCurrentArea(rect);
|
||||
}
|
||||
|
||||
void Redraw::moveNextAreas() {
|
||||
_nbOptPhysBox = 0;
|
||||
|
||||
for (int32 i = 0; i < _nbPhysBox; i++) {
|
||||
addRedrawCurrentArea(_nextRedrawList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::flipBoxes() {
|
||||
for (int32 i = 0; i < _nbOptPhysBox; i++) { // redraw areas on screen
|
||||
_engine->copyBlockPhys(_currentRedrawList[i]);
|
||||
}
|
||||
|
||||
moveNextAreas();
|
||||
}
|
||||
|
||||
void Redraw::clsBoxes() {
|
||||
for (int32 i = 0; i < _nbOptPhysBox; i++) {
|
||||
_engine->blitWorkToFront(_currentRedrawList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::sortDrawingList(DrawListStruct *list, int32 listSize) const { // SmallSort
|
||||
DrawListStruct* pNext;
|
||||
DrawListStruct* pSmallest;
|
||||
int32 n;
|
||||
|
||||
for (listSize--; listSize > 0; listSize--) {
|
||||
pSmallest = list;
|
||||
pNext = list + 1;
|
||||
|
||||
for (n = listSize; n > 0; n--) {
|
||||
if (pNext->z < pSmallest->z) {
|
||||
pSmallest = pNext;
|
||||
}
|
||||
pNext++;
|
||||
}
|
||||
|
||||
if (pSmallest != list) {
|
||||
DrawListStruct tmp = *pSmallest;
|
||||
*pSmallest = *list;
|
||||
*list = tmp;
|
||||
}
|
||||
|
||||
list++;
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::posObjIncrust(OverlayListStruct *ptrdisp, int32 num) {
|
||||
// in case we have several 3D objects rotating at the same time!
|
||||
int32 x = 10;
|
||||
OverlayType type = ptrdisp->type;
|
||||
|
||||
if (type == OverlayType::koInventory || type == OverlayType::koInventoryItem) {
|
||||
for (int32 n = 0; n < ARRAYSIZE(overlayList); n++) {
|
||||
OverlayListStruct *overlay = &overlayList[n];
|
||||
if (n != num && overlay->num != -1) {
|
||||
if (overlay->type == OverlayType::koInventory || overlay->type == OverlayType::koInventoryItem) {
|
||||
x += 70;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptrdisp->y = 10;
|
||||
ptrdisp->x = (int16)x;
|
||||
}
|
||||
}
|
||||
|
||||
int32 Redraw::addOverlay(OverlayType type, int16 info0, int16 x, int16 y, int16 info1, OverlayPosType posType, int16 lifeTime) { // InitIncrustDisp
|
||||
for (int32 i = 0; i < ARRAYSIZE(overlayList); i++) {
|
||||
OverlayListStruct *overlay = &overlayList[i];
|
||||
if (_engine->isLBA1()) {
|
||||
if (overlay->num == -1) {
|
||||
overlay->type = type;
|
||||
overlay->num = info0;
|
||||
overlay->x = x;
|
||||
overlay->y = y;
|
||||
overlay->info = info1;
|
||||
overlay->move = posType;
|
||||
overlay->timerEnd = _engine->timerRef + _engine->toSeconds(lifeTime);
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
if (overlay->num == -1 || (overlay->num == info0 && overlay->type == type)) {
|
||||
if (overlay->num == -1 || overlay->type != type) {
|
||||
overlay->x = x;
|
||||
overlay->y = y;
|
||||
}
|
||||
if ((OverlayType)((uint8)type) == OverlayType::koNumberRange) {
|
||||
// ATTENTION: Big Trickery: counters are always displayed
|
||||
// at y=20, this allows using the Y to store the
|
||||
// current value of the counter (see FlagAnimWhoSpeak)
|
||||
overlay->y = info0;
|
||||
}
|
||||
overlay->type = type;
|
||||
overlay->num = info0;
|
||||
overlay->info = info1;
|
||||
overlay->move = posType;
|
||||
overlay->timerEnd = _engine->timerRef + _engine->toSeconds(lifeTime);
|
||||
posObjIncrust(overlay, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Redraw::updateOverlayTypePosition(int16 x1, int16 y1, int16 x2, int16 y2) {
|
||||
const int16 newX = x2 - x1;
|
||||
const int16 newY = y2 - y1;
|
||||
|
||||
for (int32 i = 0; i < ARRAYSIZE(overlayList); i++) {
|
||||
OverlayListStruct *overlay = &overlayList[i];
|
||||
if (overlay->move == OverlayPosType::koFollowActor) {
|
||||
overlay->x = newX;
|
||||
overlay->y = newY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 Redraw::fillActorDrawingList(DrawListStruct *drawList, bool flagflip) {
|
||||
int32 drawListPos = 0;
|
||||
for (int32 n = 0; n < _engine->_scene->_nbObjets; n++) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(n);
|
||||
actor->_workFlags.bWasDrawn = 0; // reset visible state
|
||||
actor->_workFlags.bIsTargetable = 0;
|
||||
|
||||
if (_engine->_grid->_zoneGrm != -1 && actor->_posObj.y > _engine->_scene->_sceneZones[_engine->_grid->_indexGrm].maxs.y) {
|
||||
continue;
|
||||
}
|
||||
// no redraw required
|
||||
if (actor->_flags.bIsBackgrounded && !flagflip) {
|
||||
// get actor position on screen
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(actor->posObj() - _engine->_grid->_worldCube);
|
||||
// check if actor is visible on screen, otherwise don't display it
|
||||
if (projPos.x > VIEW_X0 && projPos.x < VIEW_X1(_engine) && projPos.y > VIEW_Y0 && projPos.y < VIEW_Y1(_engine)) {
|
||||
actor->_workFlags.bWasDrawn = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// if the actor isn't set as hidden
|
||||
if (actor->_body == -1 || actor->_flags.bIsInvisible) {
|
||||
continue;
|
||||
}
|
||||
// get actor position on screen
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(actor->posObj() - _engine->_grid->_worldCube);
|
||||
|
||||
if ((actor->_flags.bSpriteClip && projPos.x > -112 && projPos.x < _engine->width() + 112 && projPos.y > VIEW_X0 && projPos.y < _engine->height() + 171) ||
|
||||
((!actor->_flags.bSpriteClip) && projPos.x > VIEW_X0 && projPos.x < VIEW_X1(_engine) && projPos.y > VIEW_Y0 && projPos.y < VIEW_Y1(_engine))) {
|
||||
|
||||
int32 ztri = actor->_posObj.x - _engine->_grid->_worldCube.x + actor->_posObj.z - _engine->_grid->_worldCube.z;
|
||||
|
||||
// if actor is above another actor
|
||||
if (actor->_carryBy != -1) {
|
||||
const ActorStruct *standOnActor = _engine->_scene->getActor(actor->_carryBy);
|
||||
ztri = standOnActor->_posObj.x - _engine->_grid->_worldCube.x + standOnActor->_posObj.z - _engine->_grid->_worldCube.z + 2;
|
||||
}
|
||||
|
||||
if (actor->_flags.bSprite3D) {
|
||||
drawList[drawListPos].type = DrawListType::DrawActorSprites;
|
||||
drawList[drawListPos].numObj = n;
|
||||
if (actor->_flags.bSpriteClip) {
|
||||
ztri = actor->_animStep.x - _engine->_grid->_worldCube.x + actor->_animStep.z - _engine->_grid->_worldCube.z;
|
||||
}
|
||||
} else {
|
||||
drawList[drawListPos].type = DrawListType::DrawObject3D;
|
||||
drawList[drawListPos].numObj = n;
|
||||
}
|
||||
|
||||
drawList[drawListPos].z = ztri;
|
||||
|
||||
drawListPos++;
|
||||
|
||||
// if use shadows
|
||||
if (_engine->_cfgfile.ShadowMode != 0 && !(actor->_flags.bNoShadow)) {
|
||||
if (actor->_carryBy != -1) {
|
||||
drawList[drawListPos].xw = actor->_posObj.x;
|
||||
drawList[drawListPos].yw = actor->_posObj.y - 1;
|
||||
drawList[drawListPos].zw = actor->_posObj.z;
|
||||
} else {
|
||||
const IVec3 shadowCoord = _engine->_movements->getShadow(actor->posObj());
|
||||
drawList[drawListPos].xw = shadowCoord.x;
|
||||
drawList[drawListPos].yw = shadowCoord.y;
|
||||
drawList[drawListPos].zw = shadowCoord.z;
|
||||
}
|
||||
|
||||
drawList[drawListPos].z = ztri - 1; // save the shadow entry in the _drawList
|
||||
drawList[drawListPos].type = DrawListType::DrawShadows;
|
||||
drawList[drawListPos].numObj = 0;
|
||||
drawList[drawListPos].num = 1;
|
||||
drawListPos++;
|
||||
}
|
||||
if (_flagMCGA && n == _engine->_scene->_numObjFollow) {
|
||||
_sceneryViewX = projPos.x;
|
||||
_sceneryViewY = projPos.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
return drawListPos;
|
||||
}
|
||||
|
||||
int32 Redraw::fillExtraDrawingList(DrawListStruct *drawList, int32 drawListPos) { // part of AffScene
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_engine->_extra->_extraList[i];
|
||||
if (extra->sprite == -1) {
|
||||
continue;
|
||||
}
|
||||
if (extra->type & ExtraType::TIME_IN) {
|
||||
if (_engine->timerRef - extra->spawnTime > 35) {
|
||||
extra->spawnTime = _engine->timerRef;
|
||||
extra->type &= ~ExtraType::TIME_IN;
|
||||
_engine->_sound->mixSample3D(Samples::ItemPopup, 0x1000, 1, extra->pos, -1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((extra->type & ExtraType::TIME_OUT) && (extra->type & ExtraType::FLASH)) {
|
||||
if (_engine->timerRef >= extra->spawnTime + extra->payload.lifeTime - _engine->toSeconds(3)) {
|
||||
if ((_engine->timerRef + extra->spawnTime) & 8) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(extra->pos - _engine->_grid->_worldCube);
|
||||
|
||||
if (projPos.x > VIEW_X0 && projPos.x < VIEW_X1(_engine) && projPos.y > VIEW_Y0 && projPos.y < VIEW_Y1(_engine)) {
|
||||
const int16 zVal = extra->pos.x - _engine->_grid->_worldCube.x + extra->pos.z - _engine->_grid->_worldCube.z;
|
||||
drawList[drawListPos].z = zVal;
|
||||
drawList[drawListPos].numObj = i;
|
||||
drawList[drawListPos].type = DrawListType::DrawExtras;
|
||||
drawListPos++;
|
||||
|
||||
if (_engine->_cfgfile.ShadowMode == 2 && !(extra->sprite & EXTRA_SPECIAL_MASK)) {
|
||||
const IVec3 &shadowCoord = _engine->_movements->getShadow(extra->pos);
|
||||
|
||||
drawList[drawListPos].z = zVal - 1;
|
||||
drawList[drawListPos].numObj = 0;
|
||||
drawList[drawListPos].type = DrawListType::DrawShadows;
|
||||
drawList[drawListPos].xw = shadowCoord.x;
|
||||
drawList[drawListPos].yw = shadowCoord.y;
|
||||
drawList[drawListPos].zw = shadowCoord.z;
|
||||
drawList[drawListPos].num = 0;
|
||||
drawListPos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return drawListPos;
|
||||
}
|
||||
|
||||
void Redraw::processDrawListShadows(const DrawListStruct &drawCmd) {
|
||||
// get actor position on screen
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(drawCmd.xw - _engine->_grid->_worldCube.x, drawCmd.yw - _engine->_grid->_worldCube.y, drawCmd.zw - _engine->_grid->_worldCube.z);
|
||||
|
||||
int32 spriteWidth = _engine->_resources->_spriteShadowPtr.surface(drawCmd.num).w;
|
||||
int32 spriteHeight = _engine->_resources->_spriteShadowPtr.surface(drawCmd.num).h;
|
||||
|
||||
// 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);
|
||||
|
||||
if (_engine->_interface->setClip(renderRect)) {
|
||||
_engine->_grid->drawSprite(renderRect.left, renderRect.top, _engine->_resources->_spriteShadowPtr, drawCmd.num);
|
||||
|
||||
const int32 tmpX = (drawCmd.xw + SIZE_BRICK_Y) / SIZE_BRICK_XZ;
|
||||
const int32 tmpY = drawCmd.yw / SIZE_BRICK_Y;
|
||||
const int32 tmpZ = (drawCmd.zw + SIZE_BRICK_Y) / SIZE_BRICK_XZ;
|
||||
|
||||
_engine->_grid->drawOverBrick(tmpX, tmpY, tmpZ);
|
||||
|
||||
addPhysBox(_engine->_interface->_clip);
|
||||
|
||||
_engine->_debugState->drawClip(renderRect);
|
||||
}
|
||||
_engine->_interface->unsetClip();
|
||||
}
|
||||
|
||||
void Redraw::processDrawListActors(const DrawListStruct &drawCmd, bool flagflip) {
|
||||
const int32 actorIdx = drawCmd.numObj;
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (actor->_anim >= 0) {
|
||||
const AnimData &animData = _engine->_resources->_animData[actor->_anim];
|
||||
BodyData &bodyData = actor->_entityDataPtr->getBody(actor->_body);
|
||||
_engine->_animations->setInterAnimObjet2(actor->_frame, animData, bodyData, &bodyData._animTimerData);
|
||||
}
|
||||
|
||||
const IVec3 &delta = actor->posObj() - _engine->_grid->_worldCube;
|
||||
Common::Rect renderRect;
|
||||
|
||||
if (actorIdx == OWN_ACTOR_SCENE_INDEX) {
|
||||
if (_engine->_actor->_cropBottomScreen) {
|
||||
_engine->_interface->_clip.bottom = _engine->_actor->_cropBottomScreen;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_engine->_renderer->affObjetIso(delta.x, delta.y, delta.z, LBAAngles::ANGLE_0, actor->_beta, LBAAngles::ANGLE_0, actor->_entityDataPtr->getBody(actor->_body), renderRect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_engine->_interface->setClip(renderRect)) {
|
||||
actor->_workFlags.bWasDrawn = 1;
|
||||
|
||||
const int32 tempX = (actor->_posObj.x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
int32 tempY = actor->_posObj.y / SIZE_BRICK_Y;
|
||||
const int32 tempZ = (actor->_posObj.z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
if (actor->brickShape() != ShapeType::kNone) {
|
||||
tempY++;
|
||||
}
|
||||
|
||||
_engine->_grid->drawOverBrick(tempX, tempY, tempZ);
|
||||
|
||||
addPhysBox(_engine->_interface->_clip);
|
||||
|
||||
if (actor->_flags.bIsBackgrounded && flagflip) {
|
||||
_engine->copyBlock(_engine->_interface->_clip);
|
||||
}
|
||||
|
||||
_engine->_debugState->drawClip(_engine->_interface->_clip);
|
||||
}
|
||||
_engine->_interface->unsetClip();
|
||||
}
|
||||
|
||||
void Redraw::processDrawListActorSprites(const DrawListStruct &drawCmd, bool bgRedraw) {
|
||||
int32 actorIdx = drawCmd.numObj;
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
const SpriteData &spriteData = _engine->_resources->_spriteData[actor->_body];
|
||||
// TODO: using the raw pointer and not the SpriteData surface here is a workaround for issue https://bugs.scummvm.org/ticket/12024
|
||||
const uint8 *spritePtr = _engine->_resources->_spriteTable[actor->_body];
|
||||
|
||||
// get actor position on screen
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(actor->posObj() - _engine->_grid->_worldCube);
|
||||
|
||||
const int32 dx = spriteData.surface().w;
|
||||
const int32 dy = spriteData.surface().h;
|
||||
|
||||
// calculate sprite position on screen
|
||||
const SpriteDim *dim = _engine->_resources->_spriteBoundingBox.dim(actor->_body);
|
||||
Common::Rect renderRect;
|
||||
renderRect.left = projPos.x + dim->x;
|
||||
renderRect.top = projPos.y + dim->y;
|
||||
renderRect.right = renderRect.left + dx;
|
||||
renderRect.bottom = renderRect.top + dy;
|
||||
|
||||
bool validClip;
|
||||
if (actor->_flags.bSpriteClip) {
|
||||
const Common::Rect rect(_projPosScreen.x + actor->_cropLeft, _projPosScreen.y + actor->_cropTop, _projPosScreen.x + actor->_cropRight, _projPosScreen.y + actor->_cropBottom);
|
||||
validClip = _engine->_interface->setClip(rect);
|
||||
} else {
|
||||
validClip = _engine->_interface->setClip(renderRect);
|
||||
}
|
||||
|
||||
if (validClip) {
|
||||
_engine->_grid->drawGraph(0, renderRect.left, renderRect.top, spritePtr);
|
||||
|
||||
actor->_workFlags.bWasDrawn = 1;
|
||||
|
||||
if (actor->_flags.bSpriteClip) {
|
||||
const int32 xm = (actor->_animStep.x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
const int32 ym = actor->_animStep.y / SIZE_BRICK_Y;
|
||||
const int32 zm = (actor->_animStep.z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
_engine->_grid->drawOverBrick3(xm, ym, zm);
|
||||
} else {
|
||||
const int32 xm = (actor->_posObj.x + actor->_boundingBox.maxs.x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
int32 ym = actor->_posObj.y / SIZE_BRICK_Y;
|
||||
const int32 zm = (actor->_posObj.z + actor->_boundingBox.maxs.z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
if (actor->brickShape() != ShapeType::kNone) {
|
||||
ym++;
|
||||
}
|
||||
|
||||
_engine->_grid->drawOverBrick3(xm, ym, zm);
|
||||
}
|
||||
|
||||
addPhysBox(_engine->_interface->_clip);
|
||||
|
||||
if (actor->_flags.bIsBackgrounded && bgRedraw) {
|
||||
_engine->copyBlock(_engine->_interface->_clip);
|
||||
}
|
||||
|
||||
_engine->_debugState->drawClip(renderRect);
|
||||
_engine->_interface->unsetClip();
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::processDrawListExtras(const DrawListStruct &drawCmd) {
|
||||
int32 extraIdx = drawCmd.numObj;
|
||||
ExtraListStruct *extra = &_engine->_extra->_extraList[extraIdx];
|
||||
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(extra->pos - _engine->_grid->_worldCube);
|
||||
|
||||
Common::Rect renderRect;
|
||||
if (extra->sprite & EXTRA_SPECIAL_MASK) {
|
||||
_engine->_extra->affSpecial(extraIdx, projPos.x, projPos.y, renderRect);
|
||||
} else {
|
||||
const SpriteData &spritePtr = _engine->_resources->_spriteData[extra->sprite];
|
||||
const int32 dx = spritePtr.surface().w;
|
||||
const int32 dy = spritePtr.surface().h;
|
||||
|
||||
// calculate sprite position on screen
|
||||
const SpriteDim *dim = _engine->_resources->_spriteBoundingBox.dim(extra->sprite);
|
||||
renderRect.left = projPos.x + dim->x;
|
||||
renderRect.top = projPos.y + dim->y;
|
||||
renderRect.right = renderRect.left + dx;
|
||||
renderRect.bottom = renderRect.top + dy;
|
||||
|
||||
_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
|
||||
}
|
||||
|
||||
if (_engine->_interface->setClip(renderRect)) {
|
||||
const int32 xm = (extra->pos.x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
const int32 ym = extra->pos.y / SIZE_BRICK_Y;
|
||||
const int32 zm = (extra->pos.z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
|
||||
_engine->_grid->drawOverBrick(xm, ym, zm);
|
||||
addPhysBox(_engine->_interface->_clip);
|
||||
|
||||
// show clipping area
|
||||
//drawRectBorders(renderRect);
|
||||
_engine->_interface->unsetClip();
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::correctZLevels(DrawListStruct *listTri, int32 drawListPos) {
|
||||
ActorStruct *ptrobj = _engine->_scene->getActor(OWN_ACTOR_SCENE_INDEX);
|
||||
if (ptrobj->_flags.bIsInvisible || ptrobj->_body == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
IVec3 tmin = ptrobj->posObj() + ptrobj->_boundingBox.mins;
|
||||
IVec3 tmax = ptrobj->posObj() + ptrobj->_boundingBox.maxs;
|
||||
int32 twinsenpos = -1;
|
||||
int32 twinsenz = -1;
|
||||
for (int32 pos = 0; pos < drawListPos; ++pos) {
|
||||
DrawListStruct &drawCmd = listTri[pos];
|
||||
if (drawCmd.type == DrawListType::DrawObject3D && drawCmd.numObj == OWN_ACTOR_SCENE_INDEX) {
|
||||
twinsenpos = pos;
|
||||
twinsenz = drawCmd.z;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (twinsenpos == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 n = 0; n < drawListPos; ++n) {
|
||||
DrawListStruct &ptrtri = listTri[n];
|
||||
uint32 typeobj = ptrtri.type;
|
||||
int32 numobj = ptrtri.numObj;
|
||||
ptrobj = _engine->_scene->getActor(numobj);
|
||||
switch (typeobj) {
|
||||
default:
|
||||
break;
|
||||
case DrawListType::DrawActorSprites:
|
||||
if (ptrobj->_flags.bSpriteClip) {
|
||||
IVec3 pmin = ptrobj->_animStep + ptrobj->_boundingBox.mins;
|
||||
IVec3 pmax = ptrobj->_animStep + ptrobj->_boundingBox.maxs;
|
||||
|
||||
if (pmax.x > tmin.x && pmin.x < tmax.x) {
|
||||
if (pmax.z <= tmin.z) {
|
||||
// twinsen after
|
||||
if (twinsenz < ptrtri.z) {
|
||||
// correct the error
|
||||
listTri[twinsenpos].z = ptrtri.z;
|
||||
listTri[twinsenpos].numObj = ptrtri.numObj;
|
||||
listTri[twinsenpos].type = ptrtri.type;
|
||||
|
||||
ptrtri.numObj = OWN_ACTOR_SCENE_INDEX;
|
||||
ptrtri.type = DrawListType::DrawObject3D;
|
||||
ptrtri.z = (int16)twinsenz;
|
||||
|
||||
twinsenpos = n;
|
||||
numobj = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pmin.z >= tmax.z) {
|
||||
// twinsen before
|
||||
if (twinsenz > ptrtri.z) {
|
||||
// correct the error
|
||||
listTri[twinsenpos].z = ptrtri.z;
|
||||
listTri[twinsenpos].numObj = ptrtri.numObj;
|
||||
listTri[twinsenpos].type = ptrtri.type;
|
||||
|
||||
ptrtri.numObj = OWN_ACTOR_SCENE_INDEX;
|
||||
ptrtri.type = DrawListType::DrawObject3D;
|
||||
ptrtri.z = (int16)twinsenz;
|
||||
|
||||
twinsenpos = n;
|
||||
numobj = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (pmax.z > tmin.z && pmin.z < tmax.z) {
|
||||
if (pmax.x <= tmin.x) {
|
||||
// twinsen after
|
||||
if (twinsenz < ptrtri.z) {
|
||||
// correct the error
|
||||
listTri[twinsenpos].z = ptrtri.z;
|
||||
listTri[twinsenpos].numObj = ptrtri.numObj;
|
||||
listTri[twinsenpos].type = ptrtri.type;
|
||||
|
||||
ptrtri.numObj = OWN_ACTOR_SCENE_INDEX;
|
||||
ptrtri.type = DrawListType::DrawObject3D;
|
||||
ptrtri.z = (int16)twinsenz;
|
||||
|
||||
twinsenpos = n;
|
||||
numobj = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// twinsen before
|
||||
if (twinsenz > ptrtri.z) {
|
||||
// correct the error
|
||||
listTri[twinsenpos].z = ptrtri.z;
|
||||
listTri[twinsenpos].numObj = ptrtri.numObj;
|
||||
listTri[twinsenpos].type = ptrtri.type;
|
||||
|
||||
ptrtri.numObj = OWN_ACTOR_SCENE_INDEX;
|
||||
ptrtri.type = DrawListType::DrawObject3D;
|
||||
ptrtri.z = (int16)twinsenz;
|
||||
|
||||
twinsenpos = n;
|
||||
numobj = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (numobj == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::processDrawList(DrawListStruct *drawList, int32 drawListPos, bool bgRedraw) {
|
||||
bool shadowtwinsen = false;
|
||||
for (int32 pos = 0; pos < drawListPos; ++pos) {
|
||||
const DrawListStruct &drawCmd = drawList[pos];
|
||||
const uint32 flags = drawCmd.type;
|
||||
if (flags == DrawListType::DrawObject3D) {
|
||||
// this is correcting a bug that came with correctZLevels() - original sources
|
||||
if (_engine->_cfgfile.ShadowMode != 0 && drawCmd.numObj == OWN_ACTOR_SCENE_INDEX && !shadowtwinsen) {
|
||||
for (int32 i = pos; i < drawListPos; i++) {
|
||||
if (drawList[i].numObj == OWN_ACTOR_SCENE_INDEX && drawList[i].type == DrawListType::DrawShadows) {
|
||||
shadowtwinsen = true;
|
||||
processDrawListShadows(drawList[i]);
|
||||
drawList[i].type = (uint32)-1; // invalidate shadow entry
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
processDrawListActors(drawCmd, bgRedraw);
|
||||
} else if (flags == DrawListType::DrawShadows && !_engine->_actor->_cropBottomScreen) {
|
||||
if (drawCmd.numObj == OWN_ACTOR_SCENE_INDEX) {
|
||||
shadowtwinsen = true;
|
||||
}
|
||||
processDrawListShadows(drawCmd);
|
||||
} else if (flags == DrawListType::DrawActorSprites) {
|
||||
processDrawListActorSprites(drawCmd, bgRedraw);
|
||||
} else if (flags == DrawListType::DrawExtras) {
|
||||
processDrawListExtras(drawCmd);
|
||||
}
|
||||
|
||||
_engine->_interface->unsetClip();
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::renderOverlays() {
|
||||
for (int32 i = 0; i < OVERLAY_MAX_ENTRIES; i++) {
|
||||
OverlayListStruct *overlay = &overlayList[i];
|
||||
if (overlay->num == -1) {
|
||||
continue;
|
||||
}
|
||||
// process position overlay
|
||||
switch (overlay->move) {
|
||||
case OverlayPosType::koNormal: // wait number of seconds and die
|
||||
if (_engine->timerRef >= overlay->timerEnd) {
|
||||
overlay->num = -1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case OverlayPosType::koFollowActor: { // follow obj coordinates for number of seconds and die
|
||||
ActorStruct *actor2 = _engine->_scene->getActor(overlay->info);
|
||||
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(actor2->_posObj.x - _engine->_grid->_worldCube.x, actor2->_posObj.y + actor2->_boundingBox.maxs.y - _engine->_grid->_worldCube.y, actor2->_posObj.z - _engine->_grid->_worldCube.z);
|
||||
|
||||
overlay->x = projPos.x;
|
||||
overlay->y = projPos.y;
|
||||
|
||||
if (_engine->timerRef >= overlay->timerEnd) {
|
||||
overlay->num = -1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// process overlay type
|
||||
switch (overlay->type) {
|
||||
case OverlayType::koSprite: {
|
||||
const SpriteData &spritePtr = _engine->_resources->_spriteData[overlay->num];
|
||||
const int32 spriteWidth = spritePtr.surface().w;
|
||||
const int32 spriteHeight = spritePtr.surface().h;
|
||||
|
||||
const SpriteDim *dim = _engine->_resources->_spriteBoundingBox.dim(overlay->num);
|
||||
Common::Rect renderRect;
|
||||
renderRect.left = dim->x + overlay->x;
|
||||
renderRect.top = dim->y + overlay->y;
|
||||
renderRect.right = renderRect.left + spriteWidth;
|
||||
renderRect.bottom = renderRect.top + spriteHeight;
|
||||
|
||||
_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
|
||||
|
||||
addPhysBox(_engine->_interface->_clip);
|
||||
break;
|
||||
}
|
||||
case OverlayType::koNumber: {
|
||||
char text[10];
|
||||
snprintf(text, sizeof(text), "%d", overlay->num);
|
||||
|
||||
const int32 textLength = _engine->_text->sizeFont(text);
|
||||
const int32 textHeight = 48;
|
||||
|
||||
Common::Rect renderRect;
|
||||
renderRect.left = overlay->x - (textLength / 2);
|
||||
renderRect.top = overlay->y - 24;
|
||||
renderRect.right = overlay->x + (textLength / 2);
|
||||
renderRect.bottom = overlay->y + textHeight;
|
||||
|
||||
_engine->_interface->setClip(renderRect);
|
||||
|
||||
_engine->_text->setFontColor(overlay->info);
|
||||
|
||||
_engine->_text->drawText(renderRect.left, renderRect.top, text);
|
||||
|
||||
addPhysBox(_engine->_interface->_clip);
|
||||
|
||||
_engine->_interface->unsetClip();
|
||||
break;
|
||||
}
|
||||
case OverlayType::koNumberRange: {
|
||||
const int32 range = boundRuleThree(overlay->info, overlay->num, 100, overlay->timerEnd - _engine->timerRef - _engine->toSeconds(1));
|
||||
|
||||
char text[10];
|
||||
Common::sprintf_s(text, "%d", range);
|
||||
|
||||
const int32 textLength = _engine->_text->sizeFont(text);
|
||||
const int32 textHeight = 48;
|
||||
|
||||
Common::Rect renderRect;
|
||||
renderRect.left = overlay->x - (textLength / 2);
|
||||
renderRect.top = overlay->y - 24;
|
||||
renderRect.right = overlay->x + (textLength / 2);
|
||||
renderRect.bottom = overlay->y + textHeight;
|
||||
|
||||
_engine->_interface->setClip(renderRect);
|
||||
|
||||
_engine->_text->setFontColor(COLOR_GOLD);
|
||||
|
||||
_engine->_text->drawText(renderRect.left, renderRect.top, text);
|
||||
|
||||
addPhysBox(_engine->_interface->_clip);
|
||||
_engine->_interface->unsetClip();
|
||||
break;
|
||||
}
|
||||
case OverlayType::koInventoryItem: {
|
||||
const int32 item = overlay->num;
|
||||
const Common::Rect rect(10, 10, 79, 79);
|
||||
|
||||
_engine->_interface->box(rect, COLOR_BLACK);
|
||||
_engine->_interface->setClip(rect);
|
||||
|
||||
const BodyData &bodyPtr = _engine->_resources->_inventoryTable[item];
|
||||
_overlayRotation += 1; // overlayRotation += 8;
|
||||
_engine->_renderer->draw3dObject(40, 40, bodyPtr, _overlayRotation, 16000);
|
||||
_engine->_menu->drawRectBorders(rect);
|
||||
addPhysBox(rect);
|
||||
_engine->_gameState->init3DGame();
|
||||
_engine->_interface->unsetClip();
|
||||
break;
|
||||
}
|
||||
case OverlayType::koText: {
|
||||
char text[256];
|
||||
_engine->_text->getMenuText((TextId)overlay->num, text, sizeof(text));
|
||||
|
||||
const int32 textLength = _engine->_text->sizeFont(text);
|
||||
const int32 textHeight = 48;
|
||||
|
||||
Common::Rect renderRect;
|
||||
renderRect.left = overlay->x - (textLength / 2);
|
||||
renderRect.top = overlay->y - (textHeight / 2);
|
||||
renderRect.right = overlay->x + (textLength / 2);
|
||||
renderRect.bottom = overlay->y + textHeight;
|
||||
|
||||
renderRect.clip(_engine->rect());
|
||||
|
||||
_engine->_interface->setClip(renderRect);
|
||||
|
||||
_engine->_text->setFontColor(_engine->_scene->getActor(overlay->info)->_talkColor);
|
||||
|
||||
_engine->_text->drawText(renderRect.left, renderRect.top, text);
|
||||
|
||||
addPhysBox(_engine->_interface->_clip);
|
||||
_engine->_interface->unsetClip();
|
||||
break;
|
||||
}
|
||||
case OverlayType::koSysText:
|
||||
case OverlayType::koFlash:
|
||||
case OverlayType::koRain:
|
||||
case OverlayType::koInventory:
|
||||
// TODO lba2
|
||||
case OverlayType::koMax:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::setRenderText(const Common::String &text) {
|
||||
_text = text;
|
||||
if (_text.empty()) {
|
||||
_textDisappearTime = -1;
|
||||
} else {
|
||||
_textDisappearTime = _engine->timerRef + _engine->toSeconds(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::renderText() {
|
||||
if (_text.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_textDisappearTime != -1 && _engine->timerRef > _textDisappearTime) {
|
||||
_text.clear();
|
||||
_textDisappearTime = -1;
|
||||
return;
|
||||
}
|
||||
_engine->_text->setFontColor(COLOR_WHITE);
|
||||
|
||||
const int padding = 10;
|
||||
const int x = padding;
|
||||
const int height = _engine->_text->lineHeight;
|
||||
const int y = _engine->height() - height - padding;
|
||||
const int width = _engine->_text->sizeFont(_text.c_str());
|
||||
_engine->_text->drawText(x, y, _text.c_str(), true);
|
||||
_engine->copyBlockPhys(x, y, x + width, y + height);
|
||||
const Common::Rect redraw(x, y, x + width, y + height);
|
||||
addPhysBox(redraw);
|
||||
}
|
||||
|
||||
void Redraw::fillBackground(uint8 color) {
|
||||
_engine->_frontVideoBuffer.fillRect(Common::Rect(0, 0, _engine->width(), _engine->height()), color);
|
||||
}
|
||||
|
||||
void Redraw::drawScene(bool flagflip) { // AffScene
|
||||
int32 tmp_projPosX = _projPosScreen.x;
|
||||
int32 tmp_projPosY = _projPosScreen.y;
|
||||
|
||||
_engine->_interface->unsetClip();
|
||||
|
||||
if (!flagflip) {
|
||||
clsBoxes();
|
||||
} else {
|
||||
_engine->saveTimer(false);
|
||||
if (_engine->_scene->_newCube != SCENE_CEILING_GRID_FADE_1 && _engine->_scene->_newCube != SCENE_CEILING_GRID_FADE_2) {
|
||||
_engine->_screens->fadeToBlack(_engine->_screens->_ptrPal);
|
||||
}
|
||||
|
||||
_engine->_grid->redrawGrid();
|
||||
|
||||
updateOverlayTypePosition(tmp_projPosX, tmp_projPosY, _projPosScreen.x, _projPosScreen.y);
|
||||
|
||||
_engine->saveFrontBuffer();
|
||||
|
||||
if (_engine->_scene->_newCube != SCENE_CEILING_GRID_FADE_1 && _engine->_scene->_newCube != SCENE_CEILING_GRID_FADE_2) {
|
||||
_engine->_screens->fadeToPal(_engine->_screens->_ptrPal);
|
||||
}
|
||||
}
|
||||
|
||||
DrawListStruct drawList[NUM_MAX_ACTORS + EXTRA_MAX_ENTRIES]; // ListTri[MAX_OBJECTS + MAX_EXTRAS]
|
||||
int32 drawListPos = fillActorDrawingList(drawList, flagflip);
|
||||
drawListPos = fillExtraDrawingList(drawList, drawListPos);
|
||||
|
||||
_nbPhysBox = 0;
|
||||
sortDrawingList(drawList, drawListPos);
|
||||
correctZLevels(drawList, drawListPos);
|
||||
processDrawList(drawList, drawListPos, flagflip);
|
||||
|
||||
_engine->_debugState->renderDebugView();
|
||||
|
||||
renderOverlays();
|
||||
renderText();
|
||||
|
||||
_engine->_interface->unsetClip();
|
||||
|
||||
// need to be here to fade after drawing all actors in scene
|
||||
if (_engine->_scene->_newCube == SCENE_CEILING_GRID_FADE_2) {
|
||||
_engine->_scene->_newCube = SCENE_CEILING_GRID_FADE_1;
|
||||
}
|
||||
|
||||
if (flagflip) {
|
||||
moveNextAreas();
|
||||
_engine->restoreTimer();
|
||||
} else {
|
||||
flipBoxes();
|
||||
}
|
||||
|
||||
if (_engine->_screens->_flagFade) {
|
||||
if (_engine->_screens->_flagPalettePcx) {
|
||||
_engine->_screens->fadeToPal(_engine->_screens->_palettePcx);
|
||||
} else {
|
||||
_engine->_screens->fadeToPal(_engine->_screens->_ptrPal);
|
||||
}
|
||||
_engine->_screens->_flagFade = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Redraw::drawBubble(int32 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
|
||||
// get actor position on screen
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(actor->_posObj.x - _engine->_grid->_worldCube.x, actor->_posObj.y + actor->_boundingBox.maxs.y - _engine->_grid->_worldCube.y, actor->_posObj.z - _engine->_grid->_worldCube.z);
|
||||
|
||||
if (actorIdx != _bubbleActor) {
|
||||
_bubbleSpriteIndex = _bubbleSpriteIndex ^ 1;
|
||||
_bubbleActor = actorIdx;
|
||||
}
|
||||
|
||||
const SpriteData &spritePtr = _engine->_resources->_spriteData[_bubbleSpriteIndex];
|
||||
const int32 spriteWidth = spritePtr.surface().w;
|
||||
const int32 spriteHeight = spritePtr.surface().h;
|
||||
|
||||
// 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;
|
||||
renderRect.right = spriteWidth + renderRect.left - 1;
|
||||
renderRect.bottom = spriteHeight + renderRect.top - 1;
|
||||
|
||||
if (_engine->_interface->setClip(renderRect)) {
|
||||
_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
|
||||
_engine->_interface->unsetClip();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
210
engines/twine/renderer/redraw.h
Normal file
210
engines/twine/renderer/redraw.h
Normal file
@@ -0,0 +1,210 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_RENDERER_REDRAW_H
|
||||
#define TWINE_RENDERER_REDRAW_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
// MAX_INCRUST_DISP
|
||||
#define OVERLAY_MAX_ENTRIES 10
|
||||
|
||||
enum class OverlayType {
|
||||
koSprite = 0, // INCRUST_SPRITE
|
||||
koNumber = 1, // INCRUST_NUM
|
||||
koNumberRange = 2, // INCRUST_CMPT
|
||||
koInventoryItem = 3, // INCRUST_OBJ
|
||||
koText = 4, // INCRUST_TEXT
|
||||
koInventory = 5, // lba2 (INCRUST_INVENTORY)
|
||||
koSysText = 6, // lba2 (INCRUST_SYS_TEXT)
|
||||
koFlash = 7, // lba2 (INCRUST_ECLAIR)
|
||||
koRain = 8, // lba2 (INCRUST_PLUIE)
|
||||
koMax
|
||||
};
|
||||
|
||||
// lba2
|
||||
#define INCRUST_YCLIP (1 << 8)
|
||||
|
||||
enum class OverlayPosType {
|
||||
koNormal = 0,
|
||||
koFollowActor = 1
|
||||
};
|
||||
|
||||
/** Overlay list structure */
|
||||
struct OverlayListStruct {
|
||||
int16 num = 0; // sprite/3d model entry | number | number range
|
||||
int16 x = 0;
|
||||
int16 y = 0;
|
||||
OverlayType type = OverlayType::koSprite;
|
||||
int16 info = 0; // text = actor | total coins
|
||||
OverlayPosType move = OverlayPosType::koNormal;
|
||||
int16 timerEnd = 0; // life time in ticks - see toSeconds()
|
||||
};
|
||||
|
||||
struct DrawListStruct {
|
||||
int16 z = 0; // depth sorting value
|
||||
uint32 type = 0;
|
||||
// NumObj was also used with mask of type and numObj - we are
|
||||
// not masking the value in numObj, but store the type in type
|
||||
uint16 numObj = 0;
|
||||
|
||||
uint16 xw = 0;
|
||||
uint16 yw = 0;
|
||||
uint16 zw = 0;
|
||||
uint16 num = 0;
|
||||
|
||||
inline bool operator==(const DrawListStruct& other) const {
|
||||
return z == other.z;
|
||||
}
|
||||
|
||||
inline bool operator<(const DrawListStruct& other) const {
|
||||
return z < other.z;
|
||||
}
|
||||
};
|
||||
|
||||
#define TYPE_OBJ_SHIFT (10)
|
||||
#define TYPE_OBJ_FIRST (1 << TYPE_OBJ_SHIFT) // 1024
|
||||
#define NUM_OBJ_MASK (TYPE_OBJ_FIRST - 1)
|
||||
|
||||
class TwinEEngine;
|
||||
class Redraw {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
enum DrawListType {
|
||||
DrawObject3D = (0 << TYPE_OBJ_SHIFT), // TYPE_OBJ_3D
|
||||
DrawFlagRed = (1 << TYPE_OBJ_SHIFT),
|
||||
DrawFlagYellow = (2 << TYPE_OBJ_SHIFT),
|
||||
DrawShadows = (3 << TYPE_OBJ_SHIFT), // TYPE_SHADOW
|
||||
DrawActorSprites = (4 << TYPE_OBJ_SHIFT), // TYPE_OBJ_SPRITE
|
||||
DrawZoneDec = (5 << TYPE_OBJ_SHIFT),
|
||||
DrawExtras = (6 << TYPE_OBJ_SHIFT), // TYPE_EXTRA
|
||||
DrawPrimitive = (7 << TYPE_OBJ_SHIFT)
|
||||
};
|
||||
|
||||
Common::Rect _currentRedrawList[300];
|
||||
Common::Rect _nextRedrawList[300];
|
||||
|
||||
int16 _overlayRotation = 0;
|
||||
|
||||
/** Save last actor that bubble dialog icon */
|
||||
int32 _bubbleActor = -1;
|
||||
int32 _bubbleSpriteIndex;
|
||||
|
||||
// big font shadow text in the lower left corner
|
||||
Common::String _text;
|
||||
int32 _textDisappearTime = -1;
|
||||
|
||||
/**
|
||||
* Add a certain region to the current redraw list array
|
||||
* @param redrawArea redraw the region
|
||||
*/
|
||||
void addRedrawCurrentArea(const Common::Rect &redrawArea);
|
||||
/**
|
||||
* Move next regions to the current redraw list,
|
||||
* setup the redraw areas for next display
|
||||
*/
|
||||
void moveNextAreas();
|
||||
void updateOverlayTypePosition(int16 x1, int16 y1, int16 x2, int16 y2);
|
||||
|
||||
void processDrawListShadows(const DrawListStruct& drawCmd);
|
||||
void processDrawListActors(const DrawListStruct& drawCmd, bool bgRedraw);
|
||||
void processDrawListActorSprites(const DrawListStruct& drawCmd, bool bgRedraw);
|
||||
void processDrawListExtras(const DrawListStruct& drawCmd);
|
||||
|
||||
int32 fillActorDrawingList(DrawListStruct *drawList, bool bgRedraw);
|
||||
int32 fillExtraDrawingList(DrawListStruct *drawList, int32 drawListPos);
|
||||
void correctZLevels(DrawListStruct *drawList, int32 drawListPos);
|
||||
void processDrawList(DrawListStruct *drawList, int32 drawListPos, bool bgRedraw);
|
||||
void renderOverlays();
|
||||
void renderText();
|
||||
|
||||
void fillBackground(uint8 color);
|
||||
|
||||
public:
|
||||
Redraw(TwinEEngine *engine);
|
||||
|
||||
bool _flagMCGA = false;
|
||||
|
||||
/** Request background redraw */
|
||||
bool _firstTime = false;
|
||||
|
||||
IVec3 _projPosScreen; // XpOrgw, YpOrgw
|
||||
|
||||
/** Current number of redraw regions in the screen */
|
||||
int32 _nbPhysBox = 0; // fullRedrawVar8
|
||||
/** Number of redraw regions in the screen */
|
||||
int32 _nbOptPhysBox = 0;
|
||||
|
||||
int _sceneryViewX = 0; // xmin
|
||||
int _sceneryViewY = 0; // ymin
|
||||
|
||||
OverlayListStruct overlayList[OVERLAY_MAX_ENTRIES];
|
||||
|
||||
void setRenderText(const Common::String &text);
|
||||
|
||||
// InitIncrustDisp
|
||||
int32 addOverlay(OverlayType type, int16 info0, int16 x, int16 y, int16 info1, OverlayPosType posType, int16 lifeTime);
|
||||
void posObjIncrust(OverlayListStruct *ptrdisp, int32 num); // lba2
|
||||
|
||||
/**
|
||||
* Add a certain region to redraw list array
|
||||
* @param left start width to redraw the region
|
||||
* @param top start height to redraw the region
|
||||
* @param right end width to redraw the region
|
||||
* @param bottom end height to redraw the region
|
||||
*/
|
||||
void addRedrawArea(int32 left, int32 top, int32 right, int32 bottom); // AddPhysBox
|
||||
void addPhysBox(const Common::Rect &rect); // AddPhysBox
|
||||
|
||||
/**
|
||||
* Flip currentRedrawList regions in the screen
|
||||
* This only updates small areas in the screen so few CPU processor is used
|
||||
*/
|
||||
void flipBoxes();
|
||||
|
||||
/** Blit/Update all screen regions in the currentRedrawList */
|
||||
void clsBoxes();
|
||||
|
||||
/**
|
||||
* This is responsible for the entire game screen redraw
|
||||
* @param bgRedraw true if we want to redraw background grid, false if we want to update certain screen areas
|
||||
*/
|
||||
void drawScene(bool bgRedraw);
|
||||
|
||||
/** Draw dialogue sprite image */
|
||||
void drawBubble(int32 actorIdx);
|
||||
|
||||
/**
|
||||
* Sort drawing list struct ordered as the first objects appear in the top left corner of the screen
|
||||
* @param list drawing list variable which contains information of the drawing objects
|
||||
* @param listSize number of drawing objects in the list
|
||||
*/
|
||||
void sortDrawingList(DrawListStruct *list, int32 listSize) const;
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
1914
engines/twine/renderer/renderer.cpp
Normal file
1914
engines/twine/renderer/renderer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
326
engines/twine/renderer/renderer.h
Normal file
326
engines/twine/renderer/renderer.h
Normal file
@@ -0,0 +1,326 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_RENDERER_RENDERER_H
|
||||
#define TWINE_RENDERER_RENDERER_H
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/parser/bodytypes.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
#define POLYGONTYPE_FLAT 0
|
||||
#define POLYGONTYPE_TELE 1
|
||||
// horizontal color adjustment with changing pattern over the polygon
|
||||
#define POLYGONTYPE_COPPER 2
|
||||
#define POLYGONTYPE_BOPPER 3
|
||||
#define POLYGONTYPE_MARBLE 4
|
||||
#define POLYGONTYPE_TRANS 5
|
||||
#define POLYGONTYPE_TRAME 6
|
||||
#define POLYGONTYPE_GOURAUD 7
|
||||
#define POLYGONTYPE_DITHER 8
|
||||
#define POLYGONTYPE_OUTLINE 9
|
||||
|
||||
#define MAT_TRISTE 0
|
||||
#define MAT_PIERRE 1
|
||||
#define MAT_COPPER 2
|
||||
#define MAT_BOPPER 3
|
||||
#define MAT_MARBRE 4
|
||||
#define MAT_TRANS 5
|
||||
#define MAT_TRAME 6
|
||||
#define MAT_FLAT 7
|
||||
#define MAT_GRANIT 8
|
||||
#define MAT_GOURAUD 9
|
||||
#define MAT_DITHER 10
|
||||
|
||||
#define TYPE_3D 0
|
||||
#define TYPE_ISO 1
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class BodyData;
|
||||
class TwinEEngine;
|
||||
|
||||
struct ComputedVertex {
|
||||
int16 intensity = 0;
|
||||
int16 x = 0;
|
||||
int16 y = 0;
|
||||
};
|
||||
|
||||
bool isPolygonVisible(const ComputedVertex *vertices);
|
||||
|
||||
struct CmdRenderPolygon {
|
||||
uint8 renderType = 0;
|
||||
uint8 numVertices = 0;
|
||||
int16 colorIndex = 0; // intensity
|
||||
// followed by Vertex array
|
||||
};
|
||||
|
||||
struct IMatrix3x3 {
|
||||
IVec3 row1;
|
||||
IVec3 row2;
|
||||
IVec3 row3;
|
||||
};
|
||||
|
||||
inline IMatrix3x3 operator*(const IMatrix3x3 &matrix, const IVec3 &vec) {
|
||||
IMatrix3x3 out;
|
||||
out.row1.x = matrix.row1.x * vec.x;
|
||||
out.row1.y = matrix.row1.y * vec.x;
|
||||
out.row1.z = matrix.row1.z * vec.x;
|
||||
|
||||
out.row2.x = matrix.row2.x * vec.y;
|
||||
out.row2.y = matrix.row2.y * vec.y;
|
||||
out.row2.z = matrix.row2.z * vec.y;
|
||||
|
||||
out.row3.x = matrix.row3.x * vec.z;
|
||||
out.row3.y = matrix.row3.y * vec.z;
|
||||
out.row3.z = matrix.row3.z * vec.z;
|
||||
return out;
|
||||
}
|
||||
|
||||
class Renderer {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
struct RenderCommand {
|
||||
int16 depth = 0;
|
||||
int16 renderType = 0;
|
||||
/**
|
||||
* Pointer to the command data
|
||||
* @sa renderCoordinatesBuffer
|
||||
* @sa CmdRenderLine
|
||||
* @sa CmdRenderSphere
|
||||
* @sa CmdRenderPolygon
|
||||
*/
|
||||
uint8 *dataPtr = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A render command payload for drawing a line
|
||||
*
|
||||
* @sa RenderCommand
|
||||
*/
|
||||
struct CmdRenderLine {
|
||||
uint8 colorIndex = 0;
|
||||
uint8 unk1 = 0;
|
||||
uint8 unk2 = 0;
|
||||
uint8 unk3 = 0;
|
||||
int16 x1 = 0;
|
||||
int16 y1 = 0;
|
||||
int16 x2 = 0;
|
||||
int16 y2 = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A render command payload for drawing a sphere
|
||||
*
|
||||
* @sa RenderCommand
|
||||
*/
|
||||
struct CmdRenderSphere {
|
||||
uint16 color = 0; // color start and end values
|
||||
uint8 polyRenderType = 0;
|
||||
int16 radius = 0;
|
||||
int16 x = 0;
|
||||
int16 y = 0;
|
||||
int16 z = 0;
|
||||
};
|
||||
|
||||
struct ModelData {
|
||||
I16Vec3 computedPoints[800]; // List_Anim_Point
|
||||
I16Vec3 flattenPoints[800];
|
||||
int16 normalTable[500]{0};
|
||||
};
|
||||
|
||||
// this is a member var, because 10k on the stack is not supported by every platform
|
||||
ModelData _modelData;
|
||||
|
||||
// AnimNuage
|
||||
void animModel(ModelData *modelData, const BodyData &bodyData, RenderCommand *renderCmds, const IVec3 &angleVec, const IVec3 &renderPos, Common::Rect &modelRect);
|
||||
bool computeSphere(int32 x, int32 y, int32 radius, int &vtop, int &vbottom);
|
||||
bool renderObjectIso(const BodyData &bodyData, RenderCommand **renderCmds, ModelData *modelData, Common::Rect &modelRect); // RenderObjetIso
|
||||
IVec3 longInverseRot(int32 x, int32 y, int32 z);
|
||||
inline IVec3 getCameraAnglePositions(const IVec3 &vec) {
|
||||
return longInverseRot(vec.x, vec.y, vec.z);
|
||||
}
|
||||
void rotMatIndex2(IMatrix3x3 *targetMatrix, const IMatrix3x3 *currentMatrix, const IVec3 &angleVec);
|
||||
void rotList(const Common::Array<BodyVertex>& vertices, int32 firstPoint, int32 numPoints, I16Vec3 *destPoints, const IMatrix3x3 *rotationMatrix, const IVec3 &destPos);
|
||||
void processRotatedElement(IMatrix3x3 *targetMatrix, const Common::Array<BodyVertex>& vertices, int32 rotX, int32 rotY, int32 rotZ, const BodyBone &bone, ModelData *modelData);
|
||||
void transRotList(const Common::Array<BodyVertex>& vertices, int32 firstPoint, int32 numPoints, I16Vec3 *destPoints, const IMatrix3x3 *translationMatrix, const IVec3 &angleVec, const IVec3 &destPos);
|
||||
void translateGroup(IMatrix3x3 *targetMatrix, const Common::Array<BodyVertex>& vertices, int32 rotX, int32 rotY, int32 rotZ, const BodyBone &bone, ModelData *modelData);
|
||||
/**
|
||||
* @brief Rotate the given coordinates by the given rotation matrix
|
||||
*/
|
||||
IVec3 rot(const IMatrix3x3 &matrix, int32 x, int32 y, int32 z);
|
||||
|
||||
IVec3 _cameraPos; // CameraX, CameraY, CameraZ
|
||||
IVec3 _projectionCenter{320, 200, 0}; // XCentre, YCentre, IsoScale
|
||||
|
||||
int32 _kFactor = 128;
|
||||
int32 _lFactorX = 1024;
|
||||
int32 _lFactorY = 840;
|
||||
|
||||
IMatrix3x3 _matrixWorld; // LMatriceWorld
|
||||
IMatrix3x3 _matricesTable[30 + 1];
|
||||
IVec3 _normalLight; // NormalXLight, NormalYLight, NormalZLight
|
||||
IVec3 _cameraRot; // CameraXr, CameraYr, CameraZr
|
||||
|
||||
RenderCommand _renderCmds[1000];
|
||||
/**
|
||||
* @brief Raw buffer for holding the render commands. This is a type followed by the command data
|
||||
* that is needed to render the primitive.
|
||||
*/
|
||||
uint8 _renderCoordinatesBuffer[10000]{0};
|
||||
ComputedVertex _clippedPolygonVertices1[128];
|
||||
ComputedVertex _clippedPolygonVertices2[128];
|
||||
|
||||
int16* _tabVerticG = nullptr;
|
||||
int16* _tabVerticD = nullptr;
|
||||
int16* _tabCoulG = nullptr;
|
||||
int16* _tabCoulD = nullptr;
|
||||
int16* _taby0 = nullptr;
|
||||
int16* _taby1 = nullptr;
|
||||
int16* _tabx0 = nullptr; // also _tabCoulG
|
||||
int16* _tabx1 = nullptr; // also _tabCoulD
|
||||
|
||||
bool _typeProj = TYPE_3D;
|
||||
|
||||
void svgaPolyCopper(int16 vtop, int16 vbottom, uint16 color) const;
|
||||
void svgaPolyBopper(int16 vtop, int16 vbottom, uint16 color) const;
|
||||
void svgaPolyTriste(int16 vtop, int16 vbottom, uint16 color) const;
|
||||
void svgaPolyTele(int16 vtop, int16 vbottom, uint16 color) const;
|
||||
void svgaPolyTrans(int16 vtop, int16 vbottom, uint16 color) const;
|
||||
void svgaPolyTrame(int16 vtop, int16 vbottom, uint16 color) const;
|
||||
void svgaPolyGouraud(int16 vtop, int16 vbottom) const;
|
||||
void svgaPolyDith(int16 vtop, int16 vbottom) const;
|
||||
void svgaPolyMarbre(int16 vtop, int16 vbottom, uint16 color) const;
|
||||
void svgaPolyTriche(int16 vtop, int16 vbottom, uint16 color) const;
|
||||
bool computePoly(int16 polyRenderType, const ComputedVertex *vertices, int32 numVertices, int16 &vtop, int16 &vbottom);
|
||||
|
||||
const RenderCommand *depthSortRenderCommands(int32 numOfPrimitives);
|
||||
uint8 *preparePolygons(const Common::Array<BodyPolygon>& polygons, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData);
|
||||
uint8 *prepareSpheres(const Common::Array<BodySphere>& spheres, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData);
|
||||
uint8 *prepareLines(const Common::Array<BodyLine>& lines, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData);
|
||||
|
||||
void flipMatrix();
|
||||
|
||||
void fillTextPolyNoClip(int32 top, int32 bottom, const uint8 *holomapImage, uint32 holomapImageSize);
|
||||
void fillHolomapTriangle(int16 *pDest, int32 x1, int32 y1, int32 x2, int32 y2);
|
||||
void fillHolomapTriangles(const ComputedVertex &vertex1, const ComputedVertex &vertex2, const ComputedVertex &texCoord1, const ComputedVertex &texCoord2, int32 &top, int32 &bottom);
|
||||
|
||||
// ClipGauche, ClipDroite, ClipHaut, ClipBas
|
||||
int16 leftClip(int16 polyRenderType, ComputedVertex** offTabPoly, int32 numVertices);
|
||||
int16 rightClip(int16 polyRenderType, ComputedVertex** offTabPoly, int32 numVertices);
|
||||
int16 topClip(int16 polyRenderType, ComputedVertex** offTabPoly, int32 numVertices);
|
||||
int16 bottomClip(int16 polyRenderType, ComputedVertex** offTabPoly, int32 numVertices);
|
||||
int32 computePolyMinMax(int16 polyRenderType, ComputedVertex **offTabPoly, int32 numVertices, int16 &vtop, int16 &vbottom);
|
||||
public:
|
||||
Renderer(TwinEEngine *engine);
|
||||
~Renderer();
|
||||
|
||||
void init(int32 w, int32 h);
|
||||
|
||||
void setCameraRotation(int32 x, int32 y, int32 z);
|
||||
|
||||
/**
|
||||
* Calculate offset for the side and forward distances by the given angle of an actor
|
||||
* @param side Actor current X coordinate
|
||||
* @param forward Actor current Z coordinate
|
||||
* @param angle Actor angle to rotate
|
||||
*/
|
||||
IVec2 rotate(int32 side, int32 forward, int32 angle) const;
|
||||
|
||||
void setLightVector(int32 angleX, int32 angleY, int32 angleZ);
|
||||
IVec3 longWorldRot(int32 x, int32 y, int32 z);
|
||||
|
||||
IVec3 worldRotatePoint(const IVec3& vec);
|
||||
|
||||
void fillVertices(int16 vtop, int16 vbottom, uint8 renderType, uint16 color);
|
||||
void renderPolygons(const CmdRenderPolygon &polygon, ComputedVertex *vertices);
|
||||
|
||||
inline IVec3 projectPoint(const IVec3& pos) { // ProjettePoint
|
||||
return projectPoint(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
void projIso(IVec3 &pos, int32 x, int32 y, int32 z);
|
||||
|
||||
IVec3 projectPoint(int32 cX, int32 cY, int32 cZ);
|
||||
|
||||
void setFollowCamera(int32 transPosX, int32 transPosY, int32 transPosZ, int32 cameraAlpha, int32 cameraBeta, int32 cameraGamma, int32 cameraZoom);
|
||||
void setPosCamera(int32 x, int32 y, int32 z);
|
||||
IVec3 setAngleCamera(int32 alpha, int32 beta, int32 gamma);
|
||||
IVec3 setInverseAngleCamera(int32 alpha, int32 beta, int32 gamma);
|
||||
|
||||
inline IVec3 setBaseRotation(const IVec3 &rot) {
|
||||
return setAngleCamera(rot.x, rot.y, rot.z);
|
||||
}
|
||||
|
||||
void setProjection(int32 x, int32 y, int32 depthOffset, int32 scaleX, int32 scaleY);
|
||||
void setIsoProjection(int32 x, int32 y, int32 scale);
|
||||
|
||||
bool affObjetIso(int32 x, int32 y, int32 z, int32 angleX, int32 angleY, int32 angleZ, const BodyData &bodyData, Common::Rect &modelRect);
|
||||
|
||||
inline bool renderIsoModel(const IVec3 &pos, int32 angleX, int32 angleY, int32 angleZ, const BodyData &bodyData, Common::Rect &modelRect) {
|
||||
return affObjetIso(pos.x, pos.y, pos.z, angleX, angleY, angleZ, bodyData, modelRect);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param angle A value of @c -1 means that the model is automatically rotated
|
||||
*/
|
||||
void renderBehaviourModel(int32 boxLeft, int32 boxTop, int32 boxRight, int32 boxBottom, int32 y, int32 angle, const BodyData &bodyData, RealValue &move);
|
||||
/**
|
||||
* @param angle A value of @c -1 means that the model is automatically rotated
|
||||
*/
|
||||
void drawObj3D(const Common::Rect &rect, int32 y, int32 angle, const BodyData &bodyData, RealValue &move);
|
||||
|
||||
/**
|
||||
* @brief Render an inventory item
|
||||
*/
|
||||
void draw3dObject(int32 x, int32 y, const BodyData &bodyData, int32 angle, int32 cameraZoom);
|
||||
|
||||
void asmTexturedTriangleNoClip(const ComputedVertex vertexCoordinates[3], const ComputedVertex textureCoordinates[3], const uint8 *holomapImage, uint32 holomapImageSize);
|
||||
|
||||
inline IVec3 getCameraPosition() const {
|
||||
return _cameraPos;
|
||||
}
|
||||
|
||||
inline IVec3 getCameraRotation() const {
|
||||
return _cameraRot;
|
||||
}
|
||||
|
||||
inline int32 getLFactorX() const {
|
||||
return _lFactorX;
|
||||
}
|
||||
|
||||
inline int32 getLFactorY() const {
|
||||
return _lFactorY;
|
||||
}
|
||||
};
|
||||
|
||||
inline void Renderer::setCameraRotation(int32 x, int32 y, int32 z) {
|
||||
_cameraRot.x = x;
|
||||
_cameraRot.y = y;
|
||||
_cameraRot.z = z;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
287
engines/twine/renderer/screens.cpp
Normal file
287
engines/twine/renderer/screens.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/renderer/screens.h"
|
||||
#include "common/file.h"
|
||||
#include "common/str.h"
|
||||
#include "common/system.h"
|
||||
#include "common/util.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/palette.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/bmp.h"
|
||||
#include "image/image_decoder.h"
|
||||
#include "image/png.h"
|
||||
#include "twine/audio/music.h"
|
||||
#include "twine/resources/hqr.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/shared.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
int32 Screens::mapLba2Palette(int32 palIndex) {
|
||||
static const int32 palettes[] {
|
||||
RESSHQR_MAINPAL,
|
||||
-1, // TODO: current palette
|
||||
RESSHQR_BLACKPAL,
|
||||
RESSHQR_ECLAIRPAL
|
||||
};
|
||||
if (palIndex < 0 || palIndex >= ARRAYSIZE(palettes)) {
|
||||
return -1;
|
||||
}
|
||||
return palettes[palIndex];
|
||||
}
|
||||
|
||||
bool Screens::adelineLogo() {
|
||||
_engine->_music->playMidiFile(31);
|
||||
|
||||
// TODO: whiteFade and fadeWhiteToPal
|
||||
return loadImageDelay(_engine->_resources->adelineLogo(), 7);
|
||||
}
|
||||
|
||||
void Screens::loadMenuImage(bool fadeIn) {
|
||||
loadImage(_engine->_resources->menuBackground(), fadeIn);
|
||||
_engine->_workVideoBuffer.blitFrom(_engine->_frontVideoBuffer);
|
||||
}
|
||||
|
||||
void Screens::loadCustomPalette(const TwineResource &resource) {
|
||||
if (!HQR::getPaletteEntry(_palettePcx, resource)) {
|
||||
error("Failed to get palette entry for custom palette: %s:%d", resource.hqr, resource.index);
|
||||
}
|
||||
|
||||
if (_palettePcx.size() != NUMOFCOLORS) {
|
||||
warning("Unexpected palette size %s:%i", resource.hqr, resource.index);
|
||||
}
|
||||
}
|
||||
|
||||
void Screens::loadImage(TwineImage image, bool fadeIn) {
|
||||
Graphics::ManagedSurface& src = _engine->_imageBuffer;
|
||||
if (HQR::getEntry((uint8 *)src.getPixels(), image.image) == 0) {
|
||||
warning("Failed to load image with index %i", image.image.index);
|
||||
return;
|
||||
}
|
||||
debugC(1, TwinE::kDebugResources, "Load image: %i", image.image.index);
|
||||
Graphics::ManagedSurface& target = _engine->_frontVideoBuffer;
|
||||
target.blitFrom(src, src.getBounds(), target.getBounds());
|
||||
const Graphics::Palette *pal = &_ptrPal;
|
||||
if (image.palette.index != -1) {
|
||||
loadCustomPalette(image.palette);
|
||||
pal = &_palettePcx;
|
||||
}
|
||||
if (fadeIn) {
|
||||
fadeToPal(*pal);
|
||||
} else {
|
||||
_engine->setPalette(*pal);
|
||||
}
|
||||
}
|
||||
|
||||
bool Screens::loadImageDelay(TwineImage image, int32 seconds) {
|
||||
loadImage(image);
|
||||
if (_engine->delaySkip(1000 * seconds)) {
|
||||
fadePal(0, 0, 0, _palettePcx, 100);
|
||||
return true;
|
||||
}
|
||||
fadeToBlack(_palettePcx);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class ImageDecoder>
|
||||
static bool loadImageDelayViaDecoder(TwinEEngine *engine, const Common::Path &fileName, int32 seconds) {
|
||||
ImageDecoder decoder;
|
||||
Common::File fileHandle;
|
||||
if (!fileHandle.open(fileName)) {
|
||||
warning("Failed to open %s", fileName.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
if (!decoder.loadStream(fileHandle)) {
|
||||
warning("Failed to load %s", fileName.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
const Graphics::Surface *src = decoder.getSurface();
|
||||
if (src == nullptr) {
|
||||
warning("Failed to decode %s", fileName.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
Graphics::ManagedSurface &target = engine->_frontVideoBuffer;
|
||||
Common::Rect rect(src->w, src->h);
|
||||
if (!decoder.hasPalette()) {
|
||||
uint8 pal[Graphics::PALETTE_SIZE];
|
||||
engine->_frontVideoBuffer.getPalette(pal, 0, 256);
|
||||
Graphics::Surface *source = decoder.getSurface()->convertTo(target.format, nullptr, 0, pal, 256);
|
||||
target.blitFrom(*source, rect, target.getBounds());
|
||||
source->free();
|
||||
delete source;
|
||||
} else {
|
||||
engine->setPalette(0, decoder.getPalette().size(), decoder.getPalette().data());
|
||||
target.blitFrom(*src, rect, target.getBounds());
|
||||
}
|
||||
if (engine->delaySkip(1000 * seconds)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Screens::loadBitmapDelay(const char *image, int32 seconds) {
|
||||
Common::Path path(image);
|
||||
Common::String filename = path.baseName();
|
||||
size_t extPos = filename.rfind(".");
|
||||
if (extPos == Common::String::npos) {
|
||||
warning("Failed to extract extension %s", image);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ImageLoader {
|
||||
const char *extension;
|
||||
bool (*loadImageDelay)(TwinEEngine *engine, const Common::Path &fileName, int32 seconds);
|
||||
};
|
||||
|
||||
static const ImageLoader s_imageLoaders[] = {
|
||||
{ "bmp", loadImageDelayViaDecoder<Image::BitmapDecoder> },
|
||||
{ "png", loadImageDelayViaDecoder<Image::PNGDecoder> },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
const Common::String &ext = filename.substr(extPos + 1);
|
||||
for (const ImageLoader *loader = s_imageLoaders; loader->extension; ++loader) {
|
||||
if (!scumm_stricmp(loader->extension, ext.c_str())) {
|
||||
return loader->loadImageDelay(_engine, path, seconds);
|
||||
}
|
||||
}
|
||||
warning("Failed to find suitable image handler %s", image);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Screens::fadePal(uint8 r, uint8 g, uint8 b, const Graphics::Palette &rgbaPal, int32 intensity) {
|
||||
Graphics::Palette pal{NUMOFCOLORS};
|
||||
|
||||
for (int32 i = 0; i < NUMOFCOLORS; i++) {
|
||||
byte rIn, gIn, bIn;
|
||||
rgbaPal.get(i, rIn, gIn, bIn);
|
||||
const byte newR = ruleThree32(r, rIn, 100, intensity);
|
||||
const byte newG = ruleThree32(g, gIn, 100, intensity);
|
||||
const byte newB = ruleThree32(b, bIn, 100, intensity);
|
||||
pal.set(i, newR, newG, newB);
|
||||
}
|
||||
|
||||
_engine->setPalette(pal);
|
||||
_engine->_frontVideoBuffer.update();
|
||||
}
|
||||
|
||||
void Screens::fadeToBlack(const Graphics::Palette &ptrpal) {
|
||||
if (_flagBlackPal) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 n = 100; n >= 0; n -= 2) {
|
||||
FrameMarker frame(_engine, 66); // VSync()
|
||||
fadePal(0, 0, 0, ptrpal, n);
|
||||
}
|
||||
|
||||
_flagBlackPal = true;
|
||||
}
|
||||
|
||||
void Screens::whiteFade() {
|
||||
Graphics::Palette workpal{NUMOFCOLORS};
|
||||
|
||||
for (int32 n = 0; n <= 255; n++) {
|
||||
FrameMarker frame(_engine, 66); // VSync()
|
||||
for (int i = 0; i < NUMOFCOLORS; i++) {
|
||||
workpal.set(i, n, n, n);
|
||||
}
|
||||
|
||||
_engine->setPalette(workpal);
|
||||
_engine->_frontVideoBuffer.update();
|
||||
}
|
||||
}
|
||||
|
||||
void Screens::fadeWhiteToPal(const Graphics::Palette &ptrpal) {
|
||||
for (int32 n = 0; n <= 100; ++n) {
|
||||
FrameMarker frame(_engine, 66); // VSync()
|
||||
fadePal(255, 255, 255, ptrpal, n);
|
||||
}
|
||||
}
|
||||
|
||||
void Screens::fadeToPal(const Graphics::Palette &ptrpal) {
|
||||
for (int32 i = 0; i <= 100; i += 3) {
|
||||
FrameMarker frame(_engine, 66); // VSync()
|
||||
fadePal(0, 0, 0, ptrpal, i);
|
||||
}
|
||||
|
||||
_engine->setPalette(ptrpal);
|
||||
|
||||
_flagBlackPal = false;
|
||||
}
|
||||
|
||||
void Screens::setBlackPal() {
|
||||
Graphics::Palette workPal(NUMOFCOLORS);
|
||||
|
||||
_engine->setPalette(workPal);
|
||||
|
||||
_flagBlackPal = true;
|
||||
}
|
||||
|
||||
void Screens::fadePalToPal(const Graphics::Palette &ptrpal, const Graphics::Palette &ptrpal2) {
|
||||
Graphics::Palette workpal{NUMOFCOLORS};
|
||||
|
||||
for (int m = 0; m < 100; ++m) {
|
||||
FrameMarker frame(_engine, 66); // VSync()
|
||||
for (int32 i = 0; i < NUMOFCOLORS; i++) {
|
||||
byte r1, g1, b1;
|
||||
ptrpal.get(i, r1, g1, b1);
|
||||
|
||||
byte r2, g2, b2;
|
||||
ptrpal2.get(i, r2, g2, b2);
|
||||
|
||||
byte newR = ruleThree32(r1, r2, 100, m);
|
||||
byte newG = ruleThree32(g1, g2, 100, m);
|
||||
byte newB = ruleThree32(b1, b2, 100, m);
|
||||
workpal.set(i, newR, newG, newB);
|
||||
}
|
||||
|
||||
_engine->setPalette(workpal);
|
||||
_engine->_frontVideoBuffer.update();
|
||||
}
|
||||
}
|
||||
|
||||
void Screens::fadeToRed(const Graphics::Palette &ptrpal) {
|
||||
for (int32 i = 100; i >= 0; i -= 2) {
|
||||
FrameMarker frame(_engine, 66); // VSync()
|
||||
fadePal(255, 0, 0, ptrpal, i);
|
||||
}
|
||||
}
|
||||
|
||||
void Screens::fadeRedToPal(const Graphics::Palette &ptrpal) {
|
||||
for (int32 i = 0; i <= 100; i += 2) {
|
||||
FrameMarker frame(_engine, 66); // VSync()
|
||||
fadePal(255, 0, 0, ptrpal, i);
|
||||
}
|
||||
}
|
||||
|
||||
void Screens::copyScreen(const Graphics::ManagedSurface &source, Graphics::ManagedSurface &destination) {
|
||||
destination.blitFrom(source);
|
||||
}
|
||||
|
||||
void Screens::clearScreen() {
|
||||
_engine->_frontVideoBuffer.clear(0);
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
151
engines/twine/renderer/screens.h
Normal file
151
engines/twine/renderer/screens.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_RENDERER_SCREENS_H
|
||||
#define TWINE_RENDERER_SCREENS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/palette.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class TwinEEngine;
|
||||
class Screens {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
/**
|
||||
* Adjust palette intensity
|
||||
* @param r red component of color
|
||||
* @param g green component of color
|
||||
* @param b blue component of color
|
||||
* @param palette palette to adjust
|
||||
* @param intensity intensity value to adjust
|
||||
*/
|
||||
void fadePal(uint8 r, uint8 g, uint8 b, const Graphics::Palette &palette, int32 intensity);
|
||||
|
||||
public:
|
||||
Screens(TwinEEngine *engine) : _engine(engine) {}
|
||||
|
||||
int32 mapLba2Palette(int32 palIndex);
|
||||
|
||||
/** main palette */
|
||||
Graphics::Palette _ptrPal{0};
|
||||
Graphics::Palette _palettePcx{0};
|
||||
|
||||
/** flag to check in the game palette was changed */
|
||||
bool _flagBlackPal = false;
|
||||
|
||||
/** flag to check if the main flag is locked */
|
||||
bool _flagFade = false;
|
||||
|
||||
/** flag to check if we are using a different palette than the main one */
|
||||
bool _flagPalettePcx = false;
|
||||
|
||||
/** Load and display Adeline Logo */
|
||||
bool adelineLogo();
|
||||
|
||||
/**
|
||||
* Load a custom palette
|
||||
* @param index \a RESS.HQR entry index (starting from 0)
|
||||
*/
|
||||
void loadCustomPalette(const TwineResource &resource);
|
||||
|
||||
/** Load and display Main Menu image */
|
||||
void loadMenuImage(bool fadeIn = true);
|
||||
|
||||
/**
|
||||
* Load and display a particularly image on \a RESS.HQR file with cross fade effect
|
||||
* @param index \a RESS.HQR entry index (starting from 0)
|
||||
* @param paletteIndex \a RESS.HQR entry index of the palette for the given image. This is often the @c index + 1
|
||||
* @param fadeIn if we fade in before using the palette
|
||||
*/
|
||||
void loadImage(TwineImage image, bool fadeIn = true);
|
||||
|
||||
/**
|
||||
* Load and display a particularly image on \a RESS.HQR file with cross fade effect and delay
|
||||
* @param index \a RESS.HQR entry index (starting from 0)
|
||||
* @param paletteIndex \a RESS.HQR entry index of the palette for the given image. This is often the @c index + 1
|
||||
* @param seconds number of seconds to delay
|
||||
* @return @c true if aborted
|
||||
*/
|
||||
bool loadImageDelay(TwineImage image, int32 seconds);
|
||||
|
||||
bool loadBitmapDelay(const char *image, int32 seconds);
|
||||
|
||||
/**
|
||||
* Adjust between two palettes
|
||||
* @param pal1 palette from adjust
|
||||
* @param pal2 palette to adjust
|
||||
*/
|
||||
void fadePalToPal(const Graphics::Palette &pal1, const Graphics::Palette &pal2);
|
||||
|
||||
/**
|
||||
* Fade image to black
|
||||
* @param palette current palette to fade
|
||||
*/
|
||||
void fadeToBlack(const Graphics::Palette &palette);
|
||||
void fadeWhiteToPal(const Graphics::Palette &ptrpal);
|
||||
|
||||
/**
|
||||
* Fade image with another palette source
|
||||
* @param palette current palette to fade
|
||||
*/
|
||||
void fadeToPal(const Graphics::Palette &palette);
|
||||
|
||||
/** Fade black palette to white palette */
|
||||
void whiteFade();
|
||||
|
||||
/** Resets both in-game and sdl palettes */
|
||||
void setBlackPal();
|
||||
|
||||
/**
|
||||
* Fade palette to red palette
|
||||
* @param palette current palette to fade
|
||||
*/
|
||||
void fadeToRed(const Graphics::Palette &palette);
|
||||
|
||||
/**
|
||||
* Fade red to palette
|
||||
* @param palette current palette to fade
|
||||
*/
|
||||
void fadeRedToPal(const Graphics::Palette &palette);
|
||||
|
||||
/**
|
||||
* Copy a determinate screen buffer to another
|
||||
* @param source screen buffer
|
||||
* @param destination screen buffer
|
||||
*/
|
||||
void copyScreen(const Graphics::ManagedSurface &source, Graphics::ManagedSurface &destination);
|
||||
|
||||
/** Clear front buffer screen */
|
||||
void clearScreen(); // Cls()
|
||||
|
||||
/** Init palettes */
|
||||
void initPalettes();
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
1064
engines/twine/renderer/shadeangletab.h
Normal file
1064
engines/twine/renderer/shadeangletab.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user