diff options
Diffstat (limited to 'engines/mohawk/riven_graphics.cpp')
-rw-r--r-- | engines/mohawk/riven_graphics.cpp | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp index d5cb3536a1..573eddf7d8 100644 --- a/engines/mohawk/riven_graphics.cpp +++ b/engines/mohawk/riven_graphics.cpp @@ -57,6 +57,7 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm _creditsPos = 0; _transitionSpeed = 0; + _fliesEffect = nullptr; } RivenGraphics::~RivenGraphics() { @@ -65,6 +66,7 @@ RivenGraphics::~RivenGraphics() { _mainScreen->free(); delete _mainScreen; delete _bitmapDecoder; + delete _fliesEffect; } MohawkSurface *RivenGraphics::decodeImage(uint16 id) { @@ -447,4 +449,465 @@ void RivenGraphics::applyScreenUpdate(bool force) { } } +void RivenGraphics::setFliesEffect(uint16 count, bool fireflies) { + delete _fliesEffect; + _fliesEffect = new FliesEffect(_vm, count, fireflies); +} + +void RivenGraphics::clearFliesEffect() { + delete _fliesEffect; + _fliesEffect = nullptr; +} + +void RivenGraphics::runFliesEffect() { + if (_fliesEffect) { + _fliesEffect->update(); + } +} + +Graphics::Surface *RivenGraphics::getBackScreen() { + return _mainScreen; +} + +Graphics::Surface *RivenGraphics::getEffectScreen() { + return _effectScreen; +} + +const FliesEffect::FliesEffectData FliesEffect::_firefliesParameters = { + true, + true, + true, + true, + 3.0, + 0.7, + 40, + 2.0, + 1.0, + 8447718, + 30, + 10 +}; + +const FliesEffect::FliesEffectData FliesEffect::_fliesParameters = { + false, + false, + false, + true, + 8.0, + 3.0, + 80, + 3.0, + 1.0, + 661528, + 30, + 10 +}; + +FliesEffect::FliesEffect(MohawkEngine_Riven *vm, uint16 count, bool fireflies) : + _vm(vm) { + + _effectSurface = _vm->_gfx->getEffectScreen(); + _backSurface = _vm->_gfx->getBackScreen(); + _gameRect = Common::Rect(608, 392); + + if (fireflies) { + _parameters = &_firefliesParameters; + } else { + _parameters = &_fliesParameters; + } + + _updatePeriodMs = 66; + _nextUpdateTime = _vm->_system->getMillis(); + + initFlies(count); +} + +FliesEffect::~FliesEffect() { + +} + +void FliesEffect::initFlies(uint16 count) { + _fly.resize(count); + for (uint16 i = 0; i < _fly.size(); i++) { + initFlyRandomPosition(i); + } +} + +void FliesEffect::initFlyRandomPosition(uint index) { + int posX = _vm->_rnd->getRandomNumber(_gameRect.right - 3); + int posY = _vm->_rnd->getRandomNumber(_gameRect.bottom - 3); + + if (posY < 100) { + posY = 100; + } + + initFlyAtPosition(index, posX, posY, 15); +} + +int FliesEffect::randomBetween(int min, int max) { + return _vm->_rnd->getRandomNumber(max - min) + min; +} + +void FliesEffect::initFlyAtPosition(uint index, int posX, int posY, int posZ) { + FliesEffectEntry &fly = _fly[index]; + + fly.posX = posX; + fly.posXFloat = posX; + fly.posY = posY; + fly.posYFloat = posY; + fly.posZ = posZ; + fly.light = true; + + fly.framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration); + + fly.hasBlur = false; + fly.directionAngleRad = randomBetween(0, 300) / 100.0f; + fly.directionAngleRadZ = randomBetween(0, 300) / 100.0f; + fly.speed = randomBetween(0, 100) / 100.0f; +} + +void FliesEffect::update() { + if (_nextUpdateTime <= _vm->_system->getMillis()) { + _nextUpdateTime = _updatePeriodMs + _vm->_system->getMillis(); + + updateFlies(); + draw(); + updateScreen(); + } +} + +void FliesEffect::updateFlies() { + for (uint i = 0; i < _fly.size(); i++) { + updateFlyPosition(i); + + if (_fly[i].posX < 1 || _fly[i].posX > _gameRect.right - 4 || _fly[i].posY > _gameRect.bottom - 4) { + initFlyRandomPosition(i); + } + + if (_parameters->lightable) { + _fly[i].framesTillLightSwitch--; + + if (_fly[i].framesTillLightSwitch <= 0) { + _fly[i].light = !_fly[i].light; + _fly[i].framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration); + _fly[i].hasBlur = false; + } + } + } +} + +void FliesEffect::updateFlyPosition(uint index) { + FliesEffectEntry &fly = _fly[index]; + + if (fly.directionAngleRad > 2.0f * M_PI) { + fly.directionAngleRad = fly.directionAngleRad - 2.0f * M_PI; + } + if (fly.directionAngleRad < 0.0f) { + fly.directionAngleRad = fly.directionAngleRad + 2.0f * M_PI; + } + if (fly.directionAngleRadZ > 2.0f * M_PI) { + fly.directionAngleRadZ = fly.directionAngleRadZ - 2.0f * M_PI; + } + if (fly.directionAngleRadZ < 0.0f) { + fly.directionAngleRadZ = fly.directionAngleRadZ + 2.0f * M_PI; + } + fly.posXFloat += cos(fly.directionAngleRad) * fly.speed; + fly.posYFloat += sin(fly.directionAngleRad) * fly.speed; + fly.posX = fly.posXFloat; + fly.posY = fly.posYFloat; + selectAlphaMap( + fly.posXFloat - fly.posX >= 0.5, + fly.posYFloat - fly.posY >= 0.5, + &fly.alphaMap, + &fly.width, + &fly.height); + fly.posZFloat += cos(fly.directionAngleRadZ) * (fly.speed / 2.0f); + fly.posZ = fly.posZFloat; + if (_parameters->canBlur && fly.speed > _parameters->blurSpeedTreshold) { + fly.hasBlur = true; + float blurPosXFloat = cos(fly.directionAngleRad + M_PI) * _parameters->blurDistance + fly.posXFloat; + float blurPosYFloat = sin(fly.directionAngleRad + M_PI) * _parameters->blurDistance + fly.posYFloat; + + fly.blurPosX = blurPosXFloat; + fly.blurPosY = blurPosYFloat; + selectAlphaMap( + blurPosXFloat - fly.blurPosX >= 0.5, + blurPosYFloat - fly.blurPosY >= 0.5, + &fly.blurAlphaMap, + &fly.blurWidth, + &fly.blurHeight); + } + if (fly.posY >= 100) { + int maxAngularSpeed = _parameters->maxAcceleration; + if (fly.posZ > 15) { + maxAngularSpeed /= 2; + } + int angularSpeed = randomBetween(-maxAngularSpeed, maxAngularSpeed); + fly.directionAngleRad += angularSpeed / 100.0f; + } else { + // Make the flies go down if they are too high in the screen + int angularSpeed = randomBetween(0, 50); + if (fly.directionAngleRad >= M_PI / 2.0f && fly.directionAngleRad <= 3.0f * M_PI / 2.0f) { + // Going down + fly.directionAngleRad -= angularSpeed / 100.0f; + } else { + // Going up + fly.directionAngleRad += angularSpeed / 100.0f; + } + if (fly.posY < 1) { + initFlyRandomPosition(index); + } + } + if (fly.posZ >= 0) { + int distanceToScreenEdge; + if (fly.posX / 10 >= (_gameRect.right - fly.posX) / 10) { + distanceToScreenEdge = (_gameRect.right - fly.posX) / 10; + } else { + distanceToScreenEdge = fly.posX / 10; + } + if (distanceToScreenEdge > (_gameRect.bottom - fly.posY) / 10) { + distanceToScreenEdge = (_gameRect.bottom - fly.posY) / 10; + } + if (distanceToScreenEdge > 30) { + distanceToScreenEdge = 30; + } + if (fly.posZ <= distanceToScreenEdge) { + fly.directionAngleRadZ += randomBetween(-_parameters->maxAcceleration, _parameters->maxAcceleration) / 100.0f; + } else { + fly.posZ = distanceToScreenEdge; + fly.directionAngleRadZ += M_PI; + } + } else { + fly.posZ = 0; + fly.directionAngleRadZ += M_PI; + } + float minSpeed = _parameters->minSpeed - fly.posZ / 40.0f; + float maxSpeed = _parameters->maxSpeed - fly.posZ / 20.0f; + fly.speed += randomBetween(-_parameters->maxAcceleration, _parameters->maxAcceleration) / 100.0f; + if (fly.speed > maxSpeed) { + fly.speed -= randomBetween(0, 50) / 100.0f; + } + if (fly.speed < minSpeed) { + fly.speed += randomBetween(0, 50) / 100.0f; + } +} + +void FliesEffect::selectAlphaMap(bool horGridOffset, bool vertGridoffset, const uint16 **alphaMap, uint *width, uint *height) { + static const uint16 alpha1[12] = { + 8, 16, 8, + 16, 32, 16, + 8, 16, 8, + 0, 0, 0 + }; + + static const uint16 alpha2[12] = { + 4, 12, 12, 4, + 8, 24, 24, 8, + 4, 12, 12, 4 + }; + + static const uint16 alpha3[12] = { + 4, 8, 4, + 12, 24, 12, + 12, 24, 12, + 4, 8, 4 + }; + + static const uint16 alpha4[16] = { + 2, 6, 6, 2, + 6, 18, 18, 6, + 6, 18, 18, 6, + 2, 6, 6, 2 + }; + + static const uint16 alpha5[12] = { + 4, 8, 4, + 8, 32, 8, + 4, 8, 4, + 0, 0, 0 + }; + + static const uint16 alpha6[12] = { + 2, 6, 6, 2, + 4, 24, 24, 4, + 2, 6, 6, 2 + }; + + static const uint16 alpha7[12] = { + 2, 4, 2, + 6, 24, 6, + 6, 24, 6, + 2, 4, 2 + }; + + static const uint16 alpha8[16] = { + 1, 3, 3, 1, + 3, 18, 18, 3, + 3, 18, 18, 3, + 1, 3, 3, 1 + }; + + struct AlphaMap { + bool horizontalGridOffset; + bool verticalGridOffset; + bool isLarge; + uint16 width; + uint16 height; + const uint16 *pixels; + }; + + static const AlphaMap alphaSelector[] = { + { true, true, true, 4, 4, alpha4 }, + { true, true, false, 4, 4, alpha8 }, + { true, false, true, 4, 3, alpha2 }, + { true, false, false, 4, 3, alpha6 }, + { false, true, true, 3, 4, alpha3 }, + { false, true, false, 3, 4, alpha7 }, + { false, false, true, 3, 3, alpha1 }, + { false, false, false, 3, 3, alpha5 } + }; + + for (uint i = 0; i < ARRAYSIZE(alphaSelector); i++) { + if (alphaSelector[i].horizontalGridOffset == horGridOffset + && alphaSelector[i].verticalGridOffset == vertGridoffset + && alphaSelector[i].isLarge == _parameters->isLarge) { + *alphaMap = alphaSelector[i].pixels; + *width = alphaSelector[i].width; + *height = alphaSelector[i].height; + return; + } + } + + error("Unknown flies alpha map case"); +} + +void FliesEffect::draw() { + const Graphics::PixelFormat format = _effectSurface->format; + + for (uint i = 0; i < _fly.size(); i++) { + FliesEffectEntry &fly = _fly[i]; + uint32 color = _parameters->color32; + if (!fly.light) { + color = _fliesParameters.color32; + } + + bool hoveringBrightBackground = false; + for (uint y = 0; y < fly.height; y++) { + uint16 *pixel = (uint16 *) _effectSurface->getBasePtr(fly.posX, fly.posY + y); + + for (uint x = 0; x < fly.width; x++) { + byte r, g, b; + format.colorToRGB(*pixel, r, g, b); + + if (_parameters->unlightIfTooBright) { + if (r >= 192 || g >= 192 || b >= 192) { + hoveringBrightBackground = true; + } + } + colorBlending(color, r, g, b, fly.alphaMap[fly.width * y + x] - fly.posZ); + + *pixel = format.RGBToColor(r, g, b); + ++pixel; + } + } + + Common::Rect drawRect = Common::Rect(fly.width, fly.height); + drawRect.translate(fly.posX, fly.posY); + addToScreenDirtyRects(drawRect); + addToEffectsDirtyRects(drawRect); + + if (fly.hasBlur) { + for (uint y = 0; y < fly.blurHeight; y++) { + uint16 *pixel = (uint16 *) _effectSurface->getBasePtr(fly.blurPosX, fly.blurPosY + y); + for (uint x = 0; x < fly.blurWidth; x++) { + byte r, g, b; + format.colorToRGB(*pixel, r, g, b); + + colorBlending(color, r, g, b, fly.blurAlphaMap[fly.blurWidth * y + x] - fly.posZ); + + *pixel = format.RGBToColor(r, g, b); + ++pixel; + } + } + + Common::Rect drawRect2 = Common::Rect(fly.blurWidth, fly.blurHeight); + drawRect2.translate(fly.blurPosX, fly.blurPosY); + addToScreenDirtyRects(drawRect2); + addToEffectsDirtyRects(drawRect2); + + fly.hasBlur = false; + } + + if (hoveringBrightBackground) { + fly.hasBlur = false; + if (_parameters->lightable) { + fly.light = false; + fly.framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration); + } + + if (_vm->_rnd->getRandomBit()) { + fly.directionAngleRad += M_PI / 2.0; + } else { + fly.directionAngleRad -= M_PI / 2.0; + } + } + } +} + +void FliesEffect::colorBlending(uint32 flyColor, byte &r, byte &g, byte &b, int alpha) { + alpha = CLIP(alpha, 0, 32); + byte flyR = (flyColor & 0x000000FF) >> 0; + byte flyG = (flyColor & 0x0000FF00) >> 8; + byte flyB = (flyColor & 0x00FF0000) >> 16; + + r = (32 * r + alpha * (flyR - r)) / 32; + g = (32 * g + alpha * (flyG - g)) / 32; + b = (32 * b + alpha * (flyB - b)) / 32; +} + +void FliesEffect::updateScreen() { + for (uint i = 0; i < _screenSurfaceDirtyRects.size(); i++) { + const Common::Rect &rect = _screenSurfaceDirtyRects[i]; + _vm->_system->copyRectToScreen(_effectSurface->getBasePtr(rect.left, rect.top), + _effectSurface->pitch, rect.left, rect.top, + rect.width(), rect.height() + ); + } + _screenSurfaceDirtyRects.clear(); + + restoreEffectsSurface(); +} + +void FliesEffect::addToScreenDirtyRects(const Common::Rect &rect) { + for (uint i = 0; i < _screenSurfaceDirtyRects.size(); i++) { + if (rect.intersects(_screenSurfaceDirtyRects[i])) { + _screenSurfaceDirtyRects[i].extend(rect); + return; + } + } + + _screenSurfaceDirtyRects.push_back(rect); +} + +void FliesEffect::addToEffectsDirtyRects(const Common::Rect &rect) { + for (uint i = 0; i < _effectsSurfaceDirtyRects.size(); i++) { + if (rect.intersects(_effectsSurfaceDirtyRects[i])) { + _effectsSurfaceDirtyRects[i].extend(rect); + return; + } + } + + _effectsSurfaceDirtyRects.push_back(rect); +} + +void FliesEffect::restoreEffectsSurface() { + for (uint i = 0; i < _effectsSurfaceDirtyRects.size(); i++) { + const Common::Rect &rect = _effectsSurfaceDirtyRects[i]; + _effectSurface->copyRectToSurface(*_backSurface, rect.left, rect.top, rect); + addToScreenDirtyRects(rect); + } + + _effectsSurfaceDirtyRects.clear(); +} + } // End of namespace Mohawk |