MTROPOLIS: Render MTI in 32-bit mode and fix more MTI things.

This commit is contained in:
elasota 2022-10-11 21:48:26 -04:00
parent 553271dd7e
commit 7730a6c364
6 changed files with 256 additions and 64 deletions

View File

@ -810,23 +810,19 @@ const Common::SharedPtr<Graphics::ManagedSurface> &CachedImage::optimize(Runtime
_optimizedSurface.reset(new Graphics::ManagedSurface());
_optimizedSurface->create(w, h, renderFmt);
Render::convert32To16(*_optimizedSurface, *_surface);
_colorDepth = kColorDepthMode16Bit;
} else if (renderDepth == kColorDepthMode32Bit && _colorDepth == kColorDepthMode16Bit) {
_optimizedSurface.reset(new Graphics::ManagedSurface());
_optimizedSurface->create(w, h, renderFmt);
Render::convert16To32(*_optimizedSurface, *_surface);
_colorDepth = kColorDepthMode32Bit;
} else {
_optimizedSurface = _surface; // Can't optimize
}
} else {
static const byte bwPalette[6] = {255, 255, 255, 0, 0, 0};
const byte *palette = nullptr;
if (_colorDepth == kColorDepthMode16Bit || _colorDepth == kColorDepthMode32Bit)
palette = bwPalette;
_surface->convertToInPlace(renderFmt, palette);
_optimizedSurface = _surface;
_optimizedSurface = _surface; // Doesn't need to be optimized
}
return _optimizedSurface;
@ -911,8 +907,6 @@ const Common::SharedPtr<CachedImage> &ImageAsset::loadAndCacheImage(Runtime *run
if (_imageCache)
return _imageCache;
ColorDepthMode renderDepth = runtime->getRealColorDepth();
size_t streamIndex = getStreamIndex();
int segmentIndex = runtime->getProject()->getSegmentForStreamIndex(streamIndex);
runtime->getProject()->openSegmentStream(segmentIndex);
@ -1054,7 +1048,7 @@ const Common::SharedPtr<CachedImage> &ImageAsset::loadAndCacheImage(Runtime *run
}
_imageCache.reset(new CachedImage());
_imageCache->resetSurface(renderDepth, imageSurface);
_imageCache->resetSurface(getColorDepth(), imageSurface);
return _imageCache;
}

View File

