Initial commit
This commit is contained in:
485
engines/alcachofa/graphics.h
Normal file
485
engines/alcachofa/graphics.h
Normal file
@@ -0,0 +1,485 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ALCACHOFA_GRAPHICS_H
|
||||
#define ALCACHOFA_GRAPHICS_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/span.h"
|
||||
#include "math/vector2d.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
#include "alcachofa/camera.h"
|
||||
#include "alcachofa/common.h"
|
||||
|
||||
namespace Alcachofa {
|
||||
|
||||
/**
|
||||
* Because this gets confusing fast, here in tabular form
|
||||
*
|
||||
* | BlendMode | SrcColor | SrcAlpha | SrcBlend | DstBlend |
|
||||
* |:-------------:|:---------------------------------|:----------|:---------|:-------------|
|
||||
* | AdditiveAlpha | (1 - TintAlpha) * TexColor | TexAlpha | One | 1 - SrcAlpha |
|
||||
* | Additive | (1 - TintAlpha) * TexColor | TexAlpha | One | One |
|
||||
* | Multiply | (1 - TintAlpha) * TexColor | TexAlpha | DstColor | One |
|
||||
* | Alpha | TexColor | TintAlpha | SrcAlpha | 1 - SrcAlpha |
|
||||
* | Tinted | TintColor * TintAlpha * TexColor | TexAlpha | One | 1 - SrcAlpha |
|
||||
*
|
||||
*/
|
||||
enum class BlendMode {
|
||||
AdditiveAlpha, // Normal objects
|
||||
Additive, // "Effect" objects, fades
|
||||
Multiply, // Unused in Movie Adventure
|
||||
Alpha, // Unused in Movie Adventure (used for debugging)
|
||||
Tinted // Used for fonts
|
||||
};
|
||||
|
||||
class Shape;
|
||||
|
||||
class ITexture {
|
||||
public:
|
||||
ITexture(Common::Point size);
|
||||
virtual ~ITexture() {}
|
||||
|
||||
virtual void update(const Graphics::Surface &surface) = 0;
|
||||
inline void update(const Graphics::ManagedSurface &surface) { update(surface.rawSurface()); }
|
||||
|
||||
inline Common::Point size() const { return _size; }
|
||||
|
||||
private:
|
||||
Common::Point _size;
|
||||
};
|
||||
|
||||
class IRenderer {
|
||||
public:
|
||||
virtual ~IRenderer() {}
|
||||
|
||||
virtual Common::ScopedPtr<ITexture> createTexture(int32 w, int32 h, bool withMipmaps = true) = 0;
|
||||
virtual Graphics::PixelFormat getPixelFormat() const = 0;
|
||||
virtual bool requiresPoTTextures() const = 0;
|
||||
|
||||
virtual void begin() = 0;
|
||||
virtual void setTexture(ITexture *texture) = 0;
|
||||
virtual void setBlendMode(BlendMode blendMode) = 0;
|
||||
virtual void setLodBias(float lodBias) = 0;
|
||||
virtual void setOutput(Graphics::Surface &surface) = 0;
|
||||
virtual bool hasOutput() const = 0;
|
||||
virtual void quad(
|
||||
Math::Vector2d topLeft,
|
||||
Math::Vector2d size,
|
||||
Color color = kWhite,
|
||||
Math::Angle rotation = Math::Angle(),
|
||||
Math::Vector2d texMin = Math::Vector2d(0, 0),
|
||||
Math::Vector2d texMax = Math::Vector2d(1, 1)) = 0;
|
||||
virtual void end() = 0;
|
||||
|
||||
static IRenderer *createOpenGLRenderer(Common::Point resolution);
|
||||
static IRenderer *createOpenGLRendererClassic(Common::Point resolution);
|
||||
static IRenderer *createOpenGLRendererShaders(Common::Point resolution);
|
||||
static IRenderer *createTinyGLRenderer(Common::Point resolution);
|
||||
};
|
||||
|
||||
class IDebugRenderer : public virtual IRenderer {
|
||||
public:
|
||||
virtual void debugPolygon(
|
||||
Common::Span<Math::Vector2d> points,
|
||||
Color color = kDebugRed
|
||||
) = 0;
|
||||
virtual void debugPolyline(
|
||||
Common::Span<Math::Vector2d> points,
|
||||
Color color = kDebugRed
|
||||
) = 0;
|
||||
|
||||
virtual void debugShape(
|
||||
const Shape &shape,
|
||||
Color color = kDebugRed
|
||||
);
|
||||
|
||||
inline void debugPolyline(Common::Point a, Common::Point b, Color color = kDebugRed) {
|
||||
Math::Vector2d points[] = { { (float)a.x, (float)a.y }, { (float)b.x, (float)b.y } };
|
||||
debugPolygon({ points, 2 }, color);
|
||||
}
|
||||
};
|
||||
|
||||
enum class AnimationFolder {
|
||||
Animations,
|
||||
Masks,
|
||||
Backgrounds
|
||||
};
|
||||
|
||||
struct AnimationFrame {
|
||||
Common::Point
|
||||
_center, ///< the center is used for more than just drawing the animation frame
|
||||
_offset; ///< the offset is only used for drawing the animation frame
|
||||
uint32 _duration;
|
||||
};
|
||||
|
||||
/**
|
||||
* An animation contains one or more sprites which change their position and image during playback.
|
||||
*
|
||||
* Internally there is a single list of images. Every sprite ID is mapped to an index
|
||||
* (via _spriteIndexMapping) which points to:
|
||||
* 1. The fixed image base for that sprite
|
||||
* 2. The image offset for that sprite for the current frame
|
||||
* Image indices are unfortunately one-based
|
||||
*
|
||||
* As fonts are handled very differently they are split into a second class
|
||||
*/
|
||||
class AnimationBase {
|
||||
protected:
|
||||
AnimationBase(Common::String fileName, AnimationFolder folder = AnimationFolder::Animations);
|
||||
~AnimationBase();
|
||||
|
||||
void load();
|
||||
void loadMissingAnimation();
|
||||
void freeImages();
|
||||
Graphics::ManagedSurface *readImage(Common::SeekableReadStream &stream) const;
|
||||
Common::Point imageSize(int32 imageI) const;
|
||||
inline bool isLoaded() const { return _isLoaded; }
|
||||
|
||||
static void fullBlend(
|
||||
const Graphics::ManagedSurface &source,
|
||||
Graphics::ManagedSurface &destination,
|
||||
int offsetX,
|
||||
int offsetY);
|
||||
|
||||
static constexpr const uint kMaxSpriteIDs = 256;
|
||||
Common::String _fileName;
|
||||
AnimationFolder _folder;
|
||||
bool _isLoaded = false;
|
||||
uint32 _totalDuration = 0;
|
||||
|
||||
int32 _spriteIndexMapping[kMaxSpriteIDs] = { 0 };
|
||||
Common::Array<uint32>
|
||||
_spriteOffsets, ///< index offset per sprite and animation frame
|
||||
_spriteBases; ///< base index per sprite
|
||||
Common::Array<AnimationFrame> _frames;
|
||||
Common::Array<Graphics::ManagedSurface *> _images; ///< will contain nullptr for fake images
|
||||
Common::Array<Common::Point> _imageOffsets;
|
||||
};
|
||||
|
||||
/**
|
||||
* Animations prerenders its sprites into a single texture for a set frame.
|
||||
* This prerendering can be customized with a alpha to be premultiplied
|
||||
*/
|
||||
class Animation : private AnimationBase {
|
||||
public:
|
||||
Animation(Common::String fileName, AnimationFolder folder = AnimationFolder::Animations);
|
||||
|
||||
void load();
|
||||
void freeImages();
|
||||
|
||||
using AnimationBase::isLoaded;
|
||||
inline uint spriteCount() const { return _spriteBases.size(); }
|
||||
inline uint frameCount() const { return _frames.size(); }
|
||||
inline uint32 frameDuration(int32 frameI) const { return _frames[frameI]._duration; }
|
||||
inline Common::Point frameCenter(int32 frameI) const { return _frames[frameI]._center; }
|
||||
inline uint32 totalDuration() const { return _totalDuration; }
|
||||
inline uint8 &premultiplyAlpha() { return _premultiplyAlpha; }
|
||||
Common::Rect frameBounds(int32 frameI) const;
|
||||
Common::Point totalFrameOffset(int32 frameI) const;
|
||||
int32 frameAtTime(uint32 time) const;
|
||||
int32 imageIndex(int32 frameI, int32 spriteI) const;
|
||||
using AnimationBase::imageSize;
|
||||
void outputRect2D(int32 frameI, float scale, Math::Vector2d &topLeft, Math::Vector2d &size) const;
|
||||
void outputRect3D(int32 frameI, float scale, Math::Vector3d &topLeft, Math::Vector2d &size) const;
|
||||
|
||||
void overrideTexture(const Graphics::ManagedSurface &surface);
|
||||
|
||||
void draw2D(
|
||||
int32 frameI,
|
||||
Math::Vector2d topLeft,
|
||||
float scale,
|
||||
BlendMode blendMode,
|
||||
Color color);
|
||||
void draw3D(
|
||||
int32 frameI,
|
||||
Math::Vector3d topLeft,
|
||||
float scale,
|
||||
BlendMode blendMode,
|
||||
Color color);
|
||||
void drawEffect(
|
||||
int32 frameI,
|
||||
Math::Vector3d topLeft,
|
||||
Math::Vector2d tiling,
|
||||
Math::Vector2d texOffset,
|
||||
BlendMode blendMode);
|
||||
|
||||
private:
|
||||
Common::Rect spriteBounds(int32 frameI, int32 spriteI) const;
|
||||
Common::Rect maxFrameBounds() const;
|
||||
void prerenderFrame(int32 frameI);
|
||||
|
||||
int32_t _renderedFrameI = -1;
|
||||
uint8 _premultiplyAlpha = 100, ///< in percent [0-100] not [0-255]
|
||||
_renderedPremultiplyAlpha = 255;
|
||||
|
||||
Graphics::ManagedSurface _renderedSurface;
|
||||
Common::ScopedPtr<ITexture> _renderedTexture;
|
||||
};
|
||||
|
||||
class Font : private AnimationBase {
|
||||
public:
|
||||
Font(Common::String fileName);
|
||||
|
||||
void load();
|
||||
void freeImages();
|
||||
void drawCharacter(int32 imageI, Common::Point center, Color color);
|
||||
|
||||
using AnimationBase::isLoaded;
|
||||
using AnimationBase::imageSize;
|
||||
inline uint imageCount() const { return _images.size(); }
|
||||
|
||||
private:
|
||||
Common::Array<Math::Vector2d> _texMins, _texMaxs;
|
||||
Common::ScopedPtr<ITexture> _texture;
|
||||
};
|
||||
|
||||
class Graphic {
|
||||
public:
|
||||
Graphic();
|
||||
Graphic(Common::ReadStream &stream);
|
||||
Graphic(const Graphic &other); // animation reference is taken, so keep other alive
|
||||
Graphic &operator= (const Graphic &other);
|
||||
|
||||
inline Common::Point &topLeft() { return _topLeft; }
|
||||
inline int8 &order() { return _order; }
|
||||
inline int16 &scale() { return _scale; }
|
||||
inline float &depthScale() { return _depthScale; }
|
||||
inline Color &color() { return _color; }
|
||||
inline int32 &frameI() { return _frameI; }
|
||||
inline uint32 &lastTime() { return _lastTime; }
|
||||
inline bool isPaused() const { return _isPaused; }
|
||||
inline bool hasAnimation() const { return _animation != nullptr; }
|
||||
inline Animation &animation() {
|
||||
assert(_animation != nullptr && _animation->isLoaded());
|
||||
return *_animation;
|
||||
}
|
||||
inline uint8 &premultiplyAlpha() {
|
||||
assert(_animation != nullptr);
|
||||
return _animation->premultiplyAlpha();
|
||||
}
|
||||
|
||||
void loadResources();
|
||||
void freeResources();
|
||||
void update();
|
||||
void start(bool looping);
|
||||
void pause();
|
||||
void reset();
|
||||
void setAnimation(const Common::String &fileName, AnimationFolder folder);
|
||||
void setAnimation(Animation *animation); ///< no memory ownership is given, but for prerendering it has to be mutable
|
||||
void syncGame(Common::Serializer &serializer);
|
||||
|
||||
private:
|
||||
friend class AnimationDrawRequest;
|
||||
friend class SpecialEffectDrawRequest;
|
||||
Common::ScopedPtr<Animation> _ownedAnimation;
|
||||
Animation *_animation = nullptr;
|
||||
Common::Point _topLeft;
|
||||
int16 _scale = kBaseScale;
|
||||
int8 _order = 0;
|
||||
Color _color = kWhite;
|
||||
|
||||
bool _isPaused = true,
|
||||
_isLooping = true;
|
||||
uint32 _lastTime = 0; ///< either start time or played duration at pause
|
||||
int32 _frameI = -1;
|
||||
float _depthScale = 1.0f;
|
||||
};
|
||||
|
||||
class IDrawRequest {
|
||||
public:
|
||||
IDrawRequest(int8 order);
|
||||
virtual ~IDrawRequest() {}
|
||||
|
||||
inline int8 order() const { return _order; }
|
||||
virtual void draw() = 0;
|
||||
|
||||
private:
|
||||
const int8 _order;
|
||||
};
|
||||
|
||||
class AnimationDrawRequest : public IDrawRequest {
|
||||
public:
|
||||
AnimationDrawRequest(
|
||||
Graphic &graphic,
|
||||
bool is3D,
|
||||
BlendMode blendMode,
|
||||
float lodBias = 0.0f);
|
||||
AnimationDrawRequest(
|
||||
Animation *animation,
|
||||
int32 frameI,
|
||||
Math::Vector2d center,
|
||||
int8 order
|
||||
);
|
||||
|
||||
void draw() override;
|
||||
|
||||
private:
|
||||
bool _is3D;
|
||||
Animation *_animation;
|
||||
int32 _frameI;
|
||||
Math::Vector3d _topLeft;
|
||||
float _scale;
|
||||
Color _color;
|
||||
BlendMode _blendMode;
|
||||
float _lodBias;
|
||||
};
|
||||
|
||||
class SpecialEffectDrawRequest : public IDrawRequest {
|
||||
public:
|
||||
SpecialEffectDrawRequest(
|
||||
Graphic &graphic,
|
||||
Common::Point topLeft,
|
||||
Common::Point bottomRight,
|
||||
Math::Vector2d texOffset,
|
||||
BlendMode blendMode);
|
||||
|
||||
void draw() override;
|
||||
|
||||
private:
|
||||
Animation *_animation;
|
||||
int32 _frameI;
|
||||
Math::Vector3d _topLeft;
|
||||
Math::Vector2d
|
||||
_size,
|
||||
_texOffset;
|
||||
BlendMode _blendMode;
|
||||
};
|
||||
|
||||
class TextDrawRequest : public IDrawRequest {
|
||||
public:
|
||||
TextDrawRequest(
|
||||
Font &font,
|
||||
const char *text,
|
||||
Common::Point pos,
|
||||
int maxWidth,
|
||||
bool centered,
|
||||
Color color,
|
||||
int8 order);
|
||||
|
||||
inline Common::Point size() const { return { (int16)_width, (int16)_height }; }
|
||||
void draw() override;
|
||||
|
||||
private:
|
||||
static constexpr uint kMaxLines = 12;
|
||||
using TextLine = Common::Span<const byte>; ///< byte to convert 128+ characters to image indices
|
||||
|
||||
Font &_font;
|
||||
int _posY, _height, _width;
|
||||
Color _color;
|
||||
Common::Span<TextLine> _lines;
|
||||
Common::Span<int> _posX;
|
||||
TextLine _allLines[kMaxLines];
|
||||
int _allPosX[kMaxLines];
|
||||
};
|
||||
|
||||
enum class FadeType {
|
||||
ToBlack,
|
||||
ToWhite
|
||||
// Originally there was a CrossFade, but it is unused for now and thus not implemented
|
||||
};
|
||||
|
||||
enum class PermanentFadeAction {
|
||||
Nothing,
|
||||
SetFaded,
|
||||
UnsetFaded
|
||||
};
|
||||
|
||||
class FadeDrawRequest : public IDrawRequest {
|
||||
public:
|
||||
FadeDrawRequest(FadeType type, float value, int8 order);
|
||||
|
||||
void draw() override;
|
||||
|
||||
private:
|
||||
FadeType _type;
|
||||
float _value;
|
||||
};
|
||||
|
||||
Task *fade(Process &process, FadeType fadeType,
|
||||
float from, float to,
|
||||
int32 duration, EasingType easingType,
|
||||
int8 order,
|
||||
PermanentFadeAction permanentFadeAction = PermanentFadeAction::Nothing);
|
||||
|
||||
class BorderDrawRequest : public IDrawRequest {
|
||||
public:
|
||||
BorderDrawRequest(Common::Rect rect, Color color);
|
||||
|
||||
void draw() override;
|
||||
|
||||
private:
|
||||
Common::Rect _rect;
|
||||
Color _color;
|
||||
};
|
||||
|
||||
class BumpAllocator {
|
||||
public:
|
||||
BumpAllocator(size_t pageSize);
|
||||
~BumpAllocator();
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline T *allocate(Args&&... args) {
|
||||
return new(allocateRaw(sizeof(T), alignof(T))) T(Common::forward<Args>(args)...);
|
||||
}
|
||||
void *allocateRaw(size_t size, size_t align);
|
||||
void deallocateAll();
|
||||
|
||||
private:
|
||||
void allocatePage();
|
||||
|
||||
const size_t _pageSize;
|
||||
size_t _pageI = 0, _used = 0;
|
||||
Common::Array<void *> _pages;
|
||||
};
|
||||
|
||||
class DrawQueue {
|
||||
public:
|
||||
DrawQueue(IRenderer *renderer);
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline void add(Args&&... args) {
|
||||
addRequest(_allocator.allocate<T>(Common::forward<Args>(args)...));
|
||||
}
|
||||
inline BumpAllocator &allocator() { return _allocator; }
|
||||
|
||||
void clear();
|
||||
void setLodBias(int8 orderFrom, int8 orderTo, float newLodBias);
|
||||
void draw();
|
||||
|
||||
private:
|
||||
void addRequest(IDrawRequest *drawRequest);
|
||||
|
||||
static constexpr const uint kMaxDrawRequestsPerOrder = 50;
|
||||
IRenderer *const _renderer;
|
||||
BumpAllocator _allocator;
|
||||
IDrawRequest *_requestsPerOrder[kOrderCount][kMaxDrawRequestsPerOrder] = { { 0 } };
|
||||
uint8 _requestsPerOrderCount[kOrderCount] = { 0 };
|
||||
float _lodBiasPerOrder[kOrderCount] = { 0 };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ALCACHOFA_GRAPHICS_H
|
||||
Reference in New Issue
Block a user