ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

movie.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/myst3/movie.h"
00024 #include "engines/myst3/ambient.h"
00025 #include "engines/myst3/myst3.h"
00026 #include "engines/myst3/sound.h"
00027 #include "engines/myst3/state.h"
00028 #include "engines/myst3/subtitles.h"
00029 
00030 #include "common/config-manager.h"
00031 
00032 #include "graphics/colormasks.h"
00033 
00034 namespace Myst3 {
00035 
00036 Movie::Movie(Myst3Engine *vm, uint16 id) :
00037         _vm(vm),
00038         _id(id),
00039         _posU(0),
00040         _posV(0),
00041         _startFrame(0),
00042         _endFrame(0),
00043         _texture(0),
00044         _force2d(false),
00045         _forceOpaque(false),
00046         _subtitles(0),
00047         _volume(0),
00048         _additiveBlending(false),
00049         _transparency(100) {
00050 
00051     const DirectorySubEntry *binkDesc = _vm->getFileDescription("", id, 0, DirectorySubEntry::kMultitrackMovie);
00052 
00053     if (!binkDesc)
00054         binkDesc = _vm->getFileDescription("", id, 0, DirectorySubEntry::kDialogMovie);
00055 
00056     if (!binkDesc)
00057         binkDesc = _vm->getFileDescription("", id, 0, DirectorySubEntry::kStillMovie);
00058 
00059     if (!binkDesc)
00060         binkDesc = _vm->getFileDescription("", id, 0, DirectorySubEntry::kMovie);
00061 
00062     // Check whether the video is optional
00063     bool optional = false;
00064     if (_vm->_state->hasVarMovieOptional()) {
00065         optional = _vm->_state->getMovieOptional();
00066         _vm->_state->setMovieOptional(0);
00067     }
00068 
00069     if (!binkDesc) {
00070         if (!optional)
00071             error("Movie %d does not exist", id);
00072         else
00073             return;
00074     }
00075 
00076     loadPosition(binkDesc->getVideoData());
00077 
00078     Common::MemoryReadStream *binkStream = binkDesc->getData();
00079     _bink.setDefaultHighColorFormat(Texture::getRGBAPixelFormat());
00080     _bink.setSoundType(Audio::Mixer::kSFXSoundType);
00081     _bink.loadStream(binkStream);
00082 
00083     if (binkDesc->getType() == DirectorySubEntry::kMultitrackMovie
00084             || binkDesc->getType() == DirectorySubEntry::kDialogMovie) {
00085         uint language = ConfMan.getInt("audio_language");
00086         _bink.setAudioTrack(language);
00087     }
00088 
00089     if (ConfMan.getBool("subtitles"))
00090         _subtitles = Subtitles::create(_vm, id);
00091 
00092     // Clear the subtitles override anyway, so that it does not end up
00093     // being used by the another movie at some point.
00094     _vm->_state->setMovieOverrideSubtitles(0);
00095 }
00096 
00097 void Movie::loadPosition(const VideoData &videoData) {
00098     static const float scale = 50.0f;
00099 
00100     _is3D = _vm->_state->getViewType() == kCube;
00101 
00102     Math::Vector3d planeDirection = videoData.v1;
00103     planeDirection.normalize();
00104 
00105     Math::Vector3d u;
00106     u.set(planeDirection.z(), 0.0f, -planeDirection.x());
00107     u.normalize();
00108 
00109     Math::Vector3d v = Math::Vector3d::crossProduct(planeDirection, u);
00110     v.normalize();
00111 
00112     Math::Vector3d planeOrigin = planeDirection * scale;
00113 
00114     float left = (videoData.u - 320) * 0.003125f;
00115     float right = (videoData.u + videoData.width - 320) * 0.003125f;
00116     float top = (320 - videoData.v) * 0.003125f;
00117     float bottom = (320 - videoData.v - videoData.height) * 0.003125f;
00118 
00119     Math::Vector3d vLeft = scale * left * u;
00120     Math::Vector3d vRight = scale * right * u;
00121     Math::Vector3d vTop = scale * top * v;
00122     Math::Vector3d vBottom = scale * bottom * v;
00123 
00124     _pTopLeft = planeOrigin + vTop + vLeft;
00125     _pBottomLeft = planeOrigin + vBottom + vLeft;
00126     _pBottomRight = planeOrigin + vBottom + vRight;
00127     _pTopRight = planeOrigin + vTop + vRight;
00128 
00129     _posU = videoData.u;
00130     _posV = videoData.v;
00131 }
00132 
00133 void Movie::draw2d() {
00134     Common::Rect screenRect = Common::Rect(_bink.getWidth(), _bink.getHeight());
00135     screenRect.translate(_posU, _posV);
00136 
00137     Common::Rect textureRect = Common::Rect(_bink.getWidth(), _bink.getHeight());
00138 
00139     if (_forceOpaque)
00140         _vm->_gfx->drawTexturedRect2D(screenRect, textureRect, _texture);
00141     else
00142         _vm->_gfx->drawTexturedRect2D(screenRect, textureRect, _texture, (float) _transparency / 100, _additiveBlending);
00143 }
00144 
00145 void Movie::draw3d() {
00146     _vm->_gfx->drawTexturedRect3D(_pTopLeft, _pBottomLeft, _pTopRight, _pBottomRight, _texture);
00147 }
00148 
00149 void Movie::draw() {
00150     if (_force2d)
00151         return;
00152 
00153     if (_is3D) {
00154         draw3d();
00155     } else {
00156         draw2d();
00157     }
00158 }
00159 
00160 void Movie::drawOverlay() {
00161     if (_force2d)
00162         draw2d();
00163 
00164     if (_subtitles) {
00165         _subtitles->setFrame(adjustFrameForRate(_bink.getCurFrame(), false));
00166         _vm->_gfx->renderWindowOverlay(_subtitles);
00167     }
00168 }
00169 
00170 void Movie::drawNextFrameToTexture() {
00171     const Graphics::Surface *frame = _bink.decodeNextFrame();
00172 
00173     if (frame) {
00174         if (_texture)
00175             _texture->update(frame);
00176         else
00177             _texture = _vm->_gfx->createTexture(frame);
00178     }
00179 }
00180 
00181 int32 Movie::adjustFrameForRate(int32 frame, bool dataToBink) {
00182     // The scripts give frame numbers for a framerate of 15 im/s
00183     // adjust the frame number according to the actual framerate
00184     if (_bink.getFrameRate().toInt() != 15) {
00185         Common::Rational rational;
00186         if (dataToBink) {
00187             rational = _bink.getFrameRate() * frame / 15;
00188         } else {
00189             rational = 15 * frame / _bink.getFrameRate();
00190         }
00191         frame = rational.toInt();
00192     }
00193     return frame;
00194 }
00195 
00196 void Movie::setStartFrame(int32 v) {
00197     _startFrame = adjustFrameForRate(v, true);
00198 }
00199 
00200 void Movie::setEndFrame(int32 v) {
00201     _endFrame = adjustFrameForRate(v, true);
00202 }
00203 
00204 void Movie::pause(bool p) {
00205     _bink.pauseVideo(p);
00206 }
00207 
00208 Movie::~Movie() {
00209     if (_texture)
00210         _vm->_gfx->freeTexture(_texture);
00211 
00212     delete _subtitles;
00213 }
00214 
00215 void Movie::setForce2d(bool b) {
00216     _force2d = b;
00217     if (_force2d) {
00218         _is3D = false;
00219     }
00220 }
00221 
00222 ScriptedMovie::ScriptedMovie(Myst3Engine *vm, uint16 id) :
00223         Movie(vm, id),
00224         _condition(0),
00225         _conditionBit(0),
00226         _startFrameVar(0),
00227         _endFrameVar(0),
00228         _posUVar(0),
00229         _posVVar(0),
00230         _nextFrameReadVar(0),
00231         _nextFrameWriteVar(0),
00232         _playingVar(0),
00233         _enabled(false),
00234         _disableWhenComplete(false),
00235         _scriptDriven(false),
00236         _isLastFrame(false),
00237         _soundHeading(0),
00238         _soundAttenuation(0),
00239         _volumeVar(0),
00240         _loop(false),
00241         _transparencyVar(0) {
00242     _bink.start();
00243 }
00244 
00245 void ScriptedMovie::draw() {
00246     if (!_enabled)
00247         return;
00248 
00249     Movie::draw();
00250 }
00251 
00252 void ScriptedMovie::drawOverlay() {
00253     if (!_enabled)
00254         return;
00255 
00256     Movie::drawOverlay();
00257 }
00258 
00259 void ScriptedMovie::update() {
00260     if (_startFrameVar) {
00261         _startFrame = _vm->_state->getVar(_startFrameVar);
00262     }
00263 
00264     if (_endFrameVar) {
00265         _endFrame = _vm->_state->getVar(_endFrameVar);
00266     }
00267 
00268     if (!_endFrame) {
00269         _endFrame = _bink.getFrameCount();
00270     }
00271 
00272     if (_posUVar) {
00273         _posU = _vm->_state->getVar(_posUVar);
00274     }
00275 
00276     if (_posVVar) {
00277         _posV = _vm->_state->getVar(_posVVar);
00278     }
00279 
00280     if (_transparencyVar) {
00281         _transparency = _vm->_state->getVar(_transparencyVar);
00282     }
00283 
00284     bool newEnabled;
00285     if (_conditionBit) {
00286         newEnabled = (_vm->_state->getVar(_condition) & (1 << (_conditionBit - 1))) != 0;
00287     } else {
00288         newEnabled = _vm->_state->evaluate(_condition);
00289     }
00290 
00291     if (newEnabled != _enabled) {
00292         _enabled = newEnabled;
00293 
00294         if (newEnabled) {
00295             if (_disableWhenComplete
00296                     || _bink.getCurFrame() < _startFrame
00297                     || _bink.getCurFrame() >= _endFrame
00298                     || _bink.endOfVideo()) {
00299                 _bink.seekToFrame(_startFrame);
00300                 _isLastFrame = false;
00301             }
00302 
00303             if (!_scriptDriven)
00304                 _bink.pauseVideo(false);
00305 
00306             drawNextFrameToTexture();
00307 
00308         } else {
00309             // Make sure not to pause the video twice. VideoDecoder handles pause levels.
00310             // The video may have already been paused if _disableWhenComplete is set.
00311             if (!_bink.isPaused()) {
00312                 _bink.pauseVideo(true);
00313             }
00314         }
00315     }
00316 
00317     if (_enabled) {
00318         updateVolume();
00319 
00320         if (_nextFrameReadVar) {
00321             int32 nextFrame = _vm->_state->getVar(_nextFrameReadVar);
00322             if (nextFrame > 0 && nextFrame <= (int32)_bink.getFrameCount()) {
00323                 // Are we changing frame?
00324                 if (_bink.getCurFrame() != nextFrame - 1) {
00325                     // Don't seek if we just want to display the next frame
00326                     if (_bink.getCurFrame() + 1 != nextFrame - 1) {
00327                         _bink.seekToFrame(nextFrame - 1);
00328                     }
00329                     drawNextFrameToTexture();
00330                 }
00331 
00332                 _vm->_state->setVar(_nextFrameReadVar, 0);
00333                 _isLastFrame = false;
00334             }
00335         }
00336 
00337         if (!_scriptDriven && (_bink.needsUpdate() || _isLastFrame)) {
00338             bool complete = false;
00339 
00340             if (_isLastFrame) {
00341                 _isLastFrame = false;
00342 
00343                 if (_loop) {
00344                     _bink.seekToFrame(_startFrame);
00345                     drawNextFrameToTexture();
00346                 } else {
00347                     complete = true;
00348                 }
00349             } else {
00350                 drawNextFrameToTexture();
00351                 _isLastFrame = _bink.getCurFrame() == (_endFrame - 1);
00352             }
00353 
00354             if (_nextFrameWriteVar) {
00355                 _vm->_state->setVar(_nextFrameWriteVar, _bink.getCurFrame() + 1);
00356             }
00357 
00358             if (_disableWhenComplete && complete) {
00359                 _bink.pauseVideo(true);
00360 
00361                 if (_playingVar) {
00362                     _vm->_state->setVar(_playingVar, 0);
00363                 } else {
00364                     _enabled = 0;
00365                     _vm->_state->setVar(_condition & 0x7FF, 0);
00366                 }
00367             }
00368 
00369         }
00370     }
00371 }
00372 
00373 void ScriptedMovie::updateVolume() {
00374     int32 volume;
00375     if (_volumeVar) {
00376         volume = _vm->_state->getVar(_volumeVar);
00377     } else {
00378         volume = _volume;
00379     }
00380 
00381     int32 mixerVolume, balance;
00382     _vm->_sound->computeVolumeBalance(volume, _soundHeading, _soundAttenuation, &mixerVolume, &balance);
00383     _bink.setVolume(mixerVolume);
00384     _bink.setBalance(balance);
00385 }
00386 
00387 ScriptedMovie::~ScriptedMovie() {
00388 }
00389 
00390 SimpleMovie::SimpleMovie(Myst3Engine *vm, uint16 id) :
00391         Movie(vm, id),
00392         _synchronized(false) {
00393     _startFrame = 1;
00394     _endFrame = _bink.getFrameCount();
00395     _startEngineTick = _vm->_state->getTickCount();
00396 }
00397 
00398 void SimpleMovie::play() {
00399     playStartupSound();
00400 
00401     _bink.setEndFrame(_endFrame - 1);
00402     _bink.setVolume(_volume * Audio::Mixer::kMaxChannelVolume / 100);
00403 
00404     if (_bink.getCurFrame() < _startFrame - 1) {
00405         _bink.seekToFrame(_startFrame - 1);
00406     }
00407 
00408     _bink.start();
00409 }
00410 
00411 void SimpleMovie::update() {
00412     uint16 scriptStartFrame = _vm->_state->getMovieScriptStartFrame();
00413     if (scriptStartFrame && _bink.getCurFrame() > scriptStartFrame) {
00414         uint16 script = _vm->_state->getMovieScript();
00415 
00416         // The control variables are reset before running the script because
00417         // some scripts set up another movie triggered script
00418         _vm->_state->setMovieScriptStartFrame(0);
00419         _vm->_state->setMovieScript(0);
00420 
00421         _vm->runScriptsFromNode(script);
00422     }
00423 
00424     uint16 ambiantStartFrame = _vm->_state->getMovieAmbiantScriptStartFrame();
00425     if (ambiantStartFrame && _bink.getCurFrame() > ambiantStartFrame) {
00426         _vm->runAmbientScripts(_vm->_state->getMovieAmbiantScript());
00427         _vm->_state->setMovieAmbiantScriptStartFrame(0);
00428         _vm->_state->setMovieAmbiantScript(0);
00429     }
00430 
00431     if (!_synchronized) {
00432         // Play the movie according to the bink file framerate
00433         if (_bink.needsUpdate()) {
00434             drawNextFrameToTexture();
00435         }
00436     } else {
00437         // Draw a movie frame each two engine frames
00438         int targetFrame = (_vm->_state->getTickCount() - _startEngineTick) >> 1;
00439         if (_bink.getCurFrame() < targetFrame) {
00440             drawNextFrameToTexture();
00441         }
00442     }
00443 }
00444 
00445 bool SimpleMovie::endOfVideo() {
00446     if (!_synchronized) {
00447         return _bink.getTime() >= (uint)_bink.getEndTime().msecs();
00448     } else {
00449         int tickBasedEndFrame = (_vm->_state->getTickCount() - _startEngineTick) >> 1;
00450         return tickBasedEndFrame >= _endFrame;
00451     }
00452 }
00453 
00454 void SimpleMovie::playStartupSound() {
00455     int32 soundId = _vm->_state->getMovieStartSoundId();
00456     if (soundId) {
00457         uint32 volume = _vm->_state->getMovieStartSoundVolume();
00458         uint32 heading = _vm->_state->getMovieStartSoundHeading();
00459         uint32 attenuation = _vm->_state->getMovieStartSoundAttenuation();
00460 
00461         _vm->_sound->playEffect(soundId, volume, heading, attenuation);
00462 
00463         _vm->_state->setMovieStartSoundId(0);
00464     }
00465 }
00466 
00467 void SimpleMovie::refreshAmbientSounds() {
00468     uint32 engineFrames = _bink.getFrameCount() * 2;
00469     _vm->_ambient->playCurrentNode(100, engineFrames);
00470 }
00471 
00472 SimpleMovie::~SimpleMovie() {
00473 }
00474 
00475 ProjectorMovie::ProjectorMovie(Myst3Engine *vm, uint16 id, Graphics::Surface *background) :
00476         ScriptedMovie(vm, id),
00477         _background(background),
00478     _frame(0) {
00479     _enabled = true;
00480 
00481     for (uint i = 0; i < kBlurIterations; i++) {
00482         _blurTableX[i] = (uint8)(sin(2 * LOCAL_PI * i / (float)kBlurIterations) * 256.0);
00483         _blurTableY[i] = (uint8)(cos(2 * LOCAL_PI * i / (float)kBlurIterations) * 256.0);
00484     }
00485 }
00486 
00487 ProjectorMovie::~ProjectorMovie() {
00488     if (_frame) {
00489         _frame->free();
00490         delete _frame;
00491     }
00492 
00493     if (_background) {
00494         _background->free();
00495         delete _background;
00496     }
00497 }
00498 
00499 void ProjectorMovie::update() {
00500     if (!_frame) {
00501         // First call, get the alpha channel from the bink file
00502         const Graphics::Surface *frame = _bink.decodeNextFrame();
00503         _frame = new Graphics::Surface();
00504         _frame->copyFrom(*frame);
00505     }
00506 
00507     uint16 focus = _vm->_state->getProjectorBlur() / 10;
00508     uint16 zoom = _vm->_state->getProjectorZoom();
00509     uint16 backgroundX = (_vm->_state->getProjectorX() - zoom / 2) / 10;
00510     uint16 backgroundY = (_vm->_state->getProjectorY() - zoom / 2) / 10;
00511     float delta = zoom / 10.0 / _frame->w;
00512 
00513     // For each pixel in the target image
00514     for (uint i = 0; i < _frame->h; i++) {
00515         byte *dst = (byte *)_frame->getBasePtr(0, i);
00516         for (uint j = 0; j < _frame->w; j++) {
00517             uint8 depth;
00518             uint16 r = 0, g = 0, b = 0;
00519             uint32 srcX = (uint32)(backgroundX + j * delta);
00520             uint32 srcY = (uint32)(backgroundY + i * delta);
00521             byte *src = (byte *)_background->getBasePtr(srcX, srcY);
00522 
00523             // Get the depth from the background
00524             depth = *(src + 3);
00525 
00526             // Compute the blur level from the focus point and the depth of the current point
00527             uint8 blurLevel = abs(focus - depth) + 1;
00528             
00529             // No need to compute the effect for transparent pixels
00530             byte a = *(dst + 3);
00531             if (a != 0) {
00532                 // The blur effect is done by mixing the color components from the pixel at (srcX, srcY)
00533                 // and other pixels on the same row / column
00534                 uint cnt = 0;
00535                 for (uint k = 0; k < kBlurIterations; k++) {
00536                     uint32 blurX = srcX + ((uint32) (blurLevel * _blurTableX[k] * delta) >> 12); // >> 12 = / 256 / 16
00537                     uint32 blurY = srcY + ((uint32) (blurLevel * _blurTableY[k] * delta) >> 12);
00538 
00539                     if (blurX < 1024 && blurY < 1024) {
00540                         byte *blur = (byte *)_background->getBasePtr(blurX, blurY);
00541 
00542                         r += *blur++;
00543                         g += *blur++;
00544                         b += *blur;
00545                         cnt++;
00546                     }
00547                 }
00548 
00549                 // Divide the components by the number of pixels used in the blur effect
00550                 r /= cnt;
00551                 g /= cnt;
00552                 b /= cnt;
00553             }
00554 
00555             // Draw the new pixel
00556             *dst++ = r;
00557             *dst++ = g;
00558             *dst++ = b;
00559             dst++; // Keep the alpha channel from the previous frame
00560         }
00561     }
00562 
00563     if (_texture)
00564         _texture->update(_frame);
00565     else
00566         _texture = _vm->_gfx->createTexture(_frame);
00567 }
00568 
00569 } // End of namespace Myst3


Generated on Sat Dec 14 2019 05:00:39 for ResidualVM by doxygen 1.7.1
curved edge   curved edge