aboutsummaryrefslogtreecommitdiff
path: root/engines/sword25/gfx/renderobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sword25/gfx/renderobject.cpp')
-rw-r--r--engines/sword25/gfx/renderobject.cpp560
1 files changed, 560 insertions, 0 deletions
diff --git a/engines/sword25/gfx/renderobject.cpp b/engines/sword25/gfx/renderobject.cpp
new file mode 100644
index 0000000000..5de6dde79e
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.cpp
@@ -0,0 +1,560 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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.
+
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/renderobject.h"
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+#include "sword25/gfx/renderobjectregistry.h"
+#include "sword25/gfx/renderobjectmanager.h"
+#include "sword25/gfx/graphicengine.h"
+
+#include "sword25/gfx/bitmap.h"
+#include "sword25/gfx/staticbitmap.h"
+#include "sword25/gfx/dynamicbitmap.h"
+#include "sword25/gfx/animation.h"
+#include "sword25/gfx/panel.h"
+#include "sword25/gfx/text.h"
+#include "sword25/gfx/animationtemplate.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEROBJECT"
+
+// Konstruktion / Destruktion
+// --------------------------
+RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
+ _managerPtr(0),
+ _parentPtr(parentPtr),
+ _x(0),
+ _y(0),
+ _z(0),
+ _oldX(-1),
+ _oldY(-1),
+ _oldZ(-1),
+ _width(0),
+ _height(0),
+ _visible(true),
+ _oldVisible(false),
+ _childChanged(true),
+ _type(type),
+ _initSuccess(false),
+ _refreshForced(true),
+ _handle(0) {
+
+ // Renderobject registrieren, abhängig vom Handle-Parameter entweder mit beliebigem oder vorgegebenen Handle.
+ if (handle == 0)
+ _handle = RenderObjectRegistry::getInstance().registerObject(this);
+ else
+ _handle = RenderObjectRegistry::getInstance().registerObject(this, handle);
+
+ if (_handle == 0)
+ return;
+
+ updateAbsolutePos();
+
+ // Dieses Objekt zu den Kindern der Elternobjektes hinzufügen, falls nicht Wurzel (ParentPtr ungültig) und dem
+ // selben RenderObjektManager zuweisen.
+ if (_parentPtr.isValid()) {
+ _managerPtr = _parentPtr->getManager();
+ _parentPtr->addObject(this->getHandle());
+ } else {
+ if (getType() != TYPE_ROOT) {
+ BS_LOG_ERRORLN("Tried to create a non-root render object and has no parent. All non-root render objects have to have a parent.");
+ return;
+ }
+ }
+
+ updateObjectState();
+
+ _initSuccess = true;
+}
+
+RenderObject::~RenderObject() {
+ // Objekt aus dem Elternobjekt entfernen.
+ if (_parentPtr.isValid())
+ _parentPtr->detatchChildren(this->getHandle());
+
+ deleteAllChildren();
+
+ // Objekt deregistrieren.
+ RenderObjectRegistry::getInstance().deregisterObject(this);
+}
+
+// Rendern
+// -------
+bool RenderObject::render() {
+ // Objektänderungen validieren
+ validateObject();
+
+ // Falls das Objekt nicht sichtbar ist, muss gar nichts gezeichnet werden
+ if (!_visible)
+ return true;
+
+ // Falls notwendig, wird die Renderreihenfolge der Kinderobjekte aktualisiert.
+ if (_childChanged) {
+ sortRenderObjects();
+ _childChanged = false;
+ }
+
+ // Objekt zeichnen.
+ doRender();
+
+ // Dann müssen die Kinder gezeichnet werden
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (!(*it)->render())
+ return false;
+
+ return true;
+}
+
+// Objektverwaltung
+// ----------------
+
+void RenderObject::validateObject() {
+ // Die Veränderungen in den Objektvariablen aufheben
+ _oldBbox = _bbox;
+ _oldVisible = _visible;
+ _oldX = _x;
+ _oldY = _y;
+ _oldZ = _z;
+ _refreshForced = false;
+}
+
+bool RenderObject::updateObjectState() {
+ // Falls sich das Objekt verändert hat, muss der interne Zustand neu berechnet werden und evtl. Update-Regions für den nächsten Frame
+ // registriert werden.
+ if ((calcBoundingBox() != _oldBbox) ||
+ (_visible != _oldVisible) ||
+ (_x != _oldX) ||
+ (_y != _oldY) ||
+ (_z != _oldZ) ||
+ _refreshForced) {
+ // Renderrang des Objektes neu bestimmen, da sich dieser verändert haben könnte
+ if (_parentPtr.isValid())
+ _parentPtr->signalChildChange();
+
+ // Die Bounding-Box neu berechnen und Update-Regions registrieren.
+ updateBoxes();
+
+ // Änderungen Validieren
+ validateObject();
+ }
+
+ // Dann muss der Objektstatus der Kinder aktualisiert werden.
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (!(*it)->updateObjectState())
+ return false;
+
+ return true;
+}
+
+void RenderObject::updateBoxes() {
+ // Bounding-Box aktualisieren
+ _bbox = calcBoundingBox();
+}
+
+Common::Rect RenderObject::calcBoundingBox() const {
+ // Die Bounding-Box mit der Objektgröße initialisieren.
+ Common::Rect bbox(0, 0, _width, _height);
+
+ // Die absolute Position der Bounding-Box berechnen.
+ bbox.translate(_absoluteX, _absoluteY);
+
+ // Die Bounding-Box am Elternobjekt clippen.
+ if (_parentPtr.isValid()) {
+ bbox.clip(_parentPtr->getBbox());
+ }
+
+ return bbox;
+}
+
+void RenderObject::calcAbsolutePos(int &x, int &y) const {
+ x = calcAbsoluteX();
+ y = calcAbsoluteY();
+}
+
+int RenderObject::calcAbsoluteX() const {
+ if (_parentPtr.isValid())
+ return _parentPtr->getAbsoluteX() + _x;
+ else
+ return _x;
+}
+
+int RenderObject::calcAbsoluteY() const {
+ if (_parentPtr.isValid())
+ return _parentPtr->getAbsoluteY() + _y;
+ else
+ return _y;
+}
+
+// Baumverwaltung
+// --------------
+
+void RenderObject::deleteAllChildren() {
+ // Es ist nicht notwendig die Liste zu iterieren, da jedes Kind für sich DetatchChildren an diesem Objekt aufruft und sich somit
+ // selber entfernt. Daher muss immer nur ein beliebiges Element (hier das letzte) gelöscht werden, bis die Liste leer ist.
+ while (!_children.empty()) {
+ RenderObjectPtr<RenderObject> curPtr = _children.back();
+ curPtr.erase();
+ }
+}
+
+bool RenderObject::addObject(RenderObjectPtr<RenderObject> pObject) {
+ if (!pObject.isValid()) {
+ BS_LOG_ERRORLN("Tried to add a null object to a renderobject.");
+ return false;
+ }
+
+ // Objekt in die Kinderliste einfügen.
+ _children.push_back(pObject);
+
+ // Sicherstellen, dass vor dem nächsten Rendern die Renderreihenfolge aktualisiert wird.
+ if (_parentPtr.isValid())
+ _parentPtr->signalChildChange();
+
+ return true;
+}
+
+bool RenderObject::detatchChildren(RenderObjectPtr<RenderObject> pObject) {
+ // Kinderliste durchgehen und Objekt entfernen falls vorhanden
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (*it == pObject) {
+ _children.erase(it);
+ return true;
+ }
+
+ BS_LOG_ERRORLN("Tried to detach children from a render object that isn't its parent.");
+ return false;
+}
+
+void RenderObject::sortRenderObjects() {
+ Common::sort(_children.begin(), _children.end(), greater);
+}
+
+void RenderObject::updateAbsolutePos() {
+ calcAbsolutePos(_absoluteX, _absoluteY);
+
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ (*it)->updateAbsolutePos();
+}
+
+// Get-Methoden
+// ------------
+
+bool RenderObject::getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result) {
+ result = pObject->getBbox();
+ result.clip(_bbox);
+ return result.isValidRect();
+}
+
+// Set-Methoden
+// ------------
+void RenderObject::setPos(int x, int y) {
+ _x = x;
+ _y = y;
+ updateAbsolutePos();
+}
+
+void RenderObject::setX(int x) {
+ _x = x;
+ updateAbsolutePos();
+}
+
+void RenderObject::setY(int y) {
+ _y = y;
+ updateAbsolutePos();
+}
+
+void RenderObject::setZ(int z) {
+ if (z < 0)
+ BS_LOG_ERRORLN("Tried to set a negative Z value (%d).", z);
+ else
+ _z = z;
+}
+
+void RenderObject::setVisible(bool visible) {
+ _visible = visible;
+}
+
+// -----------------------------------------------------------------------------
+// Objekterzeuger
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Animation> RenderObject::addAnimation(const Common::String &filename) {
+ RenderObjectPtr<Animation> aniPtr((new Animation(this->getHandle(), filename))->getHandle());
+ if (aniPtr.isValid() && aniPtr->getInitSuccess())
+ return aniPtr;
+ else {
+ if (aniPtr.isValid())
+ aniPtr.erase();
+ return RenderObjectPtr<Animation>();
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Animation> RenderObject::addAnimation(const AnimationTemplate &animationTemplate) {
+ Animation *aniPtr = new Animation(this->getHandle(), animationTemplate);
+ if (aniPtr && aniPtr->getInitSuccess())
+ return aniPtr->getHandle();
+ else {
+ delete aniPtr;
+ return RenderObjectPtr<Animation>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Bitmap> RenderObject::addBitmap(const Common::String &filename) {
+ RenderObjectPtr<Bitmap> bitmapPtr((new StaticBitmap(this->getHandle(), filename))->getHandle());
+ if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
+ return RenderObjectPtr<Bitmap>(bitmapPtr);
+ else {
+ if (bitmapPtr.isValid())
+ bitmapPtr.erase();
+ return RenderObjectPtr<Bitmap>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Bitmap> RenderObject::addDynamicBitmap(uint width, uint height) {
+ RenderObjectPtr<Bitmap> bitmapPtr((new DynamicBitmap(this->getHandle(), width, height))->getHandle());
+ if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
+ return bitmapPtr;
+ else {
+ if (bitmapPtr.isValid())
+ bitmapPtr.erase();
+ return RenderObjectPtr<Bitmap>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Panel> RenderObject::addPanel(int width, int height, uint color) {
+ RenderObjectPtr<Panel> panelPtr((new Panel(this->getHandle(), width, height, color))->getHandle());
+ if (panelPtr.isValid() && panelPtr->getInitSuccess())
+ return panelPtr;
+ else {
+ if (panelPtr.isValid())
+ panelPtr.erase();
+ return RenderObjectPtr<Panel>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Text> RenderObject::addText(const Common::String &font, const Common::String &text) {
+ RenderObjectPtr<Text> textPtr((new Text(this->getHandle()))->getHandle());
+ if (textPtr.isValid() && textPtr->getInitSuccess() && textPtr->SetFont(font)) {
+ textPtr->SetText(text);
+ return textPtr;
+ } else {
+ if (textPtr.isValid())
+ textPtr.erase();
+ return RenderObjectPtr<Text>();
+ }
+}
+
+// Persistenz-Methoden
+// -------------------
+
+bool RenderObject::persist(OutputPersistenceBlock &writer) {
+ // Typ und Handle werden als erstes gespeichert, damit beim Laden ein Objekt vom richtigen Typ mit dem richtigen Handle erzeugt werden kann.
+ writer.write(static_cast<uint>(_type));
+ writer.write(_handle);
+
+ // Restliche Objekteigenschaften speichern.
+ writer.write(_x);
+ writer.write(_y);
+ writer.write(_absoluteX);
+ writer.write(_absoluteY);
+ writer.write(_z);
+ writer.write(_width);
+ writer.write(_height);
+ writer.write(_visible);
+ writer.write(_childChanged);
+ writer.write(_initSuccess);
+ writer.write(_bbox.left);
+ writer.write(_bbox.top);
+ writer.write(_bbox.right);
+ writer.write(_bbox.bottom);
+ writer.write(_oldBbox.left);
+ writer.write(_oldBbox.top);
+ writer.write(_oldBbox.right);
+ writer.write(_oldBbox.bottom);
+ writer.write(_oldX);
+ writer.write(_oldY);
+ writer.write(_oldZ);
+ writer.write(_oldVisible);
+ writer.write(_parentPtr.isValid() ? _parentPtr->getHandle() : 0);
+ writer.write(_refreshForced);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderObject::unpersist(InputPersistenceBlock &reader) {
+ // Typ und Handle wurden schon von RecreatePersistedRenderObject() ausgelesen. Jetzt werden die restlichen Objekteigenschaften ausgelesen.
+ reader.read(_x);
+ reader.read(_y);
+ reader.read(_absoluteX);
+ reader.read(_absoluteY);
+ reader.read(_z);
+ reader.read(_width);
+ reader.read(_height);
+ reader.read(_visible);
+ reader.read(_childChanged);
+ reader.read(_initSuccess);
+ reader.read(_bbox.left);
+ reader.read(_bbox.top);
+ reader.read(_bbox.right);
+ reader.read(_bbox.bottom);
+ reader.read(_oldBbox.left);
+ reader.read(_oldBbox.top);
+ reader.read(_oldBbox.right);
+ reader.read(_oldBbox.bottom);
+ reader.read(_oldX);
+ reader.read(_oldY);
+ reader.read(_oldZ);
+ reader.read(_oldVisible);
+ uint parentHandle;
+ reader.read(parentHandle);
+ _parentPtr = RenderObjectPtr<RenderObject>(parentHandle);
+ reader.read(_refreshForced);
+
+ updateAbsolutePos();
+ updateObjectState();
+
+ return reader.isGood();
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderObject::persistChildren(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // Kinderanzahl speichern.
+ writer.write(_children.size());
+
+ // Rekursiv alle Kinder speichern.
+ RENDEROBJECT_LIST::iterator it = _children.begin();
+ while (it != _children.end()) {
+ result &= (*it)->persist(writer);
+ ++it;
+ }
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderObject::unpersistChildren(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Kinderanzahl einlesen.
+ uint childrenCount;
+ reader.read(childrenCount);
+ if (!reader.isGood())
+ return false;
+
+ // Alle Kinder rekursiv wieder herstellen.
+ for (uint i = 0; i < childrenCount; ++i) {
+ if (!recreatePersistedRenderObject(reader).isValid())
+ return false;
+ }
+
+ return result && reader.isGood();
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<RenderObject> RenderObject::recreatePersistedRenderObject(InputPersistenceBlock &reader) {
+ RenderObjectPtr<RenderObject> result;
+
+ // Typ und Handle auslesen.
+ uint type;
+ uint handle;
+ reader.read(type);
+ reader.read(handle);
+ if (!reader.isGood())
+ return result;
+
+ switch (type) {
+ case TYPE_PANEL:
+ result = (new Panel(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_STATICBITMAP:
+ result = (new StaticBitmap(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_DYNAMICBITMAP:
+ result = (new DynamicBitmap(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_TEXT:
+ result = (new Text(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_ANIMATION:
+ result = (new Animation(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ default:
+ BS_LOG_ERRORLN("Cannot recreate render object of unknown type %d.", type);
+ }
+
+ return result;
+}
+
+// Hilfs-Methoden
+// --------------
+bool RenderObject::greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs) {
+ // Das Objekt mit dem kleinem Z-Wert müssen zuerst gerendert werden.
+ if (lhs->_z != rhs->_z)
+ return lhs->_z < rhs->_z;
+ // Falls der Z-Wert gleich ist, wird das weiter oben gelegenen Objekte zuerst gezeichnet.
+ return lhs->_y < rhs->_y;
+}
+
+} // End of namespace Sword25