-
-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathSprite2D.hpp
More file actions
209 lines (195 loc) · 10.3 KB
/
Copy pathSprite2D.hpp
File metadata and controls
209 lines (195 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#ifndef JET_SPRITE2D_HPP
#define JET_SPRITE2D_HPP
/// @file Sprite2D.hpp
/// @brief Screen-space 2D overlay: solid rectangle or upright scaled sprite.
///
/// A Sprite2D is owned by the caller and registered with Scene::addSprite().
/// After registration it is drawn automatically at the end of every
/// Scene::render() call, after all 3D geometry and PostFX, so it always
/// composites on top of the world.
///
/// Rendering rules:
/// - `material->diffuseMap != nullptr` → blit the texture at (x, y);
/// pixels equal to `diffuseMap->alphaColor` are skipped (colour-key)
/// when `diffuseMap->hasAlpha` is true.
/// - `material->diffuseMap == nullptr` → fill a `width × height` solid
/// rectangle with `material->color`.
///
/// The effective alpha is `material->alpha * alpha / 255`. At 255 the blit
/// is a straight copy (fastest path). Any other value blends against the
/// existing framebuffer content with per-channel RGB565 /255 rounding.
///
/// `zOrder` controls draw order within the sprite pass: lower values are
/// drawn first (further back). There is no framebuffer depth test — sprites
/// always paint over everything rendered before them.
#include <cstdint>
#include "Material.hpp"
#include "Object.hpp" // for BlendMode enum
namespace Renderer {
struct Sprite2D;
/// Composite one full-resolution RGB565 output row. Sprites are painted in
/// supplied order (back to front); no sorting, allocation or platform APIs.
/// `line` contains `width` pixels at screen y. Set swapDestination for panel
/// byte order. Sprite/material/texture storage must remain stable during use.
void compositeSprites(uint16_t* line, int width, int y,
Sprite2D* const* sprites, int count,
bool swapDestination = false);
/// @brief Screen-space 2D overlay registered with a Scene.
struct Sprite2D {
enum TextureFlags : uint8_t {
FLIP_X = 1, ///< Reverse horizontal sampling without changing the size.
FLIP_Y = 2, ///< Reverse vertical sampling without changing the size.
MIRROR_X = 4, ///< Append a horizontally flipped copy, doubling the width.
MIRROR_Y = 8 ///< Append a vertically flipped copy, doubling the height.
};
int x = 0; ///< Left edge in pixels (screen space, 0 = left).
int y = 0; ///< Top edge in pixels (screen space, 0 = top).
int width = 0; ///< Width in pixels. Ignored when diffuseMap is set (texture dimensions are used).
int height = 0; ///< Height in pixels. Ignored when diffuseMap is set.
Material* material = nullptr; ///< Surface description: color + optional diffuseMap. Required.
uint8_t alpha = 255; ///< Per-sprite alpha multiplied with material->alpha.
int zOrder = 0; ///< Draw order within the 2D pass; lower = drawn first (behind).
bool enabled = true; ///< When false the sprite is skipped entirely.
/// @brief Blend equation used when writing this sprite onto the framebuffer.
///
/// BLEND_REPLACE — normal alpha-lerp composite (default).
/// BLEND_ADD — saturating additive: dst = clamp(dst + src).
/// Combined alpha zero skips the sprite; other alpha values
/// do not scale the source. Bake falloff into the texture.
/// Ideal for glows, coronas and lens-flare elements drawn as
/// grayscale soft-edged textures; produces bright halos that
/// never over-darken the scene.
BlendMode blendMode = BlendMode::BLEND_REPLACE;
/// @brief Integer upscale factor applied at blit time (1 = native size).
///
/// When > 1, the sprite is rendered at texture_size × scale pixels using
/// nearest-neighbour sampling. Solid-colour (no texture) sprites scale their
/// width/height instead. Useful for soft glow textures that would look too
/// small at 1:1 — at 2× or 3× the smooth falloff still reads well.
int scale = 1;
/// Texture transforms, ignored for solid rectangles. MIRROR_X | MIRROR_Y
/// expands a stored top-left quarter into a symmetric full image. Flips
/// apply to that expanded image, before scaling. No extra sprites or image
/// buffers are created. Mirroring repeats the edge texel at the centre.
uint8_t textureFlags = 0;
int sourceWidth() const {
const Texture* tex = material ? material->diffuseMap : nullptr;
return tex ? tex->width * ((textureFlags & MIRROR_X) ? 2 : 1) : width;
}
int sourceHeight() const {
const Texture* tex = material ? material->diffuseMap : nullptr;
return tex ? tex->height * ((textureFlags & MIRROR_Y) ? 2 : 1) : height;
}
// Blend a clipped textured row. Offsets are output pixels from the sprite
// origin; the caller clips them to sourceWidth/Height() * scale, checks
// scale > 0, and supplies the combined material/sprite alpha.
void blendTextureRow(uint16_t* dst, int count, int outputX, int outputY,
uint8_t combinedAlpha, bool swapDestination = false) const;
};
// ---------------------------------------------------------------------------
// Solid-color rectangle helpers
// ---------------------------------------------------------------------------
// Convenience functions for creating full-screen fades, letterboxes, and
// other solid-color overlay effects with alpha blending. Automatically
// optimizes for fully transparent (0% alpha → enabled=false) and fully
// opaque (100% alpha → fast blit path).
//
// OWNERSHIP: Caller owns both the returned Sprite2D and its Material.
// Both must remain alive until the sprite is removed from the scene.
//
// USAGE EXAMPLES:
//
// 1) Full-screen fade to black:
// static Material fadeMat(0x0000, 0); // Black, fully transparent initially
// static Sprite2D fadeSprite = makeFullScreenFade(480, 320, 0x0000, &fadeMat);
// scene->addSprite(&fadeSprite);
// // ... in your game loop, fade in over time:
// fadeMat.alpha = (uint8_t)(fadeProgress * 255); // fadeProgress: 0.0-1.0
//
// 2) Letterbox bars for cinematic mode:
// static Material topBarMat(0x0000, 255); // Black, opaque
// static Material botBarMat(0x0000, 255);
// static Sprite2D topBar = makeLetterboxBar(480, 40, 0x0000, true, &topBarMat);
// static Sprite2D botBar = makeSolidRect(0, 280, 480, 40, &botBarMat);
// scene->addSprite(&topBar);
// scene->addSprite(&botBar);
//
// 3) Custom colored overlay with dynamic alpha:
// static Material overlayMat(0xF800, 128); // Red at 50% alpha
// static Sprite2D overlay = makeSolidRect(100, 100, 200, 150, &overlayMat);
// scene->addSprite(&overlay);
// // Toggle visibility:
// setSolidRectAlpha(overlay, wantVisible ? 128 : 0);
//
// PERFORMANCE NOTES:
// - alpha == 0: sprite.enabled = false; rendering skipped entirely (zero cost)
// - alpha == 255: fast opaque blit; no per-pixel blending (~3x faster than blended)
// - alpha 1-254: per-pixel alpha blend; moderate cost
/// @brief Create a Sprite2D configured as a solid-color rectangle.
/// @param x Left edge in screen pixels.
/// @param y Top edge in screen pixels.
/// @param width Width in pixels.
/// @param height Height in pixels.
/// @param material Material containing the RGB565 color and alpha (0-255).
/// Caller retains ownership; must remain valid while sprite is in use.
/// @return Configured Sprite2D. Caller owns this; must remain valid while
/// registered with Scene.
inline Sprite2D makeSolidRect(int x, int y, int width, int height,
Material* material) {
Sprite2D sprite;
sprite.x = x;
sprite.y = y;
sprite.width = width;
sprite.height = height;
sprite.material = material;
sprite.alpha = 255; // Use material->alpha for transparency control
sprite.zOrder = 0;
sprite.enabled = (material && material->alpha > 0); // Auto-skip when fully transparent
sprite.blendMode = BlendMode::BLEND_REPLACE;
sprite.scale = 1;
return sprite;
}
/// @brief Update a sprite's alpha, automatically disabling it when fully transparent.
/// @param sprite Sprite to update (typically a solid rectangle).
/// @param alpha New alpha value (0=transparent, 255=opaque).
///
/// Optimizations:
/// alpha == 0 → sprite.enabled = false (skips rendering entirely)
/// alpha == 255 → fast opaque blit path (no per-pixel blending)
/// 1..254 → per-pixel alpha blend
inline void setSolidRectAlpha(Sprite2D& sprite, uint8_t alpha) {
if (sprite.material) {
sprite.material->alpha = alpha;
sprite.enabled = (alpha > 0); // Skip rendering when fully transparent
}
}
/// @brief Convenience: create a full-screen fade overlay (typically black or white).
/// @param screenWidth Framebuffer width in pixels.
/// @param screenHeight Framebuffer height in pixels.
/// @param color RGB565 color (0x0000 for black, 0xFFFF for white).
/// @param material Material to use (caller must provide and keep alive).
/// @return Configured Sprite2D for a full-screen fade. Caller owns.
inline Sprite2D makeFullScreenFade(int screenWidth, int screenHeight,
uint16_t color, Material* material) {
if (material) material->color = color;
return makeSolidRect(0, 0, screenWidth, screenHeight, material);
}
/// @brief Convenience: create a letterbox bar for cinematic mode.
/// @param screenWidth Framebuffer width in pixels.
/// @param barHeight Height of each letterbox bar in pixels.
/// @param color RGB565 color (typically 0x0000 for black bars).
/// @param topBar True for top bar, false for bottom bar.
/// @param material Material to use (caller must provide and keep alive).
/// @return Configured Sprite2D for one letterbox bar. Caller owns.
inline Sprite2D makeLetterboxBar(int screenWidth, int barHeight,
uint16_t color, bool topBar,
Material* material) {
if (material) {
material->color = color;
material->alpha = 255; // Letterboxes are always opaque
}
const int y = topBar ? 0 : -1; // -1 sentinel: caller must set to (screenHeight - barHeight)
return makeSolidRect(0, y, screenWidth, barHeight, material);
}
} // namespace Renderer
#endif // JET_SPRITE2D_HPP