2023-07-10 00:29:21 +00:00
# ifndef BBGE_TILE_H
# define BBGE_TILE_H
# include <vector>
# include "Vector.h"
2023-07-11 20:30:28 +00:00
# include "EngineEnums.h"
2023-07-20 20:30:56 +00:00
# include "VertexBuffer.h"
2023-08-09 00:41:04 +00:00
# include "RenderGrid.h"
# include "Texture.h" // TexCoordBox
2023-07-10 00:29:21 +00:00
// A Tile is a very stripped down RenderObject that bypasses the default
// rendering pipeline for efficiency reasons.
// Use a TileRender to draw a list of Tiles.
/* Properties of tiles:
- Lots of these exist . Need to store & render efficiently
- Usually no dynamic behavior , BUT :
* can have a TileEffect that requires updating ( sometimes )
* can react to entities nearby ( rare )
* may have a draw grid ( wobbly plants etc )
- Only modified in the editor - > Can be slow to modify or reorder
- Render order must be strictly followed to get the correct visual overlapping
- Never part of a parent / child hierarchy , all tiles are standalone
- Does not have offset , internalOffset , gravity , etc etc that RenderObject has
- Parallax scroll factor is solely influenced by layer , not individually
- RGB is never tinted , alpha may come from efx
2023-07-11 20:30:28 +00:00
Further observations :
- Aside from EFX_WAVY , all tiles with the same effect index can share a global modulator ,
ie . the alpha value is the same per - tile unless the tile is spawned when the map is in progress .
And on map reload everything is back to the same value for each tile with the same effect and params .
So we can totally exclude the editor .
2023-07-13 22:19:33 +00:00
Assumptions :
- Most tiles that exist are going to be rendered
- Only few tiles have an effect attached
2023-08-09 00:41:04 +00:00
Gotchas :
2023-07-11 20:30:28 +00:00
- Keeping a pointer to a TileData is not safe .
2023-07-13 22:19:33 +00:00
- Tile indexes are not stable . Moving a tile changes the index it can be addressed with
2023-08-09 00:41:04 +00:00
- Tile repeat causes a tile to have non - standard texcoords .
Grid effect texcoords need to be synced with repeat tc , if repeat is toggled .
Also mind non - standard texcoords in ElementTemplate ( eg . aquarian glyphs )
2023-07-10 00:29:21 +00:00
*/
2023-07-11 20:30:28 +00:00
class ElementTemplate ;
class Texture ;
2023-08-09 00:41:04 +00:00
class DynamicRenderGrid ;
2023-07-11 20:30:28 +00:00
class TileRender ;
enum EFXType
{
2023-07-13 22:19:33 +00:00
EFX_NONE ,
2023-07-11 20:30:28 +00:00
EFX_SEGS ,
EFX_ALPHA ,
EFX_WAVY
} ;
2023-08-09 00:41:04 +00:00
struct TileData ;
2023-07-13 22:19:33 +00:00
// static configuration for one effect type. POD.
2023-07-11 20:30:28 +00:00
struct TileEffectConfig
{
public :
EFXType type ;
unsigned index ;
union
{
struct
{
int x , y ;
float dgox , dgoy , dgmx , dgmy , dgtm ;
bool dgo ;
} segs ;
struct
{
float radius , min , max ;
int segsy ;
bool flip ;
} wavy ;
struct
{
float val0 , val1 , time ;
bool pingpong , ease ;
BlendType blend ;
} alpha ;
} u ;
2023-08-09 00:41:04 +00:00
bool needsOwnInstanceForTile ( const TileData & t ) const ;
2023-07-11 20:30:28 +00:00
} ;
2023-07-10 00:29:21 +00:00
enum TileFlags
{
2023-07-11 20:30:28 +00:00
TILEFLAG_NONE = 0 ,
TILEFLAG_REPEAT = 0x01 , // texture repeats and uses texscale for the repeat factor
TILEFLAG_SOLID = 0x02 , // generates OT_INVISIBLE
2024-01-31 00:11:25 +00:00
TILEFLAG_TRIM = 0x04 , // trim generated OT_* to make it less thick
2023-07-11 20:30:28 +00:00
TILEFLAG_SOLID_IN = 0x08 , // instead of OT_INVISIBLE, generate OT_INVISIBLEIN
TILEFLAG_HURT = 0x10 , // always generate OT_HURT
TILEFLAG_FH = 0x20 , // flipped horizontally
2023-07-13 22:19:33 +00:00
TILEFLAG_OWN_EFFDATA = 0x40 , // tile owns its TileEffectData, can update, must delete
2023-07-11 20:30:28 +00:00
TILEFLAG_HIDDEN = 0x80 , // don't render tile
2023-07-13 22:19:33 +00:00
TILEFLAG_SELECTED = 0x100 , // ephemeral: selected in editor
2023-07-14 03:21:16 +00:00
TILEFLAG_EDITOR_HIDDEN = 0x200 , // tile is hidden for editor reasons. temporarily set when multi-selecting and moving. doesn't count as hidden externally and is only for rendering.
2023-08-09 00:41:04 +00:00
TILEFLAG_FV = 0x400 , // flipped vertically
2023-07-10 00:29:21 +00:00
} ;
2023-07-11 20:30:28 +00:00
struct TileEffectData
{
2023-08-24 22:40:14 +00:00
friend struct TileData ;
2023-08-09 00:41:04 +00:00
TileEffectData ( const TileEffectConfig & cfg , const TileData * t ) ; // NULL is passed in during global prepare, when we don't have a tile
2023-07-11 20:30:28 +00:00
~ TileEffectData ( ) ;
void update ( float dt , const TileData * t ) ; // optional t needed for EFX_WAVY
void doInteraction ( const TileData & t , const Vector & pos , const Vector & vel , float mult , float touchWidth ) ;
2023-08-24 22:40:14 +00:00
void deleteGrid ( ) ;
2023-07-11 20:30:28 +00:00
const EFXType efxtype ;
2023-07-13 22:19:33 +00:00
const unsigned efxidx ; // index of TileEffect
2023-08-09 00:41:04 +00:00
DynamicRenderGrid * grid ; // may or may not own this. This possibly points to a tile's TileRepeatData::grid
2023-07-11 20:30:28 +00:00
InterpolatedVector alpha ;
BlendType blend ;
2023-08-09 00:41:04 +00:00
bool ownGrid ; // true if we own grid
bool shared ; // only used for assertions. set if this tile effect instance is pre-made and shared across many tiles
2023-07-11 20:30:28 +00:00
struct Wavy
{
std : : vector < float > wavy , wavySave ;
Vector touchVel ;
float angleOffset , magnitude , lerpIn ;
float min , max ;
float hitPerc , effectMult ;
bool waving , flip , touching ;
void update ( float dt ) ;
2024-02-06 03:01:13 +00:00
void stop ( ) ;
2023-07-11 20:30:28 +00:00
} ;
Wavy wavy ;
2023-07-13 22:19:33 +00:00
private :
2023-08-24 22:40:14 +00:00
TileEffectData ( const TileEffectData & ) ; // internal use only
2023-08-09 00:41:04 +00:00
DynamicRenderGrid * _ensureGrid ( size_t w , size_t h , const TileData * t ) ;
2023-07-11 20:30:28 +00:00
} ;
2023-07-14 03:21:16 +00:00
struct TileRepeatData
{
2023-07-20 20:30:56 +00:00
TileRepeatData ( ) ;
2023-08-09 00:41:04 +00:00
TileRepeatData ( const TileRepeatData & o ) ;
const TexCoordBox & getTexCoords ( ) const { return grid . getTexCoords ( ) ; }
const DynamicRenderGrid & getGrid ( ) const { return grid ; }
2023-07-20 20:30:56 +00:00
2023-07-14 03:21:16 +00:00
// written via refresh()
2023-08-09 00:41:04 +00:00
DynamicRenderGrid grid ; // need this here because a repeating tile WITH a grid-based tile effect is a special and annoying case to handle
2023-07-14 03:21:16 +00:00
// set by user
float texscaleX , texscaleY ;
float texOffX , texOffY ;
2023-08-09 00:41:04 +00:00
// pass owning tile
void refresh ( const TileData & t ) ;
private :
TexCoordBox calcTexCoords ( const TileData & t ) const ;
2023-07-14 03:21:16 +00:00
} ;
2023-07-13 22:19:33 +00:00
// POD and as compact as possible. Intended for rendering as quickly as possible.
2023-07-11 20:30:28 +00:00
// the idea is that these are linearly adjacent in memory in the order they are rendered,
// to maximize cache & prefetch efficiency
2023-07-10 00:29:21 +00:00
struct TileData
{
2023-07-14 03:21:16 +00:00
float x , y , scalex , scaley ;
2023-07-13 22:19:33 +00:00
float rotation ;
2023-07-11 20:30:28 +00:00
unsigned flags ; // TileFlags
2023-07-13 22:19:33 +00:00
unsigned tag ; // FIXME: make this int
2023-08-09 00:41:04 +00:00
const ElementTemplate * et ; // never NULL. texture, texcoords, etc is here.
TileEffectData * eff ; // mostly NULL. owned if flags & TILEFLAG_OWN_EFFDATA, otherwise shared
TileRepeatData * rep ; // NULL in most cases, set if repeating. Always owned.
2023-07-13 22:19:33 +00:00
// helpers for external access
inline void setVisible ( bool on ) { if ( on ) flags & = ~ TILEFLAG_HIDDEN ; else flags | = TILEFLAG_HIDDEN ; }
inline bool isVisible ( ) const { return ! ( flags & TILEFLAG_HIDDEN ) ; }
bool isCoordinateInside ( float cx , float cy , float minsize = 0 ) const ;
2023-07-14 03:21:16 +00:00
TileRepeatData * setRepeatOn ( float texscalex = 1 , float texscaley = 1 , float offx = 0 , float offy = 0 ) ;
void setRepeatOff ( ) ;
void refreshRepeat ( ) ;
2023-08-09 00:41:04 +00:00
bool hasStandardTexcoords ( ) const ;
const TexCoordBox & getTexcoords ( ) const ;
const RenderGrid * getGrid ( ) const ;
2023-07-10 00:29:21 +00:00
} ;
2023-07-11 20:30:28 +00:00
class TileEffectStorage
{
public :
2023-07-13 22:19:33 +00:00
TileEffectStorage ( ) ;
~ TileEffectStorage ( ) ;
void finalize ( ) ; // first fill configs[], then call this
2023-07-11 20:30:28 +00:00
void assignEffect ( TileData & t , int index ) const ;
void update ( float dt ) ;
2023-07-13 22:19:33 +00:00
void clear ( ) ; // do NOT call this while there are tiles that may reference one in prepared[]
2023-07-11 20:30:28 +00:00
std : : vector < TileEffectConfig > configs ;
2023-07-13 22:19:33 +00:00
private :
void clearPrepared ( ) ;
std : : vector < TileEffectData * > prepared ;
TileEffectStorage ( const TileEffectStorage & ) ; // no-copy
2023-07-11 20:30:28 +00:00
} ;
2023-07-10 00:29:21 +00:00
class TileStorage
{
2023-07-11 20:30:28 +00:00
friend class TileRender ;
2023-07-10 00:29:21 +00:00
public :
2023-07-13 22:19:33 +00:00
TileStorage ( ) ;
2023-07-11 20:30:28 +00:00
~ TileStorage ( ) ;
2024-05-12 15:45:01 +00:00
// Pass in old indices, updates indices where the tiles are afterwards
void moveToFront ( size_t * indices , size_t n ) ;
void moveToBack ( size_t * indices , size_t n ) ;
2023-07-13 22:19:33 +00:00
// returns starting index of new tiles. Since new tiles are always appended at the end,
// the new indices corresponding to the moved tiles are [retn .. retn+n)
size_t moveToOther ( TileStorage & other , const size_t * indices , size_t n ) ;
size_t cloneSome ( const TileEffectStorage & effstore , const size_t * indices , size_t n ) ;
2023-07-11 20:30:28 +00:00
void deleteSome ( const size_t * indices , size_t n ) ;
void setTag ( unsigned tag , const size_t * indices , size_t n ) ;
2023-07-13 22:19:33 +00:00
void setEffect ( const TileEffectStorage & effstore , int idx , const size_t * indices , size_t n ) ;
void changeFlags ( unsigned flagsToSet , unsigned flagsToUnset , const size_t * indices , size_t n ) ;
2023-07-11 20:30:28 +00:00
void update ( float dt ) ;
void doInteraction ( const Vector & pos , const Vector & vel , float mult , float touchWidth ) ;
void refreshAll ( ) ; // call this after changing properties or moving to front/back
void destroyAll ( ) ;
2023-09-08 17:23:03 +00:00
void select ( const size_t * indices , size_t n ) ;
2023-07-11 20:30:28 +00:00
void clearSelection ( ) ;
2023-07-13 22:19:33 +00:00
struct Sizes
{
size_t tiles , update , collide ;
} ;
Sizes stats ( ) const ;
size_t size ( ) const { return tiles . size ( ) ; }
std : : vector < TileData > tiles ; // must call refreshAll() after changing this
2023-07-11 20:30:28 +00:00
private :
2024-05-12 15:45:01 +00:00
enum MoveTarget
{
MV_BEGIN ,
MV_END ,
} ;
2023-07-11 20:30:28 +00:00
std : : vector < size_t > indicesToUpdate ;
std : : vector < size_t > indicesToCollide ;
void _refreshTile ( const TileData & t ) ;
2024-05-12 15:45:01 +00:00
void _moveToFront ( size_t * indices , size_t n ) ;
void _moveToBack ( size_t * indices , size_t n ) ;
void _moveToPos ( MoveTarget where , size_t * indices , size_t n ) ;
2023-07-13 22:19:33 +00:00
TileStorage ( const TileStorage & ) ; // no-copy
2023-07-10 00:29:21 +00:00
} ;
# endif // BBGE_TILE_H