mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:23:52 +00:00
video playback
This commit is contained in:
parent
5bdc7bcacf
commit
73c69e8eda
13 changed files with 559 additions and 4 deletions
|
@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
|
|||
add_openmw_dir (mwrender
|
||||
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
|
||||
renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
|
||||
compositors characterpreview externalrendering globalmap
|
||||
compositors characterpreview externalrendering globalmap videoplayer
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
@ -108,6 +108,11 @@ target_link_libraries(openmw
|
|||
components
|
||||
)
|
||||
|
||||
if (USE_FFMPEG)
|
||||
target_link_libraries(openmw
|
||||
"swscale")
|
||||
endif()
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
|
|
@ -291,6 +291,10 @@ namespace MWBase
|
|||
/// 1 - only waiting \n
|
||||
/// 2 - player is underwater \n
|
||||
/// 3 - enemies are nearby (not implemented)
|
||||
|
||||
|
||||
/// \todo this does not belong here
|
||||
virtual void playVideo(const std::string& name) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ namespace MWGui
|
|||
GM_Loading,
|
||||
GM_LoadingWallpaper,
|
||||
|
||||
GM_QuickKeysMenu
|
||||
GM_QuickKeysMenu,
|
||||
|
||||
GM_Video
|
||||
};
|
||||
|
||||
// Windows shown in inventory mode
|
||||
|
|
|
@ -371,6 +371,9 @@ void WindowManager::updateVisible()
|
|||
case GM_Loading:
|
||||
MyGUI::PointerManager::getInstance().setVisible(false);
|
||||
break;
|
||||
case GM_Video:
|
||||
mHud->setVisible(false);
|
||||
break;
|
||||
default:
|
||||
// Unsupported mode, switch back to game
|
||||
break;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "npcanimation.hpp"
|
||||
#include "externalrendering.hpp"
|
||||
#include "globalmap.hpp"
|
||||
#include "videoplayer.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
@ -159,6 +160,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
|
|||
|
||||
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
|
||||
|
||||
mVideoPlayer = new VideoPlayer(mRendering.getScene ());
|
||||
|
||||
mSun = 0;
|
||||
|
||||
mDebugging = new Debugging(mMwRoot, engine);
|
||||
|
@ -180,6 +183,7 @@ RenderingManager::~RenderingManager ()
|
|||
delete mOcclusionQuery;
|
||||
delete mCompositors;
|
||||
delete mWater;
|
||||
delete mVideoPlayer;
|
||||
}
|
||||
|
||||
MWRender::SkyManager* RenderingManager::getSkyManager()
|
||||
|
@ -339,6 +343,8 @@ void RenderingManager::update (float duration)
|
|||
|
||||
mRendering.update(duration);
|
||||
|
||||
mVideoPlayer->update ();
|
||||
|
||||
MWWorld::RefData &data =
|
||||
MWBase::Environment::get()
|
||||
.getWorld()
|
||||
|
@ -898,4 +904,9 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend
|
|||
rendering.setup (mRendering.getScene());
|
||||
}
|
||||
|
||||
void RenderingManager::playVideo(const std::string& name)
|
||||
{
|
||||
mVideoPlayer->play ("video/" + name);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -45,6 +45,7 @@ namespace MWRender
|
|||
class Compositors;
|
||||
class ExternalRendering;
|
||||
class GlobalMap;
|
||||
class VideoPlayer;
|
||||
|
||||
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener {
|
||||
|
||||
|
@ -195,6 +196,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
|
|||
|
||||
void setupExternalRendering (MWRender::ExternalRendering& rendering);
|
||||
|
||||
void playVideo(const std::string& name);
|
||||
|
||||
protected:
|
||||
virtual void windowResized(Ogre::RenderWindow* rw);
|
||||
virtual void windowClosed(Ogre::RenderWindow* rw);
|
||||
|
@ -248,6 +251,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
|
|||
MWRender::Shadows* mShadows;
|
||||
|
||||
MWRender::Compositors* mCompositors;
|
||||
|
||||
VideoPlayer* mVideoPlayer;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
395
apps/openmw/mwrender/videoplayer.cpp
Normal file
395
apps/openmw/mwrender/videoplayer.cpp
Normal file
|
@ -0,0 +1,395 @@
|
|||
#include "videoplayer.hpp"
|
||||
|
||||
//#ifdef OPENMW_USE_FFMPEG
|
||||
|
||||
#include <OgreMaterialManager.h>
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreMaterial.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque);
|
||||
|
||||
int num_read = stream->size() - stream->tell();
|
||||
|
||||
if (num_read > buf_size)
|
||||
num_read = buf_size;
|
||||
|
||||
stream->read(buf, num_read);
|
||||
return num_read;
|
||||
}
|
||||
|
||||
int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque);
|
||||
|
||||
int num_write = stream->size() - stream->tell();
|
||||
|
||||
if (num_write > buf_size)
|
||||
num_write = buf_size;
|
||||
|
||||
stream->write (buf, num_write);
|
||||
return num_write;
|
||||
}
|
||||
|
||||
int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque);
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
stream->seek(offset);
|
||||
case SEEK_CUR:
|
||||
stream->seek(stream->tell() + offset);
|
||||
case SEEK_END:
|
||||
stream->seek(stream->size() + offset);
|
||||
case AVSEEK_SIZE:
|
||||
return stream->size();
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return stream->tell();
|
||||
}
|
||||
|
||||
VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr)
|
||||
: mAvContext(NULL)
|
||||
, mVideoStreamId(-1)
|
||||
{
|
||||
Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General");
|
||||
videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
|
||||
videoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
|
||||
videoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||||
mTextureUnit = videoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState();
|
||||
|
||||
mRectangle = new Ogre::Rectangle2D(true);
|
||||
mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0);
|
||||
mRectangle->setMaterial("VideoMaterial");
|
||||
mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1);
|
||||
// Use infinite AAB to always stay visible
|
||||
Ogre::AxisAlignedBox aabInf;
|
||||
aabInf.setInfinite();
|
||||
mRectangle->setBoundingBox(aabInf);
|
||||
// Attach background to the scene
|
||||
Ogre::SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
node->attachObject(mRectangle);
|
||||
mRectangle->setVisible(false);
|
||||
mRectangle->setVisibilityFlags (0x1);
|
||||
}
|
||||
|
||||
VideoPlayer::~VideoPlayer()
|
||||
{
|
||||
if (mAvContext)
|
||||
deleteContext();
|
||||
|
||||
delete mRectangle;
|
||||
}
|
||||
|
||||
void VideoPlayer::play (const std::string& resourceName)
|
||||
{
|
||||
mStream = Ogre::ResourceGroupManager::getSingleton ().openResource (resourceName);
|
||||
|
||||
|
||||
mVideoStreamId = -1;
|
||||
mEOF = false;
|
||||
|
||||
// if something is already playing, close it
|
||||
if (mAvContext)
|
||||
close();
|
||||
|
||||
mRectangle->setVisible(true);
|
||||
|
||||
MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video);
|
||||
|
||||
|
||||
// BASIC INITIALIZATION
|
||||
|
||||
// Load all the decoders
|
||||
av_register_all();
|
||||
|
||||
AVIOContext *ioContext = 0;
|
||||
|
||||
int err = 0;
|
||||
|
||||
mAvContext = avformat_alloc_context();
|
||||
if (!mAvContext)
|
||||
throwError(0);
|
||||
|
||||
ioContext = avio_alloc_context(NULL, 0, 0, &mStream, OgreResource_Read, OgreResource_Write, OgreResource_Seek);
|
||||
if (!ioContext)
|
||||
throwError(0);
|
||||
|
||||
mAvContext->pb = ioContext;
|
||||
|
||||
err = avformat_open_input(&mAvContext, resourceName.c_str(), NULL, NULL);
|
||||
if (err != 0)
|
||||
throwError(err);
|
||||
|
||||
err = avformat_find_stream_info(mAvContext, 0);
|
||||
if (err < 0)
|
||||
throwError(err);
|
||||
|
||||
|
||||
|
||||
|
||||
// INIT VIDEO
|
||||
|
||||
// Find the video stream among the different streams
|
||||
for (unsigned int i = 0; i < mAvContext->nb_streams; i++)
|
||||
{
|
||||
if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
|
||||
{
|
||||
mVideoStreamId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 == mVideoStreamId)
|
||||
throw std::runtime_error("No video stream found in the video");
|
||||
|
||||
// Get the video codec
|
||||
mVideoCodecContext = mAvContext->streams[mVideoStreamId]->codec;
|
||||
if (!mVideoCodecContext)
|
||||
throw std::runtime_error("Stream doesn't have a codec");
|
||||
|
||||
// Get the video decoder
|
||||
mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id);
|
||||
if (NULL == mVideoCodec)
|
||||
throw std::runtime_error("No decoder found");
|
||||
|
||||
// Load the video codec
|
||||
err = avcodec_open2(mVideoCodecContext, mVideoCodec, 0);
|
||||
if (err < 0)
|
||||
throwError (err);
|
||||
|
||||
// Create the frame buffers
|
||||
mRawFrame = avcodec_alloc_frame();
|
||||
mRGBAFrame = avcodec_alloc_frame();
|
||||
if (!mRawFrame || !mRGBAFrame)
|
||||
{
|
||||
throw std::runtime_error("Can't allocate video frames");
|
||||
}
|
||||
|
||||
|
||||
avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoCodecContext->width, mVideoCodecContext->height);
|
||||
|
||||
|
||||
// Setup the image scaler
|
||||
// All this does is convert from YUV to RGB - note it would be faster to do this in a shader,
|
||||
// but i'm not worried about performance just yet
|
||||
mSwsContext = sws_getContext(mVideoCodecContext->width, mVideoCodecContext->height,
|
||||
mVideoCodecContext->pix_fmt,
|
||||
mVideoCodecContext->width, mVideoCodecContext->height,
|
||||
PIX_FMT_RGBA,
|
||||
SWS_BICUBIC, NULL, NULL, NULL);
|
||||
if (!mSwsContext)
|
||||
throw std::runtime_error("Can't create SWS Context");
|
||||
|
||||
// Get the frame time we need for this video
|
||||
AVRational r = mAvContext->streams[mVideoStreamId]->avg_frame_rate;
|
||||
AVRational r2 = mAvContext->streams[mVideoStreamId]->r_frame_rate;
|
||||
if ((!r.num || !r.den) &&
|
||||
(!r2.num || !r2.den))
|
||||
{
|
||||
std::cerr << "Warning - unable to get the video frame rate. Using standard NTSC frame rate : 29.97 fps." << std::endl;
|
||||
mWantedFrameTime = 1.f / 29.97f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r.num && r.den)
|
||||
mWantedFrameTime = 1.f/((float)r.num / r.den);
|
||||
else
|
||||
mWantedFrameTime = 1.f/((float)r2.num / r2.den);
|
||||
}
|
||||
|
||||
|
||||
mTextureUnit->setTextureName ("");
|
||||
|
||||
if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull())
|
||||
Ogre::TextureManager::getSingleton().remove("VideoTexture");
|
||||
|
||||
mVideoTexture = Ogre::TextureManager::getSingleton().createManual(
|
||||
"VideoTexture",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
Ogre::TEX_TYPE_2D,
|
||||
mVideoCodecContext->width, mVideoCodecContext->height,
|
||||
0,
|
||||
Ogre::PF_BYTE_RGBA,
|
||||
Ogre::TU_DEFAULT);
|
||||
|
||||
mTextureUnit->setTextureName ("VideoTexture");
|
||||
|
||||
}
|
||||
|
||||
void VideoPlayer::throwError(int error)
|
||||
{
|
||||
char buffer[4096] = {0};
|
||||
|
||||
if (0 == av_strerror(error, buffer, sizeof(buffer)))
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "FFMPEG error: ";
|
||||
msg << buffer << std::endl;
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Unknown FFMPEG error");
|
||||
}
|
||||
|
||||
bool VideoPlayer::readFrameAndQueue ()
|
||||
{
|
||||
bool ret = true;
|
||||
AVPacket *pkt = NULL;
|
||||
|
||||
// check we're not at eof
|
||||
if (mEOF)
|
||||
ret = false;
|
||||
else
|
||||
{
|
||||
// read frame
|
||||
pkt = (AVPacket *)av_malloc(sizeof(*pkt));
|
||||
int res = av_read_frame(mAvContext, pkt);
|
||||
|
||||
// check we didn't reach eof right now
|
||||
if (res < 0)
|
||||
{
|
||||
mEOF = true;
|
||||
ret = false;
|
||||
av_free(pkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When a frame has been read, save it
|
||||
if (!saveFrame(pkt))
|
||||
{
|
||||
// we failed to save it, which means it was unknown type of frame - delete it here
|
||||
av_free_packet(pkt);
|
||||
av_free(pkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VideoPlayer::saveFrame(AVPacket* frame)
|
||||
{
|
||||
bool saved = false;
|
||||
|
||||
if (frame->stream_index == mVideoStreamId)
|
||||
{
|
||||
// If it was a video frame...
|
||||
mVideoPacketQueue.push(frame);
|
||||
saved = true;
|
||||
}
|
||||
|
||||
return saved;
|
||||
|
||||
}
|
||||
|
||||
void VideoPlayer::update()
|
||||
{
|
||||
if (!mAvContext)
|
||||
return;
|
||||
|
||||
|
||||
if (!mVideoPacketQueue.size() && mEOF)
|
||||
close();
|
||||
|
||||
Ogre::Timer timer;
|
||||
|
||||
if (!mVideoPacketQueue.size())
|
||||
{
|
||||
if (readFrameAndQueue())
|
||||
decodeFrontFrame();
|
||||
}
|
||||
else
|
||||
decodeFrontFrame();
|
||||
|
||||
mDecodingTime = timer.getMilliseconds ()/1000.f;
|
||||
}
|
||||
|
||||
void VideoPlayer::decodeFrontFrame ()
|
||||
{
|
||||
int didDecodeFrame = 0;
|
||||
|
||||
// Make sure there is something to decode
|
||||
if (!mVideoPacketQueue.size())
|
||||
return;
|
||||
|
||||
// Get the front frame and decode it
|
||||
AVPacket *videoPacket = mVideoPacketQueue.front();
|
||||
int res;
|
||||
res = avcodec_decode_video2(mVideoCodecContext, mRawFrame, &didDecodeFrame, videoPacket);
|
||||
|
||||
if (res < 0 || !didDecodeFrame)
|
||||
throw std::runtime_error ("an error occured while decoding the video frame");
|
||||
|
||||
// Convert the frame to RGB
|
||||
sws_scale(mSwsContext,
|
||||
mRawFrame->data, mRawFrame->linesize,
|
||||
0, mVideoCodecContext->height,
|
||||
mRGBAFrame->data, mRGBAFrame->linesize);
|
||||
|
||||
|
||||
Ogre::HardwarePixelBufferSharedPtr pixelBuffer = mVideoTexture->getBuffer();
|
||||
Ogre::PixelBox pb(mVideoCodecContext->width, mVideoCodecContext->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]);
|
||||
pixelBuffer->blitFromMemory(pb);
|
||||
|
||||
//m_displayedFrameCount++;
|
||||
av_free_packet(mVideoPacketQueue.front());
|
||||
av_free(mVideoPacketQueue.front());
|
||||
mVideoPacketQueue.pop();
|
||||
}
|
||||
|
||||
void VideoPlayer::close ()
|
||||
{
|
||||
mRectangle->setVisible (false);
|
||||
MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video);
|
||||
deleteContext();
|
||||
}
|
||||
|
||||
void VideoPlayer::deleteContext()
|
||||
{
|
||||
while (mVideoPacketQueue.size())
|
||||
{
|
||||
av_free_packet(mVideoPacketQueue.front());
|
||||
av_free(mVideoPacketQueue.front());
|
||||
mVideoPacketQueue.pop();
|
||||
}
|
||||
|
||||
avcodec_close(mVideoCodecContext);
|
||||
|
||||
avpicture_free((AVPicture *)mRGBAFrame);
|
||||
|
||||
if (mRawFrame)
|
||||
av_free(mRawFrame);
|
||||
if (mRGBAFrame)
|
||||
av_free(mRGBAFrame);
|
||||
|
||||
sws_freeContext(mSwsContext);
|
||||
|
||||
avformat_close_input(&mAvContext);
|
||||
|
||||
mAvContext = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//#endif
|
105
apps/openmw/mwrender/videoplayer.hpp
Normal file
105
apps/openmw/mwrender/videoplayer.hpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
#ifndef MWRENDER_VIDEOPLAYER_H
|
||||
#define MWRENDER_VIDEOPLAYER_H
|
||||
|
||||
//#ifdef OPENMW_USE_FFMPEG
|
||||
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <OgreDataStream.h>
|
||||
#include <OgreTexture.h>
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Rectangle2D;
|
||||
class SceneManager;
|
||||
class TextureUnitState;
|
||||
}
|
||||
|
||||
struct AVFormatContext;
|
||||
struct AVCodecContext;
|
||||
struct AVCodec;
|
||||
struct AVFrame;
|
||||
struct SwsContext;
|
||||
struct AVPacket;
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class VideoPlayer
|
||||
{
|
||||
public:
|
||||
VideoPlayer(Ogre::SceneManager* sceneMgr);
|
||||
~VideoPlayer();
|
||||
|
||||
void play (const std::string& resourceName);
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
Ogre::Rectangle2D* mRectangle;
|
||||
Ogre::TextureUnitState* mTextureUnit;
|
||||
|
||||
Ogre::DataStreamPtr mStream;
|
||||
|
||||
Ogre::TexturePtr mVideoTexture;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
AVFormatContext* mAvContext;
|
||||
|
||||
|
||||
bool mEOF;
|
||||
|
||||
// VIDEO
|
||||
AVCodecContext* mVideoCodecContext;
|
||||
AVCodec* mVideoCodec;
|
||||
int mVideoStreamId;
|
||||
AVFrame* mRawFrame;
|
||||
AVFrame* mRGBAFrame;
|
||||
SwsContext* mSwsContext;
|
||||
float mWantedFrameTime;
|
||||
float mDecodingTime;
|
||||
std::queue <AVPacket *> mVideoPacketQueue;
|
||||
|
||||
|
||||
bool readFrameAndQueue();
|
||||
bool saveFrame(AVPacket* frame);
|
||||
|
||||
void decodeFrontFrame();
|
||||
|
||||
void close();
|
||||
void deleteContext();
|
||||
|
||||
|
||||
void throwError(int error);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//#else
|
||||
/*
|
||||
|
||||
|
||||
// If FFMPEG not available, dummy implentation that does nothing
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class VideoPlayer
|
||||
{
|
||||
public:
|
||||
VideoPlayer(Ogre::SceneManager* sceneMgr){}
|
||||
|
||||
void play (const std::string& resourceName) {}
|
||||
void update() {}
|
||||
};
|
||||
|
||||
}
|
||||
*/
|
||||
//#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -205,5 +205,6 @@ op 0x200019e: PlaceAtMe Explicit
|
|||
op 0x200019f: GetPcSleep
|
||||
op 0x20001a0: ShowMap
|
||||
op 0x20001a1: FillMap
|
||||
opcodes 0x20001a2-0x3ffffff unused
|
||||
op 0x20001a2: PlayBink
|
||||
opcodes 0x20001a3-0x3ffffff unused
|
||||
|
||||
|
|
|
@ -21,6 +21,19 @@ namespace MWScript
|
|||
{
|
||||
namespace Misc
|
||||
{
|
||||
class OpPlayBink : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
std::string name = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
MWBase::Environment::get().getWorld ()->playVideo (name);
|
||||
}
|
||||
};
|
||||
|
||||
class OpGetPcSleep : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
@ -261,6 +274,7 @@ namespace MWScript
|
|||
const int opcodeDontSaveObject = 0x2000153;
|
||||
const int opcodeToggleVanityMode = 0x2000174;
|
||||
const int opcodeGetPcSleep = 0x200019f;
|
||||
const int opcodePlayBink = 0x20001a2;
|
||||
|
||||
void registerExtensions (Compiler::Extensions& extensions)
|
||||
{
|
||||
|
@ -286,6 +300,7 @@ namespace MWScript
|
|||
extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode);
|
||||
extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode);
|
||||
extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep);
|
||||
extensions.registerInstruction ("playbink", "S", opcodePlayBink);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
|
@ -307,6 +322,7 @@ namespace MWScript
|
|||
interpreter.installSegment5 (opcodeDontSaveObject, new OpDontSaveObject);
|
||||
interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode);
|
||||
interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep);
|
||||
interpreter.installSegment5 (opcodePlayBink, new OpPlayBink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1281,4 +1281,9 @@ namespace MWWorld
|
|||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void World::playVideo (const std::string &name)
|
||||
{
|
||||
mRendering->playVideo(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -322,6 +322,9 @@ namespace MWWorld
|
|||
/// 1 - only waiting \n
|
||||
/// 2 - player is underwater \n
|
||||
/// 3 - enemies are nearby (not implemented)
|
||||
|
||||
/// \todo this does not belong here
|
||||
virtual void playVideo(const std::string& name);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ compositor gbufferFinalizer
|
|||
pass render_scene
|
||||
{
|
||||
first_render_queue 51
|
||||
last_render_queue 100
|
||||
last_render_queue 105
|
||||
}
|
||||
}
|
||||
target_output
|
||||
|
|
Loading…
Reference in a new issue