mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-09 03:10:22 +00:00
MTROPOLIS: Initial movie element support
This commit is contained in:
parent
18f0271beb
commit
6e53185f52
@ -49,6 +49,7 @@ namespace Common {
|
||||
* @details File parser used in engines:
|
||||
* - groovie
|
||||
* - mohawk
|
||||
* - mtropolis
|
||||
* - sci
|
||||
* @{
|
||||
*/
|
||||
@ -78,7 +79,7 @@ public:
|
||||
|
||||
/**
|
||||
* Set the beginning offset of the video so we can modify the offsets in the stco
|
||||
* atom of videos inside the Mohawk archives
|
||||
* atom of videos inside the Mohawk/mTropolis archives
|
||||
* @param offset the beginning offset of the video
|
||||
*/
|
||||
void setChunkBeginOffset(uint32 offset) { _beginOffset = offset; }
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
AssetLoaderContext::AssetLoaderContext() {
|
||||
AssetLoaderContext::AssetLoaderContext(size_t streamIndex) : streamIndex(streamIndex) {
|
||||
}
|
||||
|
||||
template<typename TAsset, typename TAssetData>
|
||||
|
@ -28,7 +28,9 @@
|
||||
namespace MTropolis {
|
||||
|
||||
struct AssetLoaderContext {
|
||||
AssetLoaderContext();
|
||||
explicit AssetLoaderContext(size_t streamIndex);
|
||||
|
||||
size_t streamIndex;
|
||||
};
|
||||
|
||||
struct IAssetFactory {
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "mtropolis/assets.h"
|
||||
#include "mtropolis/asset_factory.h"
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
@ -29,6 +30,10 @@ Asset::Asset() : _assetID(0) {
|
||||
Asset::~Asset() {
|
||||
}
|
||||
|
||||
uint32 Asset::getAssetID() const {
|
||||
return _assetID;
|
||||
}
|
||||
|
||||
bool ColorTableAsset::load(AssetLoaderContext &context, const Data::ColorTableAsset &data) {
|
||||
_assetID = data.assetID;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
@ -39,6 +44,10 @@ bool ColorTableAsset::load(AssetLoaderContext &context, const Data::ColorTableAs
|
||||
return true;
|
||||
}
|
||||
|
||||
AssetType ColorTableAsset::getAssetType() const {
|
||||
return kAssetTypeColorTable;
|
||||
}
|
||||
|
||||
bool AudioAsset::load(AssetLoaderContext &context, const Data::AudioAsset &data) {
|
||||
_sampleRate = data.sampleRate1;
|
||||
_bitsPerSample = data.bitsPerSample;
|
||||
@ -73,14 +82,45 @@ bool AudioAsset::load(AssetLoaderContext &context, const Data::AudioAsset &data)
|
||||
return true;
|
||||
}
|
||||
|
||||
AssetType AudioAsset::getAssetType() const {
|
||||
return kAssetTypeAudio;
|
||||
}
|
||||
|
||||
bool MovieAsset::load(AssetLoaderContext &context, const Data::MovieAsset &data) {
|
||||
_assetID = data.assetID;
|
||||
_moovAtomPos = data.moovAtomPos;
|
||||
_movieDataPos = data.movieDataPos;
|
||||
_movieDataSize = data.movieDataSize;
|
||||
_extFileName = data.extFileName;
|
||||
_streamIndex = context.streamIndex;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AssetType MovieAsset::getAssetType() const {
|
||||
return kAssetTypeMovie;
|
||||
}
|
||||
|
||||
uint32 MovieAsset::getMovieDataPos() const {
|
||||
return _movieDataPos;
|
||||
}
|
||||
|
||||
uint32 MovieAsset::getMoovAtomPos() const {
|
||||
return _moovAtomPos;
|
||||
}
|
||||
|
||||
uint32 MovieAsset::getMovieDataSize() const {
|
||||
return _movieDataSize;
|
||||
}
|
||||
|
||||
|
||||
const Common::String &MovieAsset::getExtFileName() const {
|
||||
return _extFileName;
|
||||
}
|
||||
|
||||
size_t MovieAsset::getStreamIndex() const {
|
||||
return _streamIndex;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace MTropolis
|
||||
|
@ -32,6 +32,7 @@ struct AssetLoaderContext;
|
||||
class ColorTableAsset : public Asset {
|
||||
public:
|
||||
bool load(AssetLoaderContext &context, const Data::ColorTableAsset &data);
|
||||
AssetType getAssetType() const override;
|
||||
|
||||
private:
|
||||
ColorRGB8 _colors[256];
|
||||
@ -51,6 +52,7 @@ public:
|
||||
};
|
||||
|
||||
bool load(AssetLoaderContext &context, const Data::AudioAsset &data);
|
||||
AssetType getAssetType() const override;
|
||||
|
||||
private:
|
||||
uint16 _sampleRate;
|
||||
@ -67,6 +69,14 @@ private:
|
||||
class MovieAsset : public Asset {
|
||||
public:
|
||||
bool load(AssetLoaderContext &context, const Data::MovieAsset &data);
|
||||
AssetType getAssetType() const override;
|
||||
|
||||
uint32 getMovieDataPos() const;
|
||||
uint32 getMoovAtomPos() const;
|
||||
uint32 getMovieDataSize() const;
|
||||
|
||||
const Common::String &getExtFileName() const;
|
||||
size_t getStreamIndex() const;
|
||||
|
||||
private:
|
||||
uint32 _movieDataPos;
|
||||
@ -74,6 +84,7 @@ private:
|
||||
uint32 _movieDataSize;
|
||||
|
||||
Common::String _extFileName;
|
||||
size_t _streamIndex;
|
||||
};
|
||||
|
||||
} // End of namespace MTropolis
|
||||
|
@ -95,7 +95,7 @@ void Debugger::notify(DebugSeverity severity, const Common::String& str) {
|
||||
const Graphics::PixelFormat pixelFmt = _runtime->getRenderPixelFormat();
|
||||
|
||||
ToastNotification toastNotification;
|
||||
toastNotification.window.reset(new Window(0, displayHeight, width, toastNotificationHeight, pixelFmt));
|
||||
toastNotification.window.reset(new Window(_runtime, 0, displayHeight, width, toastNotificationHeight, pixelFmt));
|
||||
|
||||
byte fillColor[3] = {255, 255, 255};
|
||||
if (severity == kDebugSeverityError) {
|
||||
@ -165,7 +165,7 @@ void Debugger::refreshSceneStatus() {
|
||||
|
||||
const Graphics::PixelFormat pixelFmt = _runtime->getRenderPixelFormat();
|
||||
|
||||
_sceneStatusWindow.reset(new Window(0, 0, horizPadding * 2 + width, vertSpacing * sceneStrs.size(), pixelFmt));
|
||||
_sceneStatusWindow.reset(new Window(_runtime, 0, 0, horizPadding * 2 + width, vertSpacing * sceneStrs.size(), pixelFmt));
|
||||
_runtime->addWindow(_sceneStatusWindow);
|
||||
|
||||
for (uint i = 0; i < sceneStrs.size(); i++) {
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
ElementLoaderContext::ElementLoaderContext() {
|
||||
ElementLoaderContext::ElementLoaderContext(Runtime *runtime, size_t streamIndex) : runtime(runtime), streamIndex(streamIndex) {
|
||||
}
|
||||
|
||||
template<typename TElement, typename TElementData>
|
||||
|
@ -28,7 +28,10 @@
|
||||
namespace MTropolis {
|
||||
|
||||
struct ElementLoaderContext {
|
||||
ElementLoaderContext();
|
||||
ElementLoaderContext(Runtime *runtime, size_t streamIndex);
|
||||
|
||||
Runtime *runtime;
|
||||
size_t streamIndex;
|
||||
};
|
||||
|
||||
struct IElementFactory {
|
||||
|
@ -20,10 +20,19 @@
|
||||
*/
|
||||
|
||||
#include "mtropolis/elements.h"
|
||||
#include "mtropolis/assets.h"
|
||||
#include "mtropolis/element_factory.h"
|
||||
#include "mtropolis/render.h"
|
||||
|
||||
#include "video/video_decoder.h"
|
||||
#include "video/qt_decoder.h"
|
||||
|
||||
#include "common/substream.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
GraphicElement::GraphicElement() : _directToScreen(false), _cacheBitmap(false) {
|
||||
GraphicElement::GraphicElement() : _cacheBitmap(false) {
|
||||
}
|
||||
|
||||
GraphicElement::~GraphicElement() {
|
||||
@ -33,25 +42,29 @@ bool GraphicElement::load(ElementLoaderContext &context, const Data::GraphicElem
|
||||
if (!loadCommon(data.name, data.guid, data.rect1, data.elementFlags, data.layer, data.streamLocator, data.sectionID))
|
||||
return false;
|
||||
|
||||
_directToScreen = ((data.elementFlags & Data::ElementFlags::kNotDirectToScreen) == 0);
|
||||
_cacheBitmap = ((data.elementFlags & Data::ElementFlags::kCacheBitmap) != 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicElement::render(Window *window) {
|
||||
// todo
|
||||
}
|
||||
|
||||
MovieElement::MovieElement()
|
||||
: _directToScreen(false), _cacheBitmap(false), _paused(false)
|
||||
, _loop(false), _alternate(false), _playEveryFrame(false), _assetID(0) {
|
||||
: _cacheBitmap(false), _paused(false)
|
||||
, _loop(false), _alternate(false), _playEveryFrame(false), _assetID(0), _runtime(nullptr) {
|
||||
}
|
||||
|
||||
MovieElement::~MovieElement() {
|
||||
if (_unloadSignaller)
|
||||
_unloadSignaller->removeReceiver(this);
|
||||
}
|
||||
|
||||
bool MovieElement::load(ElementLoaderContext &context, const Data::MovieElement &data) {
|
||||
if (!loadCommon(data.name, data.guid, data.rect1, data.elementFlags, data.layer, data.streamLocator, data.sectionID))
|
||||
return false;
|
||||
|
||||
_directToScreen = ((data.elementFlags & Data::ElementFlags::kNotDirectToScreen) == 0);
|
||||
_cacheBitmap = ((data.elementFlags & Data::ElementFlags::kCacheBitmap) != 0);
|
||||
_paused = ((data.elementFlags & Data::ElementFlags::kPaused) != 0);
|
||||
_loop = ((data.animationFlags & Data::AnimationFlags::kLoop) != 0);
|
||||
@ -59,15 +72,12 @@ bool MovieElement::load(ElementLoaderContext &context, const Data::MovieElement
|
||||
_playEveryFrame = ((data.animationFlags & Data::AnimationFlags::kPlayEveryFrame) != 0);
|
||||
_assetID = data.assetID;
|
||||
|
||||
_runtime = context.runtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MovieElement::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
|
||||
if (attrib == "direct") {
|
||||
result.setBool(_directToScreen);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (attrib == "paused") {
|
||||
result.setBool(_paused);
|
||||
return true;
|
||||
@ -77,10 +87,6 @@ bool MovieElement::readAttribute(MiniscriptThread *thread, DynamicValue &result,
|
||||
}
|
||||
|
||||
bool MovieElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
|
||||
if (attrib == "direct") {
|
||||
writeProxy = DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetDirect>::create(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (attrib == "paused") {
|
||||
writeProxy = DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetPaused>::create(this);
|
||||
@ -90,12 +96,82 @@ bool MovieElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWrite
|
||||
return VisualElement::writeRefAttribute(thread, writeProxy, attrib);
|
||||
}
|
||||
|
||||
bool MovieElement::scriptSetDirect(const DynamicValue &dest) {
|
||||
if (dest.getType() == DynamicValueTypes::kBoolean) {
|
||||
_directToScreen = dest.getBool();
|
||||
return true;
|
||||
VThreadState MovieElement::consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
|
||||
if (Event::create(EventIDs::kPlay, 0).respondsTo(msg->getEvent())) {
|
||||
StartPlayingTaskData *startPlayingTaskData = runtime->getVThread().pushTask(this, &MovieElement::startPlayingTask);
|
||||
startPlayingTaskData->runtime = runtime;
|
||||
|
||||
ChangeFlagTaskData *becomeVisibleTaskData = runtime->getVThread().pushTask(static_cast<VisualElement *>(this), &MovieElement::changeVisibilityTask);
|
||||
becomeVisibleTaskData->desiredFlag = true;
|
||||
becomeVisibleTaskData->runtime = runtime;
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
|
||||
void MovieElement::activate() {
|
||||
Project *project = _runtime->getProject();
|
||||
Common::SharedPtr<Asset> asset = project->getAssetByID(_assetID).lock();
|
||||
|
||||
if (!asset) {
|
||||
warning("Movie element references asset %i but the asset isn't loaded!", _assetID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (asset->getAssetType() != kAssetTypeMovie) {
|
||||
warning("Movie element assigned an asset that isn't a movie");
|
||||
return;
|
||||
}
|
||||
|
||||
MovieAsset *movieAsset = static_cast<MovieAsset *>(asset.get());
|
||||
size_t streamIndex = movieAsset->getStreamIndex();
|
||||
int segmentIndex = project->getSegmentForStreamIndex(streamIndex);
|
||||
project->openSegmentStream(segmentIndex);
|
||||
Common::SeekableReadStream *stream = project->getStreamForSegment(segmentIndex);
|
||||
|
||||
if (!stream) {
|
||||
warning("Movie element stream could not be opened");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Video::QuickTimeDecoder *qtDecoder = new Video::QuickTimeDecoder();
|
||||
qtDecoder->setChunkBeginOffset(movieAsset->getMovieDataPos());
|
||||
|
||||
_videoDecoder.reset(qtDecoder);
|
||||
|
||||
Common::SafeSeekableSubReadStream *movieDataStream = new Common::SafeSeekableSubReadStream(stream, movieAsset->getMovieDataPos(), movieAsset->getMovieDataPos() + movieAsset->getMovieDataSize(), DisposeAfterUse::NO);
|
||||
|
||||
if (!_videoDecoder->loadStream(movieDataStream))
|
||||
_videoDecoder.reset();
|
||||
|
||||
_unloadSignaller = project->notifyOnSegmentUnload(segmentIndex, this);
|
||||
}
|
||||
|
||||
void MovieElement::deactivate() {
|
||||
if (_unloadSignaller) {
|
||||
_unloadSignaller->removeReceiver(this);
|
||||
_unloadSignaller.reset();
|
||||
}
|
||||
|
||||
_videoDecoder.reset();
|
||||
}
|
||||
|
||||
void MovieElement::render(Window *window) {
|
||||
const Graphics::Surface *videoSurface = nullptr;
|
||||
while (_videoDecoder->needsUpdate()) {
|
||||
videoSurface = _videoDecoder->decodeNextFrame();
|
||||
if (_playEveryFrame)
|
||||
break;
|
||||
}
|
||||
|
||||
if (videoSurface) {
|
||||
Graphics::ManagedSurface *target = window->getSurface().get();
|
||||
Common::Rect srcRect(0, 0, videoSurface->w, videoSurface->h);
|
||||
Common::Rect destRect(_rect.left, _rect.top, _rect.right, _rect.bottom);
|
||||
target->blitFrom(*videoSurface, srcRect, destRect);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MovieElement::scriptSetPaused(const DynamicValue& dest) {
|
||||
@ -106,4 +182,26 @@ bool MovieElement::scriptSetPaused(const DynamicValue& dest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MovieElement::onSegmentUnloaded(int segmentIndex) {
|
||||
_videoDecoder.reset();
|
||||
}
|
||||
|
||||
VThreadState MovieElement::startPlayingTask(const StartPlayingTaskData &taskData) {
|
||||
if (_videoDecoder && !_videoDecoder->isPlaying()) {
|
||||
EventIDs::EventID eventToSend = EventIDs::kPlay;
|
||||
if (_paused) {
|
||||
_paused = false;
|
||||
eventToSend = EventIDs::kUnpause;
|
||||
}
|
||||
|
||||
_videoDecoder->start();
|
||||
|
||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(eventToSend, 0), DynamicValue(), getSelfReference()));
|
||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||
taskData.runtime->sendMessageOnVThread(dispatch);
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
} // End of namespace MTropolis
|
||||
|
@ -25,6 +25,12 @@
|
||||
#include "mtropolis/data.h"
|
||||
#include "mtropolis/runtime.h"
|
||||
|
||||
namespace Video {
|
||||
|
||||
class VideoDecoder;
|
||||
|
||||
} // End of namespace Video
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
struct ElementLoaderContext;
|
||||
@ -36,16 +42,18 @@ public:
|
||||
|
||||
bool load(ElementLoaderContext &context, const Data::GraphicElement &data);
|
||||
|
||||
void render(Window *window) override;
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
const char *debugGetTypeName() const override { return "Graphic Element"; }
|
||||
SupportStatus debugGetSupportStatus() const override { return kSupportStatusPartial; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool _directToScreen;
|
||||
bool _cacheBitmap;
|
||||
};
|
||||
|
||||
class MovieElement : public VisualElement {
|
||||
class MovieElement : public VisualElement, public ISegmentUnloadSignalReceiver {
|
||||
public:
|
||||
MovieElement();
|
||||
~MovieElement();
|
||||
@ -54,22 +62,39 @@ public:
|
||||
|
||||
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib);
|
||||
bool writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib);
|
||||
VThreadState consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
void render(Window *window) override;
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
const char *debugGetTypeName() const override { return "Movie Element"; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool scriptSetDirect(const DynamicValue &dest);
|
||||
bool scriptSetPaused(const DynamicValue &dest);
|
||||
|
||||
bool _directToScreen;
|
||||
void onSegmentUnloaded(int segmentIndex) override;
|
||||
|
||||
struct StartPlayingTaskData {
|
||||
Runtime *runtime;
|
||||
};
|
||||
|
||||
VThreadState startPlayingTask(const StartPlayingTaskData &taskData);
|
||||
|
||||
bool _cacheBitmap;
|
||||
bool _paused;
|
||||
bool _loop;
|
||||
bool _alternate;
|
||||
bool _playEveryFrame;
|
||||
uint32 _assetID;
|
||||
|
||||
Common::SharedPtr<Video::VideoDecoder> _videoDecoder;
|
||||
Common::SharedPtr<SegmentUnloadSignaller> _unloadSignaller;
|
||||
|
||||
Runtime *_runtime;
|
||||
};
|
||||
|
||||
} // End of namespace MTropolis
|
||||
|
@ -241,8 +241,6 @@ void MTropolisEngine::handleEvents() {
|
||||
Common::Error MTropolisEngine::run() {
|
||||
int preferredWidth = 1024;
|
||||
int preferredHeight = 768;
|
||||
bool enableFrameRateLimit = true;
|
||||
uint expectedFrameRate = 60;
|
||||
ColorDepthMode preferredColorDepthMode = kColorDepthMode8Bit;
|
||||
|
||||
_runtime.reset(new Runtime(_system));
|
||||
@ -417,8 +415,6 @@ Common::Error MTropolisEngine::run() {
|
||||
|
||||
bool paused = false;
|
||||
|
||||
int frameCounter = 0;
|
||||
int numSkippedFrames = 0;
|
||||
while (!shouldQuit()) {
|
||||
handleEvents();
|
||||
|
||||
|
@ -64,7 +64,7 @@ inline int quantize8To5(int value, byte orderedDither16x16) {
|
||||
return (value * 249 + (orderedDither16x16 << 3)) >> 11;
|
||||
}
|
||||
|
||||
Window::Window(int32 x, int32 y, int16 width, int16 height, const Graphics::PixelFormat &format) : _x(x), _y(y) {
|
||||
Window::Window(Runtime *runtime, int32 x, int32 y, int16 width, int16 height, const Graphics::PixelFormat &format) : _runtime(runtime), _x(x), _y(y) {
|
||||
_surface.reset(new Graphics::ManagedSurface(width, height, format));
|
||||
}
|
||||
|
||||
@ -93,6 +93,18 @@ const Graphics::PixelFormat& Window::getPixelFormat() const {
|
||||
return _surface->format;
|
||||
}
|
||||
|
||||
void Window::close() {
|
||||
Runtime *runtime = _runtime;
|
||||
_runtime = nullptr;
|
||||
|
||||
if (runtime)
|
||||
runtime->removeWindow(this);
|
||||
}
|
||||
|
||||
void Window::detachFromRuntime() {
|
||||
_runtime = nullptr;
|
||||
}
|
||||
|
||||
namespace Render {
|
||||
|
||||
uint32 resolveRGB(uint8 r, uint8 g, uint8 b, const Graphics::PixelFormat &fmt) {
|
||||
@ -104,6 +116,66 @@ uint32 resolveRGB(uint8 r, uint8 g, uint8 b, const Graphics::PixelFormat &fmt) {
|
||||
return rPlaced | gPlaced | bPlaced | aPlaced;
|
||||
}
|
||||
|
||||
static void recursiveCollectDrawElements(Structural *structural, Common::Array<VisualElement *> &normalBucket, Common::Array<VisualElement *> &directBucket) {
|
||||
if (structural->isElement()) {
|
||||
Element *element = static_cast<Element *>(structural);
|
||||
if (element->isVisual()) {
|
||||
VisualElement *visualElement = static_cast<VisualElement *>(element);
|
||||
if (visualElement->isVisible()) {
|
||||
if (visualElement->isDirectToScreen())
|
||||
directBucket.push_back(visualElement);
|
||||
else
|
||||
normalBucket.push_back(visualElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Common::Array<Common::SharedPtr<Structural> >::const_iterator it = structural->getChildren().begin(), itEnd = structural->getChildren().end(); it != itEnd; ++it) {
|
||||
recursiveCollectDrawElements(it->get(), normalBucket, directBucket);
|
||||
}
|
||||
}
|
||||
|
||||
static bool visualElementLayerLess(VisualElement *a, VisualElement *b) {
|
||||
return a->getLayer() < b->getLayer();
|
||||
}
|
||||
|
||||
static void renderNormalElement(VisualElement *element, Window *mainWindow) {
|
||||
element->render(mainWindow);
|
||||
}
|
||||
|
||||
static void renderDirectElement(VisualElement *element, Window *mainWindow) {
|
||||
renderNormalElement(element, mainWindow); // Meh
|
||||
}
|
||||
|
||||
void renderProject(Runtime *runtime, Window *mainWindow) {
|
||||
Common::Array<Structural *> scenes;
|
||||
runtime->getScenesInRenderOrder(scenes);
|
||||
|
||||
Common::Array<VisualElement *> normalBucket;
|
||||
Common::Array<VisualElement *> directBucket;
|
||||
|
||||
for (Common::Array<Structural *>::const_iterator it = scenes.begin(), itEnd = scenes.end(); it != itEnd; ++it) {
|
||||
size_t normalStart = normalBucket.size();
|
||||
size_t directStart = directBucket.size();
|
||||
|
||||
recursiveCollectDrawElements(*it, normalBucket, directBucket);
|
||||
|
||||
size_t normalEnd = normalBucket.size();
|
||||
size_t directEnd = directBucket.size();
|
||||
|
||||
if (normalEnd - normalStart > 1)
|
||||
Common::sort(normalBucket.begin() + normalStart, normalBucket.end(), visualElementLayerLess);
|
||||
if (directEnd - directStart > 1)
|
||||
Common::sort(directBucket.begin() + directStart, directBucket.end(), visualElementLayerLess);
|
||||
}
|
||||
|
||||
for (Common::Array<VisualElement *>::const_iterator it = normalBucket.begin(), itEnd = normalBucket.end(); it != itEnd; ++it)
|
||||
renderNormalElement(*it, mainWindow);
|
||||
|
||||
for (Common::Array<VisualElement *>::const_iterator it = directBucket.begin(), itEnd = directBucket.end(); it != itEnd; ++it)
|
||||
renderDirectElement(*it, mainWindow);
|
||||
}
|
||||
|
||||
} // End of namespace Render
|
||||
|
||||
} // End of namespace MTropolis
|
||||
|
@ -35,9 +35,12 @@ class ManagedSurface;
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
class Runtime;
|
||||
class Project;
|
||||
|
||||
class Window {
|
||||
public:
|
||||
Window(int32 x, int32 y, int16 width, int16 height, const Graphics::PixelFormat &format);
|
||||
Window(Runtime *runtime, int32 x, int32 y, int16 width, int16 height, const Graphics::PixelFormat &format);
|
||||
~Window();
|
||||
|
||||
int32 getX() const;
|
||||
@ -47,15 +50,20 @@ public:
|
||||
const Common::SharedPtr<Graphics::ManagedSurface> &getSurface() const;
|
||||
const Graphics::PixelFormat &getPixelFormat() const;
|
||||
|
||||
void close();
|
||||
void detachFromRuntime();
|
||||
|
||||
private:
|
||||
int32 _x;
|
||||
int32 _y;
|
||||
Common::SharedPtr<Graphics::ManagedSurface> _surface;
|
||||
Runtime *_runtime;
|
||||
};
|
||||
|
||||
namespace Render {
|
||||
|
||||
uint32 resolveRGB(uint8 r, uint8 g, uint8 b, const Graphics::PixelFormat &fmt);
|
||||
void renderProject(Runtime *runtime, Window *mainWindow);
|
||||
|
||||
} // End of namespace Render
|
||||
|
||||
|
@ -95,6 +95,20 @@ void ModifierChildMaterializer::visitChildStructuralRef(Common::SharedPtr<Struct
|
||||
}
|
||||
|
||||
void ModifierChildMaterializer::visitChildModifierRef(Common::SharedPtr<Modifier> &modifier) {
|
||||
if (modifier->isAlias()) {
|
||||
Common::SharedPtr<Modifier> templateModifier = _runtime->getProject()->resolveAlias(static_cast<AliasModifier *>(modifier.get())->getAliasID());
|
||||
if (!templateModifier) {
|
||||
error("Failed to resolve alias");
|
||||
}
|
||||
|
||||
if (!modifier->isVariable()) {
|
||||
Common::SharedPtr<Modifier> clonedModifier = templateModifier->shallowClone();
|
||||
clonedModifier->setName(modifier->getName());
|
||||
|
||||
modifier = clonedModifier;
|
||||
}
|
||||
}
|
||||
|
||||
modifier->materialize(_runtime, _outerScope);
|
||||
}
|
||||
|
||||
@ -1708,12 +1722,18 @@ VThreadState Structural::consumeCommand(Runtime *runtime, const Common::SharedPt
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
void Structural::activate() {
|
||||
}
|
||||
|
||||
void Structural::deactivate() {
|
||||
}
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
SupportStatus Structural::debugGetSupportStatus() const {
|
||||
return kSupportStatusNone;
|
||||
}
|
||||
|
||||
const Common::String& Structural::debugGetName() const {
|
||||
const Common::String &Structural::debugGetName() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
@ -2121,11 +2141,13 @@ bool Runtime::runFrame() {
|
||||
|
||||
_macFontMan.reset(new Graphics::MacFontManager(0, desc->getLanguage()));
|
||||
|
||||
_project.reset(new Project());
|
||||
_project.reset(new Project(this));
|
||||
_project->setSelfReference(_project);
|
||||
|
||||
_project->loadFromDescription(*desc);
|
||||
|
||||
ensureMainWindowExists();
|
||||
|
||||
_rootLinkingScope.addObject(_project->getStaticGUID(), _project->getName(), _project);
|
||||
|
||||
// We have to materialize global variables because they are not cloned from aliases.
|
||||
@ -2229,6 +2251,12 @@ void Runtime::drawFrame() {
|
||||
|
||||
_system->fillScreen(Render::resolveRGB(0, 0, 0, getRenderPixelFormat()));
|
||||
|
||||
{
|
||||
Common::SharedPtr<Window> mainWindow = _mainWindow.lock();
|
||||
if (mainWindow)
|
||||
Render::renderProject(this, mainWindow.get());
|
||||
}
|
||||
|
||||
for (Common::Array<Common::SharedPtr<Window> >::const_iterator it = _windows.begin(), itEnd = _windows.end(); it != itEnd; ++it) {
|
||||
const Window &window = *it->get();
|
||||
const Graphics::ManagedSurface &surface = *window.getSurface();
|
||||
@ -2292,6 +2320,8 @@ void Runtime::executeTeardown(const Teardown &teardown) {
|
||||
if (!structural)
|
||||
return; // Already gone
|
||||
|
||||
recursiveDeactivateStructural(structural.get());
|
||||
|
||||
if (teardown.onlyRemoveChildren) {
|
||||
structural->removeAllChildren();
|
||||
structural->removeAllModifiers();
|
||||
@ -2533,6 +2563,24 @@ void Runtime::executeSharedScenePostSceneChangeActions() {
|
||||
}
|
||||
}
|
||||
|
||||
void Runtime::recursiveDeactivateStructural(Structural *structural) {
|
||||
const Common::Array<Common::SharedPtr<Structural> > &children = structural->getChildren();
|
||||
for (Common::Array<Common::SharedPtr<Structural> >::const_iterator it = children.begin(), itEnd = children.end(); it != itEnd; ++it) {
|
||||
recursiveDeactivateStructural(it->get());
|
||||
}
|
||||
|
||||
structural->deactivate();
|
||||
}
|
||||
|
||||
void Runtime::recursiveActivateStructural(Structural *structural) {
|
||||
structural->activate();
|
||||
|
||||
const Common::Array<Common::SharedPtr<Structural> > &children = structural->getChildren();
|
||||
for (Common::Array<Common::SharedPtr<Structural> >::const_iterator it = children.begin(), itEnd = children.end(); it != itEnd; ++it) {
|
||||
recursiveActivateStructural(it->get());
|
||||
}
|
||||
}
|
||||
|
||||
void Runtime::queueEventAsLowLevelSceneStateTransitionAction(const Event &evt, Structural *root, bool cascade, bool relay) {
|
||||
Common::SharedPtr<MessageProperties> props(new MessageProperties(evt, DynamicValue(), Common::WeakPtr<RuntimeObject>()));
|
||||
Common::SharedPtr<MessageDispatch> msg(new MessageDispatch(props, root, cascade, relay, false));
|
||||
@ -2550,6 +2598,8 @@ void Runtime::loadScene(const Common::SharedPtr<Structural>& scene) {
|
||||
debug(1, "Scene loaded OK, materializing objects...");
|
||||
scene->materializeDescendents(this, subsection->getSceneLoadMaterializeScope());
|
||||
debug(1, "Scene materialized OK");
|
||||
recursiveActivateStructural(scene.get());
|
||||
debug(1, "Scene added to renderer OK");
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
if (_debugger) {
|
||||
@ -2599,6 +2649,12 @@ Scheduler &Runtime::getScheduler() {
|
||||
return _scheduler;
|
||||
}
|
||||
|
||||
void Runtime::getScenesInRenderOrder(Common::Array<Structural*> &scenes) const {
|
||||
for (Common::Array<SceneStackEntry>::const_iterator it = _sceneStack.begin(), itEnd = _sceneStack.end(); it != itEnd; ++it) {
|
||||
scenes.push_back(it->scene.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Runtime::ensureMainWindowExists() {
|
||||
// Maybe there's a better spot for this
|
||||
@ -2607,7 +2663,7 @@ void Runtime::ensureMainWindowExists() {
|
||||
|
||||
int32 centeredX = (static_cast<int32>(_displayWidth) - static_cast<int32>(presentationSettings.width)) / 2;
|
||||
int32 centeredY = (static_cast<int32>(_displayHeight) - static_cast<int32>(presentationSettings.height)) / 2;
|
||||
Common::SharedPtr<Window> mainWindow(new Window(centeredX, centeredY, presentationSettings.width, presentationSettings.height, _displayModePixelFormats[_realDisplayMode]));
|
||||
Common::SharedPtr<Window> mainWindow(new Window(this, centeredX, centeredY, presentationSettings.width, presentationSettings.height, _displayModePixelFormats[_realDisplayMode]));
|
||||
addWindow(mainWindow);
|
||||
_mainWindow.reset(mainWindow);
|
||||
}
|
||||
@ -2682,6 +2738,7 @@ void Runtime::addWindow(const Common::SharedPtr<Window> &window) {
|
||||
void Runtime::removeWindow(Window *window) {
|
||||
for (size_t i = 0; i < _windows.size(); i++) {
|
||||
if (_windows[i].get() == window) {
|
||||
window->detachFromRuntime();
|
||||
_windows.remove_at(i);
|
||||
break;
|
||||
}
|
||||
@ -2806,11 +2863,46 @@ const IPlugInModifierFactory *ProjectPlugInRegistry::findPlugInModifierFactory(c
|
||||
return it->_value;
|
||||
}
|
||||
|
||||
Project::Project()
|
||||
: _projectFormat(Data::kProjectFormatUnknown), _isBigEndian(false), _haveGlobalObjectInfo(false), _haveProjectStructuralDef(false) {
|
||||
|
||||
SegmentUnloadSignaller::SegmentUnloadSignaller(Project *project, int segmentIndex) : _project(project), _segmentIndex(segmentIndex) {
|
||||
}
|
||||
|
||||
SegmentUnloadSignaller::~SegmentUnloadSignaller() {
|
||||
}
|
||||
|
||||
void SegmentUnloadSignaller::onSegmentUnloaded() {
|
||||
_project = nullptr;
|
||||
|
||||
// Need to be careful here because a receiver may unload this object, causing _receivers.size() to be invalid
|
||||
const size_t numReceivers = _receivers.size();
|
||||
for (size_t i = 0; i < numReceivers; i++) {
|
||||
_receivers[i]->onSegmentUnloaded(_segmentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void SegmentUnloadSignaller::addReceiver(ISegmentUnloadSignalReceiver *receiver) {
|
||||
_receivers.push_back(receiver);
|
||||
}
|
||||
|
||||
void SegmentUnloadSignaller::removeReceiver(ISegmentUnloadSignalReceiver *receiver) {
|
||||
for (size_t i = 0; i < _receivers.size(); i++) {
|
||||
if (_receivers[i] == receiver) {
|
||||
_receivers.remove_at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Project::Segment::Segment() : weakStream(nullptr) {
|
||||
}
|
||||
|
||||
Project::Project(Runtime *runtime)
|
||||
: _runtime(runtime), _projectFormat(Data::kProjectFormatUnknown), _isBigEndian(false), _haveGlobalObjectInfo(false), _haveProjectStructuralDef(false) {
|
||||
}
|
||||
|
||||
Project::~Project() {
|
||||
for (size_t i = 0; i < _segments.size(); i++)
|
||||
closeSegmentStream(i);
|
||||
}
|
||||
|
||||
void Project::loadFromDescription(const ProjectDescription& desc) {
|
||||
@ -2927,7 +3019,9 @@ void Project::loadSceneFromStream(const Common::SharedPtr<Structural>& scene, ui
|
||||
error("Invalid stream ID");
|
||||
}
|
||||
|
||||
const StreamDesc &streamDesc = _streams[streamID - 1];
|
||||
size_t streamIndex = streamID - 1;
|
||||
|
||||
const StreamDesc &streamDesc = _streams[streamIndex];
|
||||
uint segmentIndex = streamDesc.segmentIndex;
|
||||
|
||||
openSegmentStream(segmentIndex);
|
||||
@ -2972,12 +3066,12 @@ void Project::loadSceneFromStream(const Common::SharedPtr<Structural>& scene, ui
|
||||
|
||||
if (Data::DataObjectTypes::isAsset(dataObjectType)) {
|
||||
// Asset defs can appear anywhere
|
||||
loadAssetDef(assetDefLoader, *dataObject.get());
|
||||
loadAssetDef(streamIndex, assetDefLoader, *dataObject.get());
|
||||
} else if (dataObjectType == Data::DataObjectTypes::kAssetDataChunk) {
|
||||
// Ignore
|
||||
continue;
|
||||
} else if (loaderStack.contexts.size() > 0) {
|
||||
loadContextualObject(loaderStack, *dataObject.get());
|
||||
loadContextualObject(streamIndex, loaderStack, *dataObject.get());
|
||||
} else {
|
||||
error("Unexpectedly exited scene context in loader");
|
||||
}
|
||||
@ -2990,6 +3084,7 @@ void Project::loadSceneFromStream(const Common::SharedPtr<Structural>& scene, ui
|
||||
}
|
||||
|
||||
scene->holdAssets(assetDefLoader.assets);
|
||||
assignAssets(assetDefLoader.assets);
|
||||
}
|
||||
|
||||
Common::SharedPtr<Modifier> Project::resolveAlias(uint32 aliasID) const {
|
||||
@ -3018,6 +3113,21 @@ bool Project::isProject() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::WeakPtr<Asset> Project::getAssetByID(uint32 assetID) const {
|
||||
if (assetID >= _assetsByID.size())
|
||||
return Common::WeakPtr<Asset>();
|
||||
|
||||
const AssetDesc *desc = _assetsByID[assetID];
|
||||
if (desc == nullptr)
|
||||
return Common::WeakPtr<Asset>();
|
||||
|
||||
return desc->asset;
|
||||
}
|
||||
|
||||
size_t Project::getSegmentForStreamIndex(size_t streamIndex) const {
|
||||
return _streams[streamIndex].segmentIndex;
|
||||
}
|
||||
|
||||
void Project::openSegmentStream(int segmentIndex) {
|
||||
if (segmentIndex < 0 || static_cast<size_t>(segmentIndex) > _segments.size()) {
|
||||
error("Invalid segment index %i", segmentIndex);
|
||||
@ -3040,6 +3150,31 @@ void Project::openSegmentStream(int segmentIndex) {
|
||||
error("Failed to open segment file %s", segment.desc.filePath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
segment.unloadSignaller.reset(new SegmentUnloadSignaller(this, segmentIndex));
|
||||
}
|
||||
|
||||
void Project::closeSegmentStream(int segmentIndex) {
|
||||
Segment &segment = _segments[segmentIndex];
|
||||
|
||||
if (!segment.weakStream)
|
||||
return;
|
||||
|
||||
segment.unloadSignaller->onSegmentUnloaded();
|
||||
segment.unloadSignaller.reset();
|
||||
segment.rcStream.reset();
|
||||
segment.weakStream = nullptr;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream* Project::getStreamForSegment(int segmentIndex) {
|
||||
return _segments[segmentIndex].weakStream;
|
||||
}
|
||||
|
||||
Common::SharedPtr<SegmentUnloadSignaller> Project::notifyOnSegmentUnload(int segmentIndex, ISegmentUnloadSignalReceiver *receiver) {
|
||||
Common::SharedPtr<SegmentUnloadSignaller> signaller = _segments[segmentIndex].unloadSignaller;
|
||||
if (signaller)
|
||||
signaller->addReceiver(receiver);
|
||||
return signaller;
|
||||
}
|
||||
|
||||
void Project::loadBootStream(size_t streamIndex) {
|
||||
@ -3069,12 +3204,12 @@ void Project::loadBootStream(size_t streamIndex) {
|
||||
|
||||
if (Data::DataObjectTypes::isAsset(dataObjectType)) {
|
||||
// Asset defs can appear anywhere
|
||||
loadAssetDef(assetDefLoader, *dataObject.get());
|
||||
loadAssetDef(streamIndex, assetDefLoader, *dataObject.get());
|
||||
} else if (dataObjectType == Data::DataObjectTypes::kAssetDataChunk) {
|
||||
// Ignore
|
||||
continue;
|
||||
} else if (loaderStack.contexts.size() > 0) {
|
||||
loadContextualObject(loaderStack, *dataObject.get());
|
||||
loadContextualObject(streamIndex, loaderStack, *dataObject.get());
|
||||
} else {
|
||||
// Root-level objects
|
||||
switch (dataObject->getType()) {
|
||||
@ -3120,6 +3255,7 @@ void Project::loadBootStream(size_t streamIndex) {
|
||||
}
|
||||
|
||||
holdAssets(assetDefLoader.assets);
|
||||
assignAssets(assetDefLoader.assets);
|
||||
}
|
||||
|
||||
void Project::loadPresentationSettings(const Data::PresentationSettings &presentationSettings) {
|
||||
@ -3167,7 +3303,7 @@ void Project::loadAssetCatalog(const Data::AssetCatalog &assetCatalog) {
|
||||
}
|
||||
}
|
||||
|
||||
void Project::loadGlobalObjectInfo(ChildLoaderStack& loaderStack, const Data::GlobalObjectInfo& globalObjectInfo) {
|
||||
void Project::loadGlobalObjectInfo(ChildLoaderStack &loaderStack, const Data::GlobalObjectInfo& globalObjectInfo) {
|
||||
if (_haveGlobalObjectInfo)
|
||||
error("Multiple global object infos");
|
||||
|
||||
@ -3280,7 +3416,28 @@ ObjectLinkingScope *Project::getPersistentModifierScope() {
|
||||
return &_modifierScope;
|
||||
}
|
||||
|
||||
void Project::loadContextualObject(ChildLoaderStack &stack, const Data::DataObject &dataObject) {
|
||||
void Project::assignAssets(const Common::Array<Common::SharedPtr<Asset> >& assets) {
|
||||
for (Common::Array<Common::SharedPtr<Asset> >::const_iterator it = assets.begin(), itEnd = assets.end(); it != itEnd; ++it) {
|
||||
Common::SharedPtr<Asset> asset = *it;
|
||||
uint32 assetID = asset->getAssetID();
|
||||
|
||||
if (assetID >= _assetsByID.size()) {
|
||||
warning("Bad asset ID %u", assetID);
|
||||
continue;
|
||||
}
|
||||
|
||||
AssetDesc *desc = _assetsByID[assetID];
|
||||
if (desc == nullptr) {
|
||||
warning("Asset attempting to use deleted asset slot %u", assetID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desc->asset.expired())
|
||||
desc->asset = asset;
|
||||
}
|
||||
}
|
||||
|
||||
void Project::loadContextualObject(size_t streamIndex, ChildLoaderStack &stack, const Data::DataObject &dataObject) {
|
||||
ChildLoaderContext &topContext = stack.contexts.back();
|
||||
const Data::DataObjectTypes::DataObjectType dataObjectType = dataObject.getType();
|
||||
|
||||
@ -3408,7 +3565,7 @@ void Project::loadContextualObject(ChildLoaderStack &stack, const Data::DataObje
|
||||
error("No element factory defined for structural object");
|
||||
}
|
||||
|
||||
ElementLoaderContext elementLoaderContext;
|
||||
ElementLoaderContext elementLoaderContext(_runtime, streamIndex);
|
||||
Common::SharedPtr<Element> element = elementFactory->createElement(elementLoaderContext, dataObject);
|
||||
|
||||
container->addChild(element);
|
||||
@ -3448,7 +3605,7 @@ void Project::loadContextualObject(ChildLoaderStack &stack, const Data::DataObje
|
||||
}
|
||||
}
|
||||
|
||||
void Project::loadAssetDef(AssetDefLoaderContext& context, const Data::DataObject& dataObject) {
|
||||
void Project::loadAssetDef(size_t streamIndex, AssetDefLoaderContext& context, const Data::DataObject& dataObject) {
|
||||
assert(Data::DataObjectTypes::isAsset(dataObject.getType()));
|
||||
|
||||
IAssetFactory *factory = getAssetFactoryForDataObjectType(dataObject.getType());
|
||||
@ -3457,7 +3614,7 @@ void Project::loadAssetDef(AssetDefLoaderContext& context, const Data::DataObjec
|
||||
return;
|
||||
}
|
||||
|
||||
AssetLoaderContext loaderContext;
|
||||
AssetLoaderContext loaderContext(streamIndex);
|
||||
context.assets.push_back(factory->createAsset(loaderContext, dataObject));
|
||||
}
|
||||
|
||||
@ -3519,11 +3676,23 @@ bool VisualElement::isVisible() const {
|
||||
return _visible;
|
||||
}
|
||||
|
||||
bool VisualElement::isDirectToScreen() const {
|
||||
return _directToScreen;
|
||||
}
|
||||
|
||||
uint16 VisualElement::getLayer() const {
|
||||
return _layer;
|
||||
}
|
||||
|
||||
bool VisualElement::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
|
||||
if (attrib == "visible") {
|
||||
result.setBool(_visible);
|
||||
return true;
|
||||
}
|
||||
if (attrib == "direct") {
|
||||
result.setBool(_directToScreen);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Element::readAttribute(thread, result, attrib);
|
||||
}
|
||||
@ -3533,11 +3702,16 @@ bool VisualElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWrit
|
||||
writeProxy = DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetVisibility>::create(this);
|
||||
return true;
|
||||
}
|
||||
if (attrib == "direct") {
|
||||
writeProxy = DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetDirect>::create(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Element::writeRefAttribute(thread, writeProxy, attrib);
|
||||
}
|
||||
|
||||
bool VisualElement::scriptSetVisibility(const DynamicValue& result) {
|
||||
// FIXME: Need to make this fire Show/Hide events!
|
||||
if (result.getType() == DynamicValueTypes::kBoolean) {
|
||||
_visible = result.getBool();
|
||||
return true;
|
||||
@ -3553,6 +3727,7 @@ bool VisualElement::loadCommon(const Common::String &name, uint32 guid, const Da
|
||||
_name = name;
|
||||
_guid = guid;
|
||||
_visible = ((elementFlags & Data::ElementFlags::kHidden) == 0);
|
||||
_directToScreen = ((elementFlags & Data::ElementFlags::kNotDirectToScreen) == 0);
|
||||
_streamLocator = streamLocator;
|
||||
_sectionID = sectionID;
|
||||
_layer = layer;
|
||||
@ -3560,6 +3735,26 @@ bool VisualElement::loadCommon(const Common::String &name, uint32 guid, const Da
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisualElement::scriptSetDirect(const DynamicValue &dest) {
|
||||
if (dest.getType() == DynamicValueTypes::kBoolean) {
|
||||
_directToScreen = dest.getBool();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VThreadState VisualElement::changeVisibilityTask(const ChangeFlagTaskData &taskData) {
|
||||
if (_visible != taskData.desiredFlag) {
|
||||
_visible = taskData.desiredFlag;
|
||||
|
||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(_visible ? EventIDs::kElementHide : EventIDs::kElementShow, 0), DynamicValue(), getSelfReference()));
|
||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||
taskData.runtime->sendMessageOnVThread(dispatch);
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
bool NonVisualElement::isVisual() const {
|
||||
return false;
|
||||
}
|
||||
|
@ -1060,6 +1060,8 @@ public:
|
||||
|
||||
Scheduler &getScheduler();
|
||||
|
||||
void getScenesInRenderOrder(Common::Array<Structural *> &scenes) const;
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
void debugSetEnabled(bool enabled);
|
||||
void debugBreak();
|
||||
@ -1111,6 +1113,9 @@ private:
|
||||
void executeCompleteTransitionToScene(const Common::SharedPtr<Structural> &scene);
|
||||
void executeSharedScenePostSceneChangeActions();
|
||||
|
||||
void recursiveDeactivateStructural(Structural *structural);
|
||||
void recursiveActivateStructural(Structural *structural);
|
||||
|
||||
void queueEventAsLowLevelSceneStateTransitionAction(const Event &evt, Structural *root, bool cascade, bool relay);
|
||||
|
||||
void loadScene(const Common::SharedPtr<Structural> &scene);
|
||||
@ -1281,6 +1286,9 @@ public:
|
||||
|
||||
virtual VThreadState consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg);
|
||||
|
||||
virtual void activate();
|
||||
virtual void deactivate();
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
SupportStatus debugGetSupportStatus() const override;
|
||||
const Common::String &debugGetName() const override;
|
||||
@ -1368,10 +1376,28 @@ private:
|
||||
Common::HashMap<Common::String, const IPlugInModifierFactory *> _factoryRegistry;
|
||||
};
|
||||
|
||||
class Project : public Structural {
|
||||
struct ISegmentUnloadSignalReceiver {
|
||||
virtual void onSegmentUnloaded(int segmentIndex) = 0;
|
||||
};
|
||||
|
||||
class SegmentUnloadSignaller {
|
||||
public:
|
||||
Project();
|
||||
explicit SegmentUnloadSignaller(Project *project, int segmentIndex);
|
||||
~SegmentUnloadSignaller();
|
||||
|
||||
void onSegmentUnloaded();
|
||||
void addReceiver(ISegmentUnloadSignalReceiver *receiver);
|
||||
void removeReceiver(ISegmentUnloadSignalReceiver *receiver);
|
||||
|
||||
private:
|
||||
Project *_project;
|
||||
int _segmentIndex;
|
||||
Common::Array<ISegmentUnloadSignalReceiver *> _receivers;
|
||||
};
|
||||
|
||||
class Project : public Structural {
|
||||
public:
|
||||
explicit Project(Runtime *runtime);
|
||||
~Project();
|
||||
|
||||
void loadFromDescription(const ProjectDescription &desc);
|
||||
@ -1384,6 +1410,13 @@ public:
|
||||
|
||||
bool isProject() const override;
|
||||
|
||||
Common::WeakPtr<Asset> getAssetByID(uint32 assetID) const;
|
||||
size_t getSegmentForStreamIndex(size_t streamIndex) const;
|
||||
void openSegmentStream(int segmentIndex);
|
||||
void closeSegmentStream(int segmentIndex);
|
||||
Common::SeekableReadStream *getStreamForSegment(int segmentIndex);
|
||||
Common::SharedPtr<SegmentUnloadSignaller> notifyOnSegmentUnload(int segmentIndex, ISegmentUnloadSignalReceiver *receiver);
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
const char *debugGetTypeName() const override { return "Project"; }
|
||||
#endif
|
||||
@ -1407,9 +1440,12 @@ private:
|
||||
};
|
||||
|
||||
struct Segment {
|
||||
Segment();
|
||||
|
||||
SegmentDescription desc;
|
||||
Common::SharedPtr<Common::SeekableReadStream> rcStream;
|
||||
Common::SeekableReadStream *weakStream;
|
||||
Common::SharedPtr<SegmentUnloadSignaller> unloadSignaller;
|
||||
};
|
||||
|
||||
enum StreamType {
|
||||
@ -1436,14 +1472,13 @@ private:
|
||||
Common::WeakPtr<Asset> asset;
|
||||
};
|
||||
|
||||
void openSegmentStream(int segmentIndex);
|
||||
void loadBootStream(size_t streamIndex);
|
||||
|
||||
void loadPresentationSettings(const Data::PresentationSettings &presentationSettings);
|
||||
void loadAssetCatalog(const Data::AssetCatalog &assetCatalog);
|
||||
void loadGlobalObjectInfo(ChildLoaderStack &loaderStack, const Data::GlobalObjectInfo &globalObjectInfo);
|
||||
void loadAssetDef(AssetDefLoaderContext &context, const Data::DataObject &dataObject);
|
||||
void loadContextualObject(ChildLoaderStack &stack, const Data::DataObject &dataObject);
|
||||
void loadAssetDef(size_t streamIndex, AssetDefLoaderContext &context, const Data::DataObject &dataObject);
|
||||
void loadContextualObject(size_t streamIndex, ChildLoaderStack &stack, const Data::DataObject &dataObject);
|
||||
Common::SharedPtr<Modifier> loadModifierObject(ModifierLoaderContext &loaderContext, const Data::DataObject &dataObject);
|
||||
void loadLabelMap(const Data::ProjectLabelMap &projectLabelMap);
|
||||
static size_t recursiveCountLabels(const Data::ProjectLabelMap::LabelTree &tree);
|
||||
@ -1451,6 +1486,8 @@ private:
|
||||
ObjectLinkingScope *getPersistentStructuralScope() override;
|
||||
ObjectLinkingScope *getPersistentModifierScope() override;
|
||||
|
||||
void assignAssets(const Common::Array<Common::SharedPtr<Asset> > &assets);
|
||||
|
||||
Common::Array<Segment> _segments;
|
||||
Common::Array<StreamDesc> _streams;
|
||||
Common::Array<LabelTree> _labelTree;
|
||||
@ -1475,6 +1512,8 @@ private:
|
||||
Common::SharedPtr<ProjectResources> _resources;
|
||||
ObjectLinkingScope _structuralScope;
|
||||
ObjectLinkingScope _modifierScope;
|
||||
|
||||
Runtime *_runtime;
|
||||
};
|
||||
|
||||
class Section : public Structural {
|
||||
@ -1532,15 +1571,29 @@ public:
|
||||
bool isVisual() const override;
|
||||
|
||||
bool isVisible() const;
|
||||
bool isDirectToScreen() const;
|
||||
uint16 getLayer() const;
|
||||
|
||||
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib);
|
||||
bool writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib);
|
||||
|
||||
bool scriptSetVisibility(const DynamicValue &result);
|
||||
|
||||
virtual void render(Window *window) = 0;
|
||||
|
||||
protected:
|
||||
bool loadCommon(const Common::String &name, uint32 guid, const Data::Rect &rect, uint32 elementFlags, uint16 layer, uint32 streamLocator, uint16 sectionID);
|
||||
|
||||
bool scriptSetDirect(const DynamicValue &dest);
|
||||
|
||||
struct ChangeFlagTaskData {
|
||||
bool desiredFlag;
|
||||
Runtime *runtime;
|
||||
};
|
||||
|
||||
VThreadState changeVisibilityTask(const ChangeFlagTaskData &taskData);
|
||||
|
||||
bool _directToScreen;
|
||||
bool _visible;
|
||||
Rect16 _rect;
|
||||
uint16 _layer;
|
||||
@ -1630,11 +1683,23 @@ public:
|
||||
virtual void getValue(DynamicValue &dest) const = 0;
|
||||
};
|
||||
|
||||
enum AssetType {
|
||||
kAssetTypeNone,
|
||||
|
||||
kAssetTypeMovie,
|
||||
kAssetTypeAudio,
|
||||
kAssetTypeColorTable,
|
||||
};
|
||||
|
||||
class Asset {
|
||||
public:
|
||||
Asset();
|
||||
virtual ~Asset();
|
||||
|
||||
uint32 getAssetID() const;
|
||||
|
||||
virtual AssetType getAssetType() const = 0;
|
||||
|
||||
protected:
|
||||
uint32 _assetID;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user