mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-07 10:21:31 +00:00
06bce68860
svn-id: r53310
561 lines
15 KiB
C++
561 lines
15 KiB
C++
/* 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<62>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<75>gen, falls nicht Wurzel (ParentPtr ung<6E>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<6B>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<65>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<65>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<65>ndert haben k<>nnte
|
||
if (_parentPtr.isValid())
|
||
_parentPtr->signalChildChange();
|
||
|
||
// Die Bounding-Box neu berechnen und Update-Regions registrieren.
|
||
updateBoxes();
|
||
|
||
// <20>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<67><72>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<65>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<6E>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
|