Skip to content

Commit

Permalink
SCUMM: (FM-TOWNS) - add smooth scrolling
Browse files Browse the repository at this point in the history
This is mostly based on ZAK and MI2 disasm (and a bit LOOM). For MI1 and INDY4 I have at least checked with the FM-Towns UNZ emulator to ensure that they actually have the smooth scrolling.

The smooth scrolling is a bit tricky with regard to the timing. The scrolling tends to be a bit too slow for the engine, since it happens at a speed of 1 pixel per 60Hz tick, while the engine sometimes writes one or more new 8 pixel strips at the same time. So the scrolling often has to catch up to the engine. The original has a compensation mechanism which I tried to adapt. We'll see if it needs more fine tuning...

The Loom intro even glitches in the UNZ FM-Towns emulator due overflowing the scrolling surface (and causing a wraparound). But I made a fix for that.

Since we do have a bug report about the speed of the ZAK intro (and the new scrolling code does affect the timing) I have run the ZAK intro in ScummVM and in the FM-Towns UNZ emulator (with i80386DX, 16 MHz setting) side by side and they seem totally in sync. If I reduce the MHz setting in the emulator to 10 MHz on the emulator the intro will play slower, but still not play through the whole song. With 8 MHz I get close to finishing the song, but this seems to be almost to slow to run the game properly. So I'll leave that bug ticket open...
  • Loading branch information
athrxx committed Feb 11, 2021
1 parent e097625 commit 4f9ae44
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 160 deletions.
76 changes: 36 additions & 40 deletions engines/scumm/gfx.cpp
Expand Up @@ -372,6 +372,11 @@ void ScummEngine::initScreens(int b, int h) {
_screenH = h;

_gdi->init();

#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
// The right most visible strip for FM-Towns smooth scrolling
_scrollFeedStrips[0] = _gdi->_numStrips - 1;
#endif
}