@ -1081,6 +1081,15 @@ void ImageElement::render(Window *window) {
Common::Rect srcRect(optimized->w, optimized->h);
Common::Rect destRect(_cachedAbsoluteOrigin.x, _cachedAbsoluteOrigin.y, _cachedAbsoluteOrigin.x + _rect.width(), _cachedAbsoluteOrigin.y + _rect.height());
if (optimized->format.bytesPerPixel == 1) {
const Palette *palette = getPalette().get();
if (!palette)
palette = &_runtime->getGlobalPalette();
// FIXME: Pass palette to blit functions instead
optimized->setPalette(palette->getPalette(), 0, 256);
}
uint8 alpha = _transitionProps.getAlpha();
if (inkMode == VisualElementRenderProperties::kInkModeBackgroundMatte || inkMode == VisualElementRenderProperties::kInkModeBackgroundTransparent) {
@ -1238,7 +1247,6 @@ bool MToonElement::canAutoPlay() const {
void MToonElement::render(Window *window) {
if (_cachedMToon) {
_cachedMToon->optimize(_runtime);
uint32 frame = _cel - 1;
@ -1246,6 +1254,15 @@ void MToonElement::render(Window *window) {
_cachedMToon->getOrRenderFrame(_renderedFrame, frame, _renderSurface);
if (_renderSurface->format.bytesPerPixel == 1) {
const Palette *palette = getPalette().get();
if (!palette)
palette = &_runtime->getGlobalPalette();
// FIXME: Should support passing the palette to the blit function instead
_renderSurface->setPalette(palette->getPalette(), 0, 256);
}
_renderedFrame = frame;
// This is a bit suboptimal since we don't need to render the frame if invisible, but

View File

@ -287,16 +287,18 @@ VThreadState ColorTableModifier::consumeMessage(Runtime *runtime, const Common::
if (ctabAsset->getAssetType() == kAssetTypeColorTable) {
const ColorRGB8 *colors = static_cast<ColorTableAsset *>(ctabAsset.get())->getColors();
byte palette[256 * 3];
for (int i = 0; i < 256; i++) {
byte *paletteColor = palette + i * 3;
const ColorRGB8 &clr = colors[i];
paletteColor[0] = clr.r;
paletteColor[1] = clr.g;
paletteColor[2] = clr.b;
}
Palette palette(colors);
g_system->getPaletteManager()->setPalette(palette, 0, 256);
if (runtime->getFakeColorDepth() <= kColorDepthMode8Bit) {
runtime->setGlobalPalette(palette);
} else {
Structural *structural = this->findStructuralOwner();
if (structural != nullptr && structural->isElement() && static_cast<Element *>(structural)->isVisual()) {
static_cast<VisualElement *>(structural)->setPalette(Common::SharedPtr<Palette>(new Palette(palette)));
} else {
warning("Attempted to apply a color table to a non-element");
}
}
} else {
error("Color table modifier applied an asset that wasn't a color table");
}

View File

@ -142,6 +142,7 @@ Common::Error MTropolisEngine::run() {
preferredWidth = 640;
preferredHeight = 480;
preferredColorDepthMode = kColorDepthMode8Bit;
enhancedColorDepthMode = kColorDepthMode32Bit;
}
if (ConfMan.getBool("mtropolis_mod_minimum_transition_duration"))

View File

@ -27,6 +27,7 @@
#include "graphics/cursorman.h"
#include "graphics/managed_surface.h"
#include "graphics/palette.h"
#include "graphics/surface.h"
#include "graphics/wincursor.h"
#include "graphics/maccursor.h"
@ -46,6 +47,44 @@
namespace MTropolis {
int32 displayModeToBitDepth(ColorDepthMode displayMode) {
switch (displayMode) {
case kColorDepthMode1Bit:
return 1;
case kColorDepthMode2Bit:
return 2;
case kColorDepthMode4Bit:
return 4;
case kColorDepthMode8Bit:
return 8;
case kColorDepthMode16Bit:
return 16;
case kColorDepthMode32Bit:
return 32;
default:
return 0;
}
}
ColorDepthMode bitDepthToDisplayMode(int32 bits) {
switch (bits) {
case 1:
return kColorDepthMode1Bit;
case 2:
return kColorDepthMode2Bit;
case 4:
return kColorDepthMode4Bit;
case 8:
return kColorDepthMode8Bit;
case 16:
return kColorDepthMode16Bit;
case 32:
return kColorDepthMode32Bit;
default:
return kColorDepthModeInvalid;
}
}
class MainWindow : public Window {
public:
MainWindow(const WindowParameters &windowParams);
@ -1733,6 +1772,25 @@ void DynamicValueWriteStringHelper::create(Common::String *strValue, DynamicValu
proxy.pod.ifc = DynamicValueWriteInterfaceGlue<DynamicValueWriteStringHelper>::getInstance();
}
MiniscriptInstructionOutcome DynamicValueWriteDiscardHelper::write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) {
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome DynamicValueWriteDiscardHelper::refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) {
return kMiniscriptInstructionOutcomeFailed;
}
MiniscriptInstructionOutcome DynamicValueWriteDiscardHelper::refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index) {
return kMiniscriptInstructionOutcomeFailed;
}
void DynamicValueWriteDiscardHelper::create(DynamicValueWriteProxy &proxy) {
proxy.pod.ptrOrOffset = 0;
proxy.pod.objectRef = nullptr;
proxy.pod.ifc = DynamicValueWriteInterfaceGlue<DynamicValueWriteDiscardHelper>::getInstance();
}
MiniscriptInstructionOutcome DynamicValueWritePointHelper::write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) {
if (value.getType() != DynamicValueTypes::kPoint) {
thread->error("Can't set point to invalid type");
@ -2448,6 +2506,13 @@ bool WorldManagerInterface::readAttribute(MiniscriptThread *thread, DynamicValue
else
result.clear();
return true;
} else if (attrib == "monitordepth") {
int bitDepth = displayModeToBitDepth(thread->getRuntime()->getFakeColorDepth());
if (bitDepth <= 0)
return false;
result.setInt(bitDepth);
return true;
}
return RuntimeObject::readAttribute(thread, result, attrib);
@ -2590,44 +2655,6 @@ MiniscriptInstructionOutcome SystemInterface::writeRefAttribute(MiniscriptThread
return RuntimeObject::writeRefAttribute(thread, result, attrib);
}
int32 SystemInterface::displayModeToBitDepth(ColorDepthMode displayMode) {
switch (displayMode) {
case kColorDepthMode1Bit:
return 1;
case kColorDepthMode2Bit:
return 2;
case kColorDepthMode4Bit:
return 4;
case kColorDepthMode8Bit:
return 8;
case kColorDepthMode16Bit:
return 16;
case kColorDepthMode32Bit:
return 32;
default:
return 0;
}
}
ColorDepthMode SystemInterface::bitDepthToDisplayMode(int32 bits) {
switch (bits) {
case 1:
return kColorDepthMode1Bit;
case 2:
return kColorDepthMode2Bit;
case 4:
return kColorDepthMode4Bit;
case 8:
return kColorDepthMode8Bit;
case 16:
return kColorDepthMode16Bit;
case 32:
return kColorDepthMode32Bit;
default:
return kColorDepthModeInvalid;
}
}
MiniscriptInstructionOutcome SystemInterface::setEjectCD(MiniscriptThread *thread, const DynamicValue &value) {
if (value.getType() != DynamicValueTypes::kBoolean)
return kMiniscriptInstructionOutcomeFailed;
@ -2667,7 +2694,7 @@ MiniscriptInstructionOutcome SystemInterface::setMonitorBitDepth(MiniscriptThrea
if (!value.roundToInt(asInteger))
return kMiniscriptInstructionOutcomeFailed;
const ColorDepthMode depthMode = SystemInterface::bitDepthToDisplayMode(asInteger);
const ColorDepthMode depthMode = bitDepthToDisplayMode(asInteger);
if (depthMode != kColorDepthModeInvalid) {
thread->getRuntime()->switchDisplayMode(thread->getRuntime()->getRealColorDepth(), depthMode);
}
@ -2687,6 +2714,33 @@ MiniscriptInstructionOutcome SystemInterface::setVolumeName(MiniscriptThread *th
StructuralHooks::~StructuralHooks() {
}
AssetManagerInterface::AssetManagerInterface() {
}
bool AssetManagerInterface::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
if (attrib == "volumeismounted") {
int volID = 0;
bool isMounted = false;
bool hasVolume = thread->getRuntime()->getVolumeState(_opString.c_str(), volID, isMounted);
result.setBool(hasVolume && isMounted);
return true;
}
return false;
}
MiniscriptInstructionOutcome AssetManagerInterface::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
if (attrib == "opstring") {
DynamicValueWriteStringHelper::create(&_opString, result);
return kMiniscriptInstructionOutcomeContinue;
}
if (attrib == "cdeject") {
DynamicValueWriteDiscardHelper::create(result);
return kMiniscriptInstructionOutcomeContinue;
}
return kMiniscriptInstructionOutcomeFailed;
}
void StructuralHooks::onCreate(Structural *structural) {
}
@ -3851,6 +3905,62 @@ void SceneTransitionHooks::onSceneTransitionSetup(Runtime *runtime, const Common
void SceneTransitionHooks::onSceneTransitionEnded(Runtime *runtime, const Common::WeakPtr<Structural> &newScene) {
}
Palette::Palette() {
int outColorIndex = 0;
for (int rb = 0; rb < 6; rb++) {
for (int rg = 0; rg < 6; rg++) {
for (int rr = 0; rr < 6; rr++) {
byte *color = _colors + outColorIndex * 3;
outColorIndex++;
color[0] = 255 - rr * 51;
color[1] = 255 - rg * 51;
color[2] = 255 - rb * 51;
}
}
}
outColorIndex--;
for (int ch = 0; ch < 4; ch++) {
for (int ri = 0; ri < 16; ri++) {
if (ri % 3 == 0)
continue;
byte *color = _colors + outColorIndex * 3;
outColorIndex++;
byte intensity = 255 - ri * 17;
if (ch == 4) {
color[0] = color[1] = color[2] = intensity;
} else {
color[0] = color[1] = color[2] = 0;
color[ch] = intensity;
}
}
}
assert(outColorIndex == 255);
_colors[255 * 3 + 0] = 0;
_colors[255 * 3 + 1] = 0;
_colors[255 * 3 + 2] = 0;
}
Palette::Palette(const ColorRGB8 *colors) {
for (int i = 0; i < 256; i++) {
_colors[i * 3 + 0] = colors[i].r;
_colors[i * 3 + 1] = colors[i].g;
_colors[i * 3 + 2] = colors[i].b;
}
}
const byte *Palette::getPalette() const {
return _colors;
}
Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, ISaveUIProvider *saveProvider, ILoadUIProvider *loadProvider, const Common::SharedPtr<SubtitleRenderer> &subRenderer)
: _system(system), _mixer(mixer), _saveProvider(saveProvider), _loadProvider(loadProvider),
_nextRuntimeGUID(1), _realDisplayMode(kColorDepthModeInvalid), _fakeDisplayMode(kColorDepthModeInvalid),
@ -5700,6 +5810,19 @@ const Common::Array<IPostEffect *> &Runtime::getPostEffects() const {
return _postEffects;
}
const Palette &Runtime::getGlobalPalette() const {
return _globalPalette;
}
void Runtime::setGlobalPalette(const Palette &palette) {
if (_realDisplayMode <= kColorDepthMode8Bit)
g_system->getPaletteManager()->setPalette(palette.getPalette(), 0, 256);
else
setSceneGraphDirty();
_globalPalette = palette;
}
void Runtime::checkBoundaries() {
// Boundary Detection Messenger behavior is very quirky in mTropolis 1.1. Basically, if an object moves in the direction of
// the boundary, then it may trigger collision checks with the boundary. If it moves but does not move in the direction of
@ -6312,6 +6435,15 @@ VThreadState Project::consumeCommand(Runtime *runtime, const Common::SharedPtr<M
return Structural::consumeCommand(runtime, msg);
}
MiniscriptInstructionOutcome Project::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
if (attrib == "allowquit") {
DynamicValueWriteDiscardHelper::create(result);
return kMiniscriptInstructionOutcomeContinue;
}
return Structural::writeRefAttribute(thread, result, attrib);
}
void Project::loadFromDescription(const ProjectDescription &desc, const Hacks &hacks) {
_resources = desc.getResources();
_cursorGraphics = desc.getCursorGraphics();
@ -7723,6 +7855,15 @@ void VisualElement::finalizeRender() {
_contentsDirty = false;
}
void VisualElement::setPalette(const Common::SharedPtr<Palette> &palette) {
_palette = palette;
_contentsDirty = true;
}
const Common::SharedPtr<Palette> &VisualElement::getPalette() const {
return _palette;
}
#ifdef MTROPOLIS_DEBUG_ENABLE
void VisualElement::debugInspect(IDebugInspectionReport *report) const {
report->declareDynamic("layer", Common::String::format("%i", static_cast<int>(_layer)));

View File

@ -1003,6 +1003,14 @@ struct DynamicValueWriteStringHelper {
static void create(Common::String *strValue, DynamicValueWriteProxy &proxy);
};
struct DynamicValueWriteDiscardHelper {
static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset);
static MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib);
static MiniscriptInstructionOutcome refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index);
static void create(DynamicValueWriteProxy &proxy);
};
template<class TClass, MiniscriptInstructionOutcome (TClass::*TWriteMethod)(MiniscriptThread *thread, const DynamicValue &dest), MiniscriptInstructionOutcome (TClass::*TRefAttribMethod)(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib)>
struct DynamicValueWriteOrRefAttribFuncHelper {
static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) {
@ -1485,6 +1493,17 @@ public:
virtual void onSceneTransitionEnded(Runtime *runtime, const Common::WeakPtr<Structural> &newScene);
};
class Palette {
public:
Palette();
explicit Palette(const ColorRGB8 *colors);
const byte *getPalette() const;
private:
byte _colors[256 * 3];
};
class Runtime {
public:
explicit Runtime(OSystem *system, Audio::Mixer *mixer, ISaveUIProvider *saveProvider, ILoadUIProvider *loadProvider, const Common::SharedPtr<SubtitleRenderer> &subRenderer);
@ -1604,6 +1623,9 @@ public:
void removePostEffect(IPostEffect *postEffect);
const Common::Array<IPostEffect *> &getPostEffects() const;
const Palette &getGlobalPalette() const;
void setGlobalPalette(const Palette &palette);
const Common::String *resolveAttributeIDName(uint32 attribID) const;
const Common::WeakPtr<Window> &getMainWindow() const;
@ -1851,6 +1873,8 @@ private:
Common::Array<IPostEffect *> _postEffects;
Palette _globalPalette;
Common::SharedPtr<SubtitleRenderer> _subtitleRenderer;
Hacks _hacks;
@ -1957,6 +1981,14 @@ private:
};
class AssetManagerInterface : public RuntimeObject {
public:
AssetManagerInterface();
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
private:
Common::String _opString;
};
class SystemInterface : public RuntimeObject {
@ -1970,9 +2002,6 @@ public:
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
private:
static ColorDepthMode bitDepthToDisplayMode(int32 bitDepth);
static int32 displayModeToBitDepth(ColorDepthMode displayMode);
MiniscriptInstructionOutcome setEjectCD(MiniscriptThread *thread, const DynamicValue &value);
MiniscriptInstructionOutcome setGameMode(MiniscriptThread *thread, const DynamicValue &value);
MiniscriptInstructionOutcome setMasterVolume(MiniscriptThread *thread, const DynamicValue &value);
@ -1980,6 +2009,7 @@ private:
MiniscriptInstructionOutcome setVolumeName(MiniscriptThread *thread, const DynamicValue &value);
Common::String _volumeName;
Common::String _opString;
int _masterVolume;
};
@ -2238,6 +2268,8 @@ public:
VThreadState consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
void loadFromDescription(const ProjectDescription &desc, const Hacks &hacks);
void loadSceneFromStream(const Common::SharedPtr<Structural> &structural, uint32 streamID, const Hacks &hacks);
@ -2611,6 +2643,9 @@ public:
virtual void render(Window *window) = 0;
void finalizeRender();
void setPalette(const Common::SharedPtr<Palette> &palette);
const Common::SharedPtr<Palette> &getPalette() const;
#ifdef MTROPOLIS_DEBUG_ENABLE
void debugInspect(IDebugInspectionReport *report) const override;
#endif
@ -2663,6 +2698,8 @@ protected:
VisualElementTransitionProperties _transitionProps;
Common::SharedPtr<Palette> _palette;
Common::Rect _prevRect;
bool _contentsDirty;
};