From 91d19d0a737176ffd936d69a46cdab7b57f9dfef Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Tue, 12 Aug 2014 21:50:59 +0200 Subject: [PATCH] Implement RGSS2 Window class (WindowVX) --- binding-mri/binding-mri.cpp | 11 +- binding-mri/windowvx-binding.cpp | 153 +++++ mkxp.pro | 11 +- src/scene.h | 2 + src/windowvx.cpp | 1041 ++++++++++++++++++++++++++++++ src/windowvx.h | 76 +++ 6 files changed, 1292 insertions(+), 2 deletions(-) create mode 100644 binding-mri/windowvx-binding.cpp create mode 100644 src/windowvx.cpp create mode 100644 src/windowvx.h diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp index cfeb720..1be0289 100644 --- a/binding-mri/binding-mri.cpp +++ b/binding-mri/binding-mri.cpp @@ -59,6 +59,10 @@ void planeBindingInit(); void windowBindingInit(); void tilemapBindingInit(); +#ifdef RGSS2 +void windowVXBindingInit(); +#endif + void inputBindingInit(); void audioBindingInit(); void graphicsBindingInit(); @@ -83,9 +87,14 @@ static void mriBindingInit() spriteBindingInit(); viewportBindingInit(); planeBindingInit(); - windowBindingInit(); tilemapBindingInit(); +#ifdef RGSS2 + windowVXBindingInit(); +#else + windowBindingInit(); +#endif + inputBindingInit(); audioBindingInit(); graphicsBindingInit(); diff --git a/binding-mri/windowvx-binding.cpp b/binding-mri/windowvx-binding.cpp new file mode 100644 index 0000000..31bf940 --- /dev/null +++ b/binding-mri/windowvx-binding.cpp @@ -0,0 +1,153 @@ +/* +** window-binding.cpp +** +** This file is part of mkxp. +** +** Copyright (C) 2013 Jonas Kulla +** +** mkxp is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 2 of the License, or +** (at your option) any later version. +** +** mkxp is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with mkxp. If not, see . +*/ + +#include "windowvx.h" +#include "disposable-binding.h" +#include "viewportelement-binding.h" +#include "binding-util.h" + +DEF_TYPE(WindowVX); + +RB_METHOD(windowVXInitialize) +{ + int x, y, width, height; + x = y = width = height = 0; + + if (argc == 4) + rb_get_args(argc, argv, "iiii", &x, &y, &width, &height RB_ARG_END); + + WindowVX *w = new WindowVX(x, y, width, height); + + setPrivateData(self, w); + + w->setCursorRect(new Rect); + w->setTone(new Tone); + wrapNilProperty(self, "windowskin"); + wrapNilProperty(self, "contents"); + wrapProperty(self, w->getTone(), "tone", ToneType); + wrapProperty(self, w->getCursorRect(), "cursor_rect", RectType); + + return self; +} + +RB_METHOD(windowVXUpdate) +{ + RB_UNUSED_PARAM; + + WindowVX *w = getPrivateData(self); + + w->update(); + + return Qnil; +} + +RB_METHOD(windowVXMove) +{ + WindowVX *w = getPrivateData(self); + + int x, y, width, height; + rb_get_args(argc, argv, "iiii", &x, &y, &width, &height RB_ARG_END); + + w->move(x, y, width, height); + + return Qnil; +} + +RB_METHOD(windowVXIsOpen) +{ + RB_UNUSED_PARAM; + + WindowVX *w = getPrivateData(self); + + return rb_bool_new(w->isOpen()); +} + +RB_METHOD(windowVXIsClosed) +{ + RB_UNUSED_PARAM; + + WindowVX *w = getPrivateData(self); + + return rb_bool_new(w->isClosed()); +} + +DEF_PROP_OBJ_NIL(WindowVX, Bitmap, Windowskin, "windowskin") +DEF_PROP_OBJ_NIL(WindowVX, Bitmap, Contents, "contents") + +DEF_PROP_OBJ(WindowVX, Rect, CursorRect, "cursor_rect") +DEF_PROP_OBJ(WindowVX, Tone, Tone, "tone") + +DEF_PROP_I(WindowVX, X) +DEF_PROP_I(WindowVX, Y) +DEF_PROP_I(WindowVX, OX) +DEF_PROP_I(WindowVX, OY) +DEF_PROP_I(WindowVX, Width) +DEF_PROP_I(WindowVX, Height) +DEF_PROP_I(WindowVX, Padding) +DEF_PROP_I(WindowVX, PaddingBottom) +DEF_PROP_I(WindowVX, Opacity) +DEF_PROP_I(WindowVX, BackOpacity) +DEF_PROP_I(WindowVX, ContentsOpacity) +DEF_PROP_I(WindowVX, Openness) + +DEF_PROP_B(WindowVX, Active) +DEF_PROP_B(WindowVX, ArrowsVisible) +DEF_PROP_B(WindowVX, Pause) + +void +windowVXBindingInit() +{ + // FIXME: data type name will end up as "WindowVX" + INIT_TYPE(WindowVX); + + VALUE klass = rb_define_class("Window", rb_cObject); + rb_define_alloc_func(klass, classAllocate<&WindowVXType>); + + disposableBindingInit (klass); + viewportElementBindingInit(klass); + + _rb_define_method(klass, "initialize", windowVXInitialize); + + _rb_define_method(klass, "update", windowVXUpdate); + _rb_define_method(klass, "move", windowVXMove); + _rb_define_method(klass, "open?", windowVXIsOpen); + _rb_define_method(klass, "closed?", windowVXIsClosed); + + INIT_PROP_BIND( WindowVX, Windowskin, "windowskin" ); + INIT_PROP_BIND( WindowVX, Contents, "contents" ); + INIT_PROP_BIND( WindowVX, CursorRect, "cursor_rect" ); + INIT_PROP_BIND( WindowVX, Active, "active" ); + INIT_PROP_BIND( WindowVX, ArrowsVisible, "arrows_visible" ); + INIT_PROP_BIND( WindowVX, Pause, "pause" ); + INIT_PROP_BIND( WindowVX, X, "x" ); + INIT_PROP_BIND( WindowVX, Y, "y" ); + INIT_PROP_BIND( WindowVX, Width, "width" ); + INIT_PROP_BIND( WindowVX, Height, "height" ); + INIT_PROP_BIND( WindowVX, OX, "ox" ); + INIT_PROP_BIND( WindowVX, OY, "oy" ); + INIT_PROP_BIND( WindowVX, Padding, "padding" ); + INIT_PROP_BIND( WindowVX, PaddingBottom, "padding_bottom" ); + INIT_PROP_BIND( WindowVX, Opacity, "opacity" ); + INIT_PROP_BIND( WindowVX, BackOpacity, "back_opacity" ); + INIT_PROP_BIND( WindowVX, ContentsOpacity, "contents_opacity" ); + INIT_PROP_BIND( WindowVX, Openness, "openness" ); + INIT_PROP_BIND( WindowVX, Tone, "tone" ); +} diff --git a/mkxp.pro b/mkxp.pro index cdcd039..2695f4b 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -196,8 +196,12 @@ EMBED = \ assets/liberation.ttf RGSS2 { + HEADERS += \ + src/windowvx.h + SOURCES += \ - src/vorbissource.cpp + src/vorbissource.cpp \ + src/windowvx.cpp EMBED += \ shader/blur.frag \ @@ -311,6 +315,11 @@ BINDING_MRI { binding-mri/audio-binding.cpp \ binding-mri/module_rpg.cpp \ binding-mri/filesystem-binding.cpp + + RGSS2 { + SOURCES += \ + binding-mri/windowvx-binding.cpp + } } OTHER_FILES += $$EMBED diff --git a/src/scene.h b/src/scene.h index 9aa916c..a0e23e3 100644 --- a/src/scene.h +++ b/src/scene.h @@ -29,6 +29,7 @@ class SceneElement; class Viewport; +class WindowVX; class Window; struct ScanRow; struct TilemapPrivate; @@ -63,6 +64,7 @@ protected: friend class SceneElement; friend class Window; + friend class WindowVX; friend struct ScanRow; }; diff --git a/src/windowvx.cpp b/src/windowvx.cpp new file mode 100644 index 0000000..56ab784 --- /dev/null +++ b/src/windowvx.cpp @@ -0,0 +1,1041 @@ +/* +** window-vx.cpp +** +** This file is part of mkxp. +** +** Copyright (C) 2014 Jonas Kulla +** +** mkxp is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 2 of the License, or +** (at your option) any later version. +** +** mkxp is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with mkxp. If not, see . +*/ + +#include "windowvx.h" + +#include "bitmap.h" +#include "etc.h" +#include "etc-internal.h" +#include "quad.h" +#include "quadarray.h" +#include "sharedstate.h" +#include "texpool.h" +#include "tilequad.h" +#include "glstate.h" +#include "shader.h" + +#include +#include + +template +struct Sides +{ + T l, r, t, b; +}; + +template +struct Corners +{ + T tl, tr, bl, br; +}; + +static const IntRect bgStretchSrc( 0, 0, 64, 64 ); +static const IntRect bgTileSrc ( 0, 64, 64, 64 ); + +static const Corners cornerSrc = +{ + IntRect( 64, 0, 16, 16 ), + IntRect( 112, 0, 16, 16 ), + IntRect( 64, 48, 16, 16 ), + IntRect( 112, 48, 16, 16 ) +}; + +static const Sides borderSrc = +{ + IntRect( 64, 16, 16, 32 ), + IntRect( 112, 16, 16, 32 ), + IntRect( 80, 0, 32, 16 ), + IntRect( 80, 48, 32, 16 ) +}; + +static const Sides scrollArrowSrc = +{ + IntRect( 80, 24, 8, 16 ), + IntRect( 104, 24, 8, 16 ), + IntRect( 88, 16, 16, 8 ), + IntRect( 88, 40, 16, 8 ) +}; + +static const IntRect pauseSrc[4] = +{ + IntRect( 96, 64, 16, 16 ), + IntRect( 112, 64, 16, 16 ), + IntRect( 96, 80, 16, 16 ), + IntRect( 112, 80, 16, 16 ) +}; + +struct CursorSrc +{ + Corners corners; + Sides border; + IntRect bg; +}; + +static const CursorSrc cursorSrc = +{ + { + IntRect( 64, 64, 4, 4 ), + IntRect( 92, 64, 4, 4 ), + IntRect( 64, 92, 4, 4 ), + IntRect( 92, 92, 4, 4 ) + }, + { + IntRect( 64, 68, 4, 24 ), + IntRect( 92, 68, 4, 24 ), + IntRect( 68, 64, 24, 4 ), + IntRect( 68, 92, 24, 4 ) + }, + IntRect( 68, 68, 24, 24 ) +}; + +static const uint8_t cursorAlpha[] = +{ + /* Fade out */ + 0xFF, 0xF7, 0xEF, 0xE7, 0xDF, 0xD7, 0xCF, 0xC7, 0xBF, 0xB7, + 0xAF, 0xA7, 0x9F, 0x97, 0x8F, 0x87, 0x7F, 0x77, 0x6F, 0x67, + /* Fade in */ + 0x5F, 0x67, 0x6F, 0x77, 0x7F, 0x87, 0x8F, 0x97, 0x9F, 0xA7, + 0xAF, 0xB7, 0xBF, 0xC7, 0xCF, 0xD7, 0xDF, 0xE7, 0xEF, 0xF7 +}; + +static elementsN(cursorAlpha); + +static const uint8_t cursorAlphaResetIdx = 0x10; + +/* No cycle */ +static const uint8_t pauseAlpha[] = +{ + 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0, + 0xFF +}; + +static elementsN(pauseAlpha); + +/* Cycling */ +static const uint8_t pauseQuad[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 +}; + +static elementsN(pauseQuad); + +typedef DisposeWatch BitmapWatch; + +struct WindowVXPrivate +{ + Bitmap *windowskin; + BitmapWatch windowskinWatch; + + Bitmap *contents; + BitmapWatch contentsWatch; + + Rect *cursorRect; + bool active; + bool arrowsVisible; + bool pause; + + /* Externally visible width/height */ + int width, height; + + /* Window geometry (x, Y, width, height) + * with non-negative size */ + IntRect geo; + + /* ox / oy */ + Vec2i contentsOff; + + int padding; + int paddingBottom; + + NormValue opacity; + NormValue backOpacity; + NormValue contentsOpacity; + + NormValue openness; + Tone *tone; + + sigc::connection cursorRectCon; + sigc::connection toneCon; + sigc::connection prepareCon; + + EtcTemps tmp; + + struct + { + TEXFBO tex; + ColorQuadArray vert; + size_t bgTileQuads; + size_t borderQuads; + Quad quad; + + bool vertDirty; + bool texSizeDirty; + bool texDirty; + } base; + + ColorQuadArray ctrlVert; + size_t ctrlQuads; + Vertex *pauseVert; + + Quad contentsQuad; + + IntRect padRect; + IntRect clipRect; + + ColorQuadArray cursorVert; + + bool ctrlVertDirty; + bool ctrlVertArrayDirty; + bool clipRectDirty; + bool cursorVertDirty; + bool cursorVertArrayDirty; + + uint8_t pauseAlphaIdx; + uint8_t pauseQuadIdx; + uint8_t cursorAlphaIdx; + + Vec2i sceneOffset; + + WindowVXPrivate(int x, int y, int w, int h) + : windowskin(0), + windowskinWatch(*this, windowskin), + contents(0), + contentsWatch(*this, contents), + cursorRect(&tmp.rect), + active(true), + arrowsVisible(true), + pause(false), + width(w), + height(h), + geo(x, y, w, h), + padding(12), + paddingBottom(padding), + opacity(255), + backOpacity(192), + contentsOpacity(255), + openness(255), + tone(&tmp.tone), + pauseAlphaIdx(0), + pauseQuadIdx(0) + { + /* 4 scroll arrows + pause */ + ctrlVert.resize(4 + 1); + pauseVert = &ctrlVert.vertices[4*4]; + + base.vertDirty = true; + base.texSizeDirty = true; + base.texDirty = true; + ctrlVertDirty = true; + clipRectDirty = true; + + prepareCon = shState->prepareDraw.connect + (sigc::mem_fun(this, &WindowVXPrivate::prepare)); + } + + ~WindowVXPrivate() + { + shState->texPool().release(base.tex); + + cursorRectCon.disconnect(); + toneCon.disconnect(); + prepareCon.disconnect(); + } + + void invalidateCursorVert() + { + cursorVertDirty = true; + } + + void invalidateBaseTex() + { + base.texDirty = true; + } + + void refreshCursorRectCon() + { + cursorRectCon.disconnect(); + cursorRectCon = cursorRect->valueChanged.connect + (sigc::mem_fun(this, &WindowVXPrivate::invalidateCursorVert)); + } + + void refreshToneCon() + { + toneCon.disconnect(); + toneCon = tone->valueChanged.connect + (sigc::mem_fun(this, &WindowVXPrivate::invalidateBaseTex)); + } + + void updateBaseTexSize() + { + if (base.tex.width >= geo.w && base.tex.height >= geo.h) + return; + + if (base.tex.tex != TEX::ID(0)) + { + TEX::bind(base.tex.tex); + TEX::setSmooth(false); // XXX make pool set this up at alloc time + } + + shState->texPool().release(base.tex); + TEXFBO::clear(base.tex); + + if (geo.w == 0 || geo.h == 0) + return; + + base.tex = shState->texPool().request(geo.w, geo.h); + TEX::bind(base.tex.tex); + TEX::setSmooth(true); + + updateBaseQuad(); + } + + void rebuildBaseVert() + { + if (geo.w == 0 || geo.h == 0) + { + base.vert.clear(); + base.vert.commit(); + + return; + } + + const IntRect bgPos(2, 2, geo.w-4, geo.h-4); + size_t count = 0; + + /* Stretched layer (1) */ + count += 1; + + /* Tiled layer (2) */ + base.bgTileQuads = TileQuads::twoDimCount(bgTileSrc.w, bgTileSrc.h, bgPos.w, bgPos.h); + count += base.bgTileQuads; + + const Vec2 corOff(geo.w - 16, geo.h - 16); + + const Corners cornerPos = + { + FloatRect( 0, 0, 16, 16 ), /* Top left */ + FloatRect( corOff.x, 0, 16, 16 ), /* Top right */ + FloatRect( 0, corOff.y, 16, 16 ), /* Bottom left */ + FloatRect( corOff.x, corOff.y, 16, 16 ) /* Bottom right */ + }; + + const Vec2i sideLen(geo.w - 16*2, geo.h - 16*2); + + bool drawSidesLR = sideLen.x > 0; + bool drawSidesTB = sideLen.y > 0; + + base.borderQuads = 0; + base.borderQuads += 4; /* 4 corners */ + + if (drawSidesLR) + base.borderQuads += TileQuads::oneDimCount(32, sideLen.y) * 2; + + if (drawSidesTB) + base.borderQuads += TileQuads::oneDimCount(32, sideLen.x) * 2; + + count += base.borderQuads; + + base.vert.resize(count); + + Vertex *vert = &base.vert.vertices[0]; + size_t i = 0; + + /* Stretched background */ + i += Quad::setTexPosRect(&vert[i*4], bgStretchSrc, bgPos); + + /* Tiled background */ + i += TileQuads::build(bgTileSrc, bgPos, &vert[i*4]); + + /* Corners */ + i += Quad::setTexPosRect(&vert[i*4], cornerSrc.tl, cornerPos.tl); + i += Quad::setTexPosRect(&vert[i*4], cornerSrc.tr, cornerPos.tr); + i += Quad::setTexPosRect(&vert[i*4], cornerSrc.bl, cornerPos.bl); + i += Quad::setTexPosRect(&vert[i*4], cornerSrc.br, cornerPos.br); + + /* Sides */ + if (drawSidesLR) + { + i += TileQuads::buildV(borderSrc.l, sideLen.y, 0, 16, &vert[i*4]); + i += TileQuads::buildV(borderSrc.r, sideLen.y, corOff.x, 16, &vert[i*4]); + } + + if (drawSidesTB) + { + i += TileQuads::buildH(borderSrc.t, sideLen.x, 16, 0, &vert[i*4]); + i += TileQuads::buildH(borderSrc.b, sideLen.x, 16, corOff.y, &vert[i*4]); + } + + base.vert.commit(); + } + + void redrawBaseTex() + { + if (!windowskin) + return; + + if (base.tex.tex == TEX::ID(0)) + return; + + FBO::bind(base.tex.fbo); + + /* Clear texture */ + glState.clearColor.pushSet(Vec4()); + FBO::clear(); + glState.clearColor.pop(); + + glState.viewport.pushSet(IntRect(0, 0, base.tex.width, base.tex.height)); + glState.blend.pushSet(false); + + ShaderBase *shader; + + if (backOpacity < 255 || tone->hasEffect()) + { + PlaneShader &planeShader = shState->shaders().plane; + planeShader.bind(); + + planeShader.setColor(Vec4()); + planeShader.setFlash(Vec4()); + planeShader.setTone(tone->norm); + planeShader.setOpacity(backOpacity.norm); + + shader = &planeShader; + } + else + { + shader = &shState->shaders().simple; + shader->bind(); + } + + windowskin->bindTex(*shader); + TEX::setSmooth(true); + + shader->setTranslation(Vec2i()); + shader->applyViewportProj(); + + /* Draw stretched layer */ + base.vert.draw(0, 1); + + glState.blend.set(true); + glState.blendMode.pushSet(BlendKeepDestAlpha); + + /* Draw tiled layer */ + base.vert.draw(1, base.bgTileQuads); + + glState.blendMode.set(BlendNormal); + + /* If we used plane shader before, switch to simple */ + if (shader != &shState->shaders().simple) + { + shader = &shState->shaders().simple; + shader->bind(); + shader->setTranslation(Vec2i()); + shader->applyViewportProj(); + windowskin->bindTex(*shader); + } + + base.vert.draw(1+base.bgTileQuads, base.borderQuads); + + TEX::setSmooth(false); + + glState.blendMode.pop(); + glState.blend.pop(); + glState.viewport.pop(); + } + + void updateBaseQuad() + { + const FloatRect tex(0, 0, base.tex.width, base.tex.height); + const FloatRect pos(0, (geo.h / 2.0) * (1 - openness.norm), + geo.w, geo.h * openness.norm); + + base.quad.setTexPosRect(tex, pos); + } + + void updateClipRect() + { + SDL_Rect winRect = { 0, 0, geo.w, geo.h }; + + padRect.x = padRect.y = padding; + padRect.w = std::max(0, geo.w - padding*2); + padRect.h = std::max(0, geo.h - (padding+paddingBottom)); + + SDL_Rect tmp = padRect; + + SDL_IntersectRect(&winRect, &tmp, &tmp); + clipRect = IntRect(tmp.x, tmp.y, tmp.w, tmp.h); + + ctrlVertDirty = true; + } + + void rebuildCtrlVert() + { + const int arrowTBX = (geo.w - 16) / 2; + const int arrowLRY = (geo.h - 16) / 2; + + const Sides arrowPos = + { + FloatRect( 4, arrowLRY, 8, 16 ), /* Left */ + FloatRect( geo.w - 12, arrowLRY, 8, 16 ), /* Right */ + FloatRect( arrowTBX, 4, 16, 8 ), /* Top */ + FloatRect( arrowTBX, geo.h - 12, 16, 8 ) /* Bottom */ + }; + + size_t i = 0; + Vertex *vert = &ctrlVert.vertices[0]; + + if (contents && arrowsVisible) + { + if (contentsOff.x > 0) + i += Quad::setTexPosRect(&vert[i*4], scrollArrowSrc.l, arrowPos.l); + if (contentsOff.y > 0) + i += Quad::setTexPosRect(&vert[i*4], scrollArrowSrc.t, arrowPos.t); + + if (padRect.w < (contents->width() - contentsOff.x)) + i += Quad::setTexPosRect(&vert[i*4], scrollArrowSrc.r, arrowPos.r); + + if (padRect.h < (contents->height() - contentsOff.y)) + i += Quad::setTexPosRect(&vert[i*4], scrollArrowSrc.b, arrowPos.b); + } + + pauseVert = 0; + + if (pause) + { + const FloatRect pausePos(arrowTBX, geo.h - 16, 16, 16); + pauseVert = &vert[i*4]; + + i += Quad::setTexPosRect(&vert[i*4], pauseSrc[0], pausePos); + } + + ctrlQuads = i; + ctrlVertArrayDirty = true; + } + + void rebuildCursorVert() + { + const IntRect rect = cursorRect->toIntRect(); + const CursorSrc &src = cursorSrc; + + cursorVertArrayDirty = true; + + if (rect.w <= 0 || rect.h <= 0) + { + cursorVert.clear(); + return; + } + + const Vec2 corOff(rect.w - 4, rect.h - 4); + + const Corners cornerPos = + { + FloatRect( 0, 0, 4, 4 ), /* Top left */ + FloatRect( corOff.x, 0, 4, 4 ), /* Top right */ + FloatRect( 0, corOff.y, 4, 4 ), /* Bottom left */ + FloatRect( corOff.x, corOff.y, 4, 4 ) /* Bottom right */ + }; + + const Vec2i sideLen(rect.w - 4*2, rect.h - 4*2); + + const Sides sidePos = + { + FloatRect( 0, 4, 4, sideLen.y ), /* Left */ + FloatRect( corOff.x, 4, 4, sideLen.y ), /* Right */ + FloatRect( 4, 0, sideLen.x, 4 ), /* Top */ + FloatRect( 4, corOff.y, sideLen.x, 4 ) /* Bottom */ + }; + + const FloatRect bgPos(4, 4, sideLen.x, sideLen.y); + + bool drawSidesLR = rect.h > 8; + bool drawSidesTB = rect.w > 8; + bool drawBg = drawSidesLR && drawSidesTB; + + size_t quads = 0; + quads += 4; /* 4 corners */ + + if (drawSidesLR) + quads += 2; + + if (drawSidesTB) + quads += 2; + + if (drawBg) + quads += 1; + + cursorVert.resize(quads); + Vertex *vert = &cursorVert.vertices[0]; + size_t i = 0; + + i += Quad::setTexPosRect(&vert[i*4], src.corners.tl, cornerPos.tl); + i += Quad::setTexPosRect(&vert[i*4], src.corners.tr, cornerPos.tr); + i += Quad::setTexPosRect(&vert[i*4], src.corners.bl, cornerPos.bl); + i += Quad::setTexPosRect(&vert[i*4], src.corners.br, cornerPos.br); + + if (drawSidesLR) + { + i += Quad::setTexPosRect(&vert[i*4], src.border.l, sidePos.l); + i += Quad::setTexPosRect(&vert[i*4], src.border.r, sidePos.r); + } + + if (drawSidesTB) + { + i += Quad::setTexPosRect(&vert[i*4], src.border.t, sidePos.t); + i += Quad::setTexPosRect(&vert[i*4], src.border.b, sidePos.b); + } + + if (drawBg) + Quad::setTexPosRect(&vert[i*4], src.bg, bgPos); + } + + void updatePauseQuad() + { + if (!pauseVert) + return; + + /* Set quad */ + Quad::setTexRect(pauseVert, pauseSrc[pauseQuad[pauseQuadIdx]]); + + /* Set opacity */ + Quad::setColor(pauseVert, Vec4(1, 1, 1, pauseAlpha[pauseAlphaIdx] / 255.0)); + + ctrlVertArrayDirty = true; + } + + void updateCursorAlpha() + { + if (cursorVert.count() == 0) + return; + + Vec4 color(1, 1, 1, cursorAlpha[cursorAlphaIdx] / 255.0); + + for (size_t i = 0; i < cursorVert.count(); ++i) + Quad::setColor(&cursorVert.vertices[i*4], color); + + cursorVertArrayDirty = true; + } + + void stepAnimations() + { + if (active) + if (++cursorAlphaIdx == cursorAlphaN) + cursorAlphaIdx = 0; + + if (pause) + { + if (pauseAlphaIdx < pauseAlphaN-1) + ++pauseAlphaIdx; + + if (++pauseQuadIdx == pauseQuadN) + pauseQuadIdx = 0; + } + } + + void prepare() + { + if (base.vertDirty) + { + rebuildBaseVert(); + base.vertDirty = false; + base.texDirty = true; + } + + if (base.texSizeDirty) + { + updateBaseTexSize(); + base.texSizeDirty = false; + base.texDirty = true; + } + + if (base.texDirty) + { + redrawBaseTex(); + base.texDirty = false; + } + + if (clipRectDirty) + { + updateClipRect(); + clipRectDirty = false; + } + + if (ctrlVertDirty) + { + rebuildCtrlVert(); + updatePauseQuad(); + ctrlVertDirty = false; + } + + if (ctrlVertArrayDirty) + { + ctrlVert.commit(); + ctrlVertArrayDirty = false; + } + + if (cursorVertDirty) + { + rebuildCursorVert(); + updateCursorAlpha(); + cursorVertDirty = false; + } + + if (cursorVertArrayDirty) + { + cursorVert.commit(); + cursorVertArrayDirty = false; + } + } + + void draw() + { + if (base.tex.tex == TEX::ID(0)) + return; + + Vec2i trans(geo.x + sceneOffset.x, + geo.y + sceneOffset.y); + + SimpleAlphaShader &shader = shState->shaders().simpleAlpha; + shader.bind(); + shader.applyViewportProj(); + + if (windowskin) + { + shader.setTranslation(trans); + shader.setTexSize(Vec2i(base.tex.width, base.tex.height)); + + TEX::bind(base.tex.tex); + base.quad.draw(); + + if (openness < 255) + return; + + windowskin->bindTex(shader); + + TEX::setSmooth(true); + ctrlVert.draw(0, ctrlQuads); + TEX::setSmooth(false); + } + + if (openness < 255) + return; + + bool drawCursor = cursorVert.count() > 0 && windowskin; + + if (drawCursor || contents) + { + /* Translate cliprect from local into screen space */ + IntRect clip = clipRect; + clip.x += trans.x; + clip.y += trans.y; + + glState.scissorBox.push(); + glState.scissorTest.pushSet(true); + glState.scissorBox.setIntersect(clip); + + IntRect pad = padRect; + pad.x += trans.x; + pad.y += trans.y; + + if (drawCursor) + { + Vec2i contTrans = pad.pos(); + contTrans.x += std::abs(-contentsOff.x); + contTrans.y += std::abs(-contentsOff.y); + shader.setTranslation(contTrans); + + TEX::setSmooth(true); + cursorVert.draw(); + TEX::setSmooth(false); + } + + if (contents) + { + Vec2i contTrans = pad.pos(); + contTrans.x -= contentsOff.x; + contTrans.y -= contentsOff.y; + shader.setTranslation(contTrans); + + TEX::setSmooth(false); // XXX + contents->bindTex(shader); + contentsQuad.draw(); + } + + glState.scissorBox.pop(); + glState.scissorTest.pop(); + } + + TEX::setSmooth(false); // XXX FIND out a way to eliminate + } +}; + +WindowVX::WindowVX(Viewport *viewport) + : ViewportElement(viewport, 100) +{ + p = new WindowVXPrivate(0, 0, 0, 0); + onGeometryChange(scene->getGeometry()); +} + +WindowVX::WindowVX(int x, int y, int width, int height) + : ViewportElement(0, 100) +{ + p = new WindowVXPrivate(x, y, width, height); + onGeometryChange(scene->getGeometry()); +} + +WindowVX::~WindowVX() +{ + unlink(); + + delete p; +} + +void WindowVX::update() +{ + p->stepAnimations(); + + p->updatePauseQuad(); + p->updateCursorAlpha(); +} + +void WindowVX::move(int x, int y, int width, int height) +{ + p->width = width; + p->height = height; + + const Vec2i size(std::max(0, width), std::max(0, height)); + + if (p->geo.w != size.x || p->geo.h != size.y) + p->base.texSizeDirty = true; + + p->geo = IntRect(x, y, size.x, size.y); +} + +bool WindowVX::isOpen() const +{ + return p->openness == 255; +} + +bool WindowVX::isClosed() const +{ + return p->openness == 0; +} + +DEF_ATTR_SIMPLE(WindowVX, X, int, p->geo.x) +DEF_ATTR_SIMPLE(WindowVX, Y, int, p->geo.y) + +DEF_ATTR_RD_SIMPLE(WindowVX, Windowskin, Bitmap*, p->windowskin) +DEF_ATTR_RD_SIMPLE(WindowVX, Contents, Bitmap*, p->contents) +DEF_ATTR_RD_SIMPLE(WindowVX, CursorRect, Rect*, p->cursorRect) +DEF_ATTR_RD_SIMPLE(WindowVX, Active, bool, p->active) +DEF_ATTR_RD_SIMPLE(WindowVX, ArrowsVisible, bool, p->arrowsVisible) +DEF_ATTR_RD_SIMPLE(WindowVX, Pause, bool, p->pause) +DEF_ATTR_RD_SIMPLE(WindowVX, Width, int, p->width) +DEF_ATTR_RD_SIMPLE(WindowVX, Height, int, p->height) +DEF_ATTR_RD_SIMPLE(WindowVX, OX, int, p->contentsOff.x) +DEF_ATTR_RD_SIMPLE(WindowVX, OY, int, p->contentsOff.y) +DEF_ATTR_RD_SIMPLE(WindowVX, Padding, int, p->padding) +DEF_ATTR_RD_SIMPLE(WindowVX, PaddingBottom, int, p->paddingBottom) +DEF_ATTR_RD_SIMPLE(WindowVX, Opacity, int, p->opacity) +DEF_ATTR_RD_SIMPLE(WindowVX, BackOpacity, int, p->backOpacity) +DEF_ATTR_RD_SIMPLE(WindowVX, ContentsOpacity, int, p->contentsOpacity) +DEF_ATTR_RD_SIMPLE(WindowVX, Openness, int, p->openness) +DEF_ATTR_RD_SIMPLE(WindowVX, Tone, Tone*, p->tone) + +void WindowVX::setWindowskin(Bitmap *value) +{ + if (p->windowskin == value) + return; + + p->windowskin = value; + p->windowskinWatch.update(value); + p->base.texDirty = true; +} + +void WindowVX::setContents(Bitmap *value) +{ + if (p->contents == value) + return; + + p->contents = value; + p->contentsWatch.update(value); + + FloatRect rect = p->contents->rect(); + p->contentsQuad.setTexPosRect(rect, rect); +} + +void WindowVX::setCursorRect(Rect *value) +{ + if (p->cursorRect == value) + return; + + p->cursorRect = value; + p->cursorVertDirty = true; + p->refreshCursorRectCon(); +} + +void WindowVX::setActive(bool value) +{ + if (p->active == value) + return; + + p->active = value; + p->cursorAlphaIdx = cursorAlphaResetIdx; + p->updateCursorAlpha(); +} + +void WindowVX::setArrowsVisible(bool value) +{ + if (p->arrowsVisible == value) + return; + + p->arrowsVisible = value; + p->ctrlVertDirty = true; +} + +void WindowVX::setPause(bool value) +{ + if (p->pause == value) + return; + + p->pause = value; + p->pauseAlphaIdx = 0; + p->pauseQuadIdx = 0; + p->ctrlVertDirty = true; +} + +void WindowVX::setWidth(int value) +{ + if (p->width == value) + return; + + p->width = value; + p->geo.w = std::max(0, value); + p->base.vertDirty = true; + p->base.texSizeDirty = true; + p->ctrlVertDirty = true; +} + +void WindowVX::setHeight(int value) +{ + if (p->height == value) + return; + + p->height = value; + p->geo.h = std::max(0, value); + p->base.vertDirty = true; + p->base.texSizeDirty = true; + p->ctrlVertDirty = true; +} + +void WindowVX::setOX(int value) +{ + if (p->contentsOff.x == value) + return; + + p->contentsOff.x = value; + p->ctrlVertDirty = true; +} + +void WindowVX::setOY(int value) +{ + if (p->contentsOff.y == value) + return; + + p->contentsOff.y = value; + p->ctrlVertDirty = true; +} + +void WindowVX::setPadding(int value) +{ + if (p->padding == value) + return; + + p->padding = value; + p->paddingBottom = value; + p->clipRectDirty = true; +} + +void WindowVX::setPaddingBottom(int value) +{ + if (p->paddingBottom == value) + return; + + p->paddingBottom = value; + p->clipRectDirty = true; +} + +void WindowVX::setOpacity(int value) +{ + if (p->opacity == value) + return; + + p->opacity = value; + p->base.quad.setColor(Vec4(1, 1, 1, p->opacity.norm)); +} + +void WindowVX::setBackOpacity(int value) +{ + if (p->backOpacity == value) + return; + + p->backOpacity = value; + p->base.texDirty = true; +} + +void WindowVX::setContentsOpacity(int value) +{ + if (p->contentsOpacity == value) + return; + + p->contentsOpacity = value; + p->contentsQuad.setColor(Vec4(1, 1, 1, p->contentsOpacity.norm)); +} + +void WindowVX::setOpenness(int value) +{ + if (p->openness == value) + return; + + p->openness = value; + p->updateBaseQuad(); +} + +void WindowVX::setTone(Tone *value) +{ + if (p->tone == value) + return; + + p->tone = value; + p->refreshToneCon(); +} + +void WindowVX::draw() +{ + p->draw(); +} + +void WindowVX::onGeometryChange(const Scene::Geometry &geo) +{ + p->sceneOffset.x = geo.rect.x - geo.xOrigin; + p->sceneOffset.y = geo.rect.y - geo.yOrigin; +} diff --git a/src/windowvx.h b/src/windowvx.h new file mode 100644 index 0000000..3d42410 --- /dev/null +++ b/src/windowvx.h @@ -0,0 +1,76 @@ +/* +** window-vx.h +** +** This file is part of mkxp. +** +** Copyright (C) 2014 Jonas Kulla +** +** mkxp is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 2 of the License, or +** (at your option) any later version. +** +** mkxp is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with mkxp. If not, see . +*/ + +#ifndef WINDOWVX_H +#define WINDOWVX_H + +#include "viewport.h" +#include "disposable.h" + +#include "util.h" + +class Bitmap; +struct Rect; +struct Tone; + +struct WindowVXPrivate; + +class WindowVX : public ViewportElement, public Disposable +{ +public: + WindowVX(Viewport *viewport = 0); + WindowVX(int x, int y, int width, int height); + ~WindowVX(); + + void update(); + + void move(int x, int y, int width, int height); + bool isOpen() const; + bool isClosed() const; + + DECL_ATTR( Windowskin, Bitmap* ) + DECL_ATTR( Contents, Bitmap* ) + DECL_ATTR( CursorRect, Rect* ) + DECL_ATTR( Active, bool ) + DECL_ATTR( ArrowsVisible, bool ) + DECL_ATTR( Pause, bool ) + DECL_ATTR( X, int ) + DECL_ATTR( Y, int ) + DECL_ATTR( Width, int ) + DECL_ATTR( Height, int ) + DECL_ATTR( OX, int ) + DECL_ATTR( OY, int ) + DECL_ATTR( Padding, int ) + DECL_ATTR( PaddingBottom, int ) + DECL_ATTR( Opacity, int ) + DECL_ATTR( BackOpacity, int ) + DECL_ATTR( ContentsOpacity, int ) + DECL_ATTR( Openness, int ) + DECL_ATTR( Tone, Tone* ) + +private: + WindowVXPrivate *p; + + void draw(); + void onGeometryChange(const Scene::Geometry &); +}; + +#endif // WINDOWVX_H