void ScummEngine::initVirtScreen(VirtScreenNumber slot, int top, int width, int height, bool twobufs,
Expand Down Expand Up @@ -661,13 +666,13 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
assert(IS_ALIGNED(text, 4));
assert(0 == (width & 3));

// Compose the text over the game graphics
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_game.platform == Common::kPlatformFMTowns) {
towns_drawStripToScreen(vs, x, y, x, top, width, height);
return;
} else
#endif
// Compose the text over the game graphics
if (_outputPixelFormat.bytesPerPixel == 2) {
const byte *srcPtr = (const byte *)src;
const byte *textPtr = (byte *)_textSurface.getBasePtr(x * m, y * m);
Expand Down Expand Up @@ -965,10 +970,10 @@ void ScummEngine::redrawBGAreas() {
diff = camera._cur.x - camera._last.x;
if (!_fullRedraw && diff == 8) {
val = -1;
redrawBGStrip(_gdi->_numStrips - 1, 1);
scrollLeft();
} else if (!_fullRedraw && diff == -8) {
val = +1;
redrawBGStrip(0, 1);
scrollRight();
} else if (_fullRedraw || diff != 0) {
if (_game.version <= 5) {
((ScummEngine_v5 *)this)->clearFlashlight();
Expand Down Expand Up @@ -3769,7 +3774,7 @@ void ScummEngine::fadeIn(int effect) {
_screenEffectFlag = true;
return;
}

towns_waitForScroll(0);
updatePalette();

switch (effect) {
Expand Down Expand Up @@ -3815,8 +3820,9 @@ void ScummEngine::fadeIn(int effect) {
}

void ScummEngine::fadeOut(int effect) {
VirtScreen *vs = &_virtscr[kMainVirtScreen];
towns_waitForScroll(0);

VirtScreen *vs = &_virtscr[kMainVirtScreen];
vs->setDirtyRange(0, 0);
if (_game.version < 7)
camera._last.x = camera._cur.x;
Expand Down Expand Up @@ -3859,10 +3865,7 @@ void ScummEngine::fadeOut(int effect) {
// Just blit screen 0 to the display (i.e. display will be black)
vs->setDirtyRange(0, vs->h);
updateDirtyScreen(kMainVirtScreen);
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_townsScreen)
_townsScreen->update();
#endif
towns_updateGfx();
break;
case 134:
dissolveEffect(1, 1);
Expand Down Expand Up @@ -4062,6 +4065,13 @@ void ScummEngine::dissolveEffect(int width, int height) {
}

void ScummEngine::scrollEffect(int dir) {
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
// The FM-Towns versions use smooth scrolling here, but only for left and right.
if (_game.platform == Common::kPlatformFMTowns && dir > 1) {
towns_scriptScrollEffect((dir & 1) * 2 - 1);
return;
}
#endif
VirtScreen *vs = &_virtscr[kMainVirtScreen];

int x, y;
Expand All @@ -4084,7 +4094,7 @@ void ScummEngine::scrollEffect(int dir) {
//up
y = 1 + step;
while (y < vs->h) {
moveScreen(0, -step, vs->h);
moveScreen(0, -step * m, vs->h * m);
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_townsScreen) {
towns_drawStripToScreen(vs, 0, vs->topline + vs->h - step, 0, y - step, vs->w, step);
Expand All @@ -4107,7 +4117,7 @@ void ScummEngine::scrollEffect(int dir) {
// down
y = 1 + step;
while (y < vs->h) {
moveScreen(0, step, vs->h);
moveScreen(0, step * m, vs->h * m);
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_townsScreen) {
towns_drawStripToScreen(vs, 0, vs->topline, 0, vs->h - y, vs->w, step);
Expand All @@ -4130,21 +4140,14 @@ void ScummEngine::scrollEffect(int dir) {
// left
x = 1 + step;
while (x < vs->w) {
moveScreen(-step, 0, vs->h);
moveScreen(-step * m, 0, vs->h * m);

#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_townsScreen) {
towns_drawStripToScreen(vs, vs->w - step, vs->topline, x - step, 0, step, vs->h);
} else
#endif
{
src = vs->getPixels(x - step, 0);
_system->copyRectToScreen(src,
vsPitch,
(vs->w - step) * m, 0,
step * m, vs->h * m);
_system->updateScreen();
}
src = vs->getPixels(x - step, 0);
_system->copyRectToScreen(src,
vsPitch,
(vs->w - step) * m, 0,
step * m, vs->h * m);
_system->updateScreen();

waitForTimer(delay);
x += step;
Expand All @@ -4154,21 +4157,14 @@ void ScummEngine::scrollEffect(int dir) {
// right
x = 1 + step;
while (x < vs->w) {
moveScreen(step, 0, vs->h);

#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_townsScreen) {
towns_drawStripToScreen(vs, 0, vs->topline, vs->w - x, 0, step, vs->h);
} else
#endif
{
src = vs->getPixels(vs->w - x, 0);
_system->copyRectToScreen(src,
vsPitch,
0, 0,
step, vs->h);
_system->updateScreen();
}
moveScreen(step * m, 0, vs->h * m);

src = vs->getPixels(vs->w - x, 0);
_system->copyRectToScreen(src,
vsPitch,
0, 0,
step, vs->h);
_system->updateScreen();

waitForTimer(delay);
x += step;
Expand Down
31 changes: 18 additions & 13 deletions engines/scumm/gfx.h
Expand Up @@ -456,28 +456,28 @@ class GdiHE16bit : public GdiHE {
#endif

#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
// Helper class for FM-Towns output (required for specific hardware effects like
// switching graphics layers on and off).
// Helper class for FM-Towns output (required for specific hardware effects like switching graphics layers on and off).
class TownsScreen {
public:
TownsScreen(OSystem *system, int width, int height, Graphics::PixelFormat &format);
~TownsScreen();

void setupLayer(int layer, int width, int height, int numCol, void *srcPal = 0);
void setupLayer(int layer, int width, int height, int scaleW, int scaleH, int numCol, void *srcPal = 0);
void clearLayer(int layer);
void fillLayerRect(int layer, int x, int y, int w, int h, int col);
//void copyRectToLayer(int layer, int x, int y, int w, int h, const uint8 *src);

uint8 *getLayerPixels(int layer, int x, int y);
int getLayerPitch(int layer);
int getLayerHeight(int layer);
int getLayerBpp(int layer);
int getLayerScaleW(int layer);
int getLayerScaleH(int layer);

void addDirtyRect(int x, int y, int w, int h);
void toggleLayers(int flag);
void toggleLayers(int flags);
void scrollLayers(int flags, int offset);
void update();
bool isScrolling(int direction, int threshold = 0) const { return (direction == 0) ? _scrollRemainder != threshold : (direction == 1 ? _scrollRemainder > threshold : _scrollRemainder < threshold); }

uint8 *getLayerPixels(int layer, int x, int y) const;
int getLayerPitch(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].pitch : 0; }
int getLayerWidth(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].width : 0; }
int getLayerHeight(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].height : 0; }
int getLayerBpp(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].bpp : 0; }
int getLayerScaleW(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].scaleW : 0; }
int getLayerScaleH(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].scaleH : 0; }

private:
void updateOutputBuffer();
Expand All @@ -488,9 +488,12 @@ class TownsScreen {
uint8 *pixels;
uint8 *palette;
int pitch;
int width;
int height;
int modW;
int bpp;
int numCol;
int hScroll;
uint8 scaleW;
uint8 scaleH;
bool onBottom;
Expand All @@ -507,6 +510,8 @@ class TownsScreen {
int _height;
int _width;
int _pitch;
uint16 _scrollOffset;
int _scrollRemainder;
Graphics::PixelFormat _pixelFormat;

int _numDirtyRects;
Expand Down

0 comments on commit 4f9ae44

Please sign in to comment.