From d3ef6a51607898dfdc629ba099c0b99ddde717ad Mon Sep 17 00:00:00 2001 From: wgroeneveld Date: Sat, 4 Aug 2018 10:43:27 +0200 Subject: [PATCH] scene transition added for fun --- src/CMakeLists.txt | 2 +- src/engine/background/background.cpp | 4 + src/engine/background/text_stream.cpp | 4 +- src/engine/effects/fade_out_scene.cpp | 19 + src/engine/effects/fade_out_scene.h | 25 ++ src/engine/effects/scene_effect.h | 21 + src/engine/gba/tonc_core_stub.h | 573 +++++++++++++++++++++++++ src/engine/gba_engine.cpp | 16 + src/engine/gba_engine.h | 6 + src/engine/palette_manager.cpp | 42 ++ src/engine/palette_manager.h | 8 +- src/engine/sprites/animated_sprite.cpp | 43 ++ src/engine/sprites/animated_sprite.h | 34 ++ src/engine/sprites/sprite_manager.cpp | 2 +- src/sample_start_scene.cpp | 4 +- test/CMakeLists.txt | 2 +- test/palettetest.cpp | 69 +++ test/scenetest.cpp | 16 - 18 files changed, 865 insertions(+), 25 deletions(-) create mode 100644 src/engine/effects/fade_out_scene.cpp create mode 100644 src/engine/effects/fade_out_scene.h create mode 100644 src/engine/effects/scene_effect.h create mode 100644 src/engine/gba/tonc_core_stub.h create mode 100644 src/engine/sprites/animated_sprite.cpp create mode 100644 src/engine/sprites/animated_sprite.h create mode 100644 test/palettetest.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8118059..e33e19a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,7 @@ SET(CMAKE_C_COMPILER arm-none-eabi-gcc) SET(CMAKE_CXX_COMPILER arm-none-eabi-g++) set_property(SOURCE engine/gba/sin_lut.s PROPERTY LANGUAGE C) -add_executable(${PROJECT_NAME}.elf main.cpp engine/sprites/sprite_manager.cpp engine/sprites/sprite_manager.h engine/gba/tonc_memmap.h engine/gba/tonc_core.h engine/gba/tonc_memdef.h engine/gba/tonc_types.h engine/sprites/sprite.cpp engine/sprites/sprite.h kul.h engine/palette_manager.cpp engine/palette_manager.h engine/allocator.cpp engine/allocator.h engine/gba/tonc_oam.h engine/gba/tonc_math.h engine/gba/sin_lut.s engine/Scene.cpp engine/Scene.h engine/sprites/sprite_builder.cpp engine/sprites/sprite_builder.h engine/sprites/affine_sprite.cpp engine/sprites/affine_sprite.h flying_stuff_scene.cpp flying_stuff_scene.h engine/gba_engine.cpp engine/gba_engine.h engine/background/text_stream.cpp engine/background/text_stream.h engine/background/background.cpp engine/background/background.h engine/background/text.h sample_start_scene.cpp sample_start_scene.h) +add_executable(${PROJECT_NAME}.elf main.cpp engine/sprites/sprite_manager.cpp engine/sprites/sprite_manager.h engine/gba/tonc_memmap.h engine/gba/tonc_core.h engine/gba/tonc_memdef.h engine/gba/tonc_types.h engine/sprites/sprite.cpp engine/sprites/sprite.h kul.h engine/palette_manager.cpp engine/palette_manager.h engine/allocator.cpp engine/allocator.h engine/gba/tonc_oam.h engine/gba/tonc_math.h engine/gba/sin_lut.s engine/Scene.cpp engine/Scene.h engine/sprites/sprite_builder.cpp engine/sprites/sprite_builder.h engine/sprites/affine_sprite.cpp engine/sprites/affine_sprite.h flying_stuff_scene.cpp flying_stuff_scene.h engine/gba_engine.cpp engine/gba_engine.h engine/background/text_stream.cpp engine/background/text_stream.h engine/background/background.cpp engine/background/background.h engine/background/text.h sample_start_scene.cpp sample_start_scene.h engine/sprites/animated_sprite.cpp engine/sprites/animated_sprite.h engine/effects/fade_out_scene.cpp engine/effects/fade_out_scene.h engine/gba/tonc_core_stub.h engine/effects/scene_effect.h) add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD COMMAND ${CMAKE_OBJCOPY} -v -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.gba diff --git a/src/engine/background/background.cpp b/src/engine/background/background.cpp index d81e854..642d9ba 100644 --- a/src/engine/background/background.cpp +++ b/src/engine/background/background.cpp @@ -5,7 +5,11 @@ #include #include #include +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else #include +#endif #include "background.h" #include "text_stream.h" diff --git a/src/engine/background/text_stream.cpp b/src/engine/background/text_stream.cpp index 94429b8..94305f1 100644 --- a/src/engine/background/text_stream.cpp +++ b/src/engine/background/text_stream.cpp @@ -31,8 +31,6 @@ void log_text(const char* text) { void consoleLog_func(const char* fileName, const int lineNr, const char* fnName, const char* msg) { TextStream::instance().clear(); - char lineNrBuf[10]; - itoa(lineNr, lineNrBuf, 10); TextStream::instance() << (std::string("DEBUG: ") + @@ -40,7 +38,7 @@ void consoleLog_func(const char* fileName, const int lineNr, const char* fnName, std::string(":") + std::string(fnName) + std::string("@") + - std::string(lineNrBuf) + + std::to_string(lineNr) + std::string(" -- ") + std::string(msg)) .c_str(); diff --git a/src/engine/effects/fade_out_scene.cpp b/src/engine/effects/fade_out_scene.cpp new file mode 100644 index 0000000..b07ebcd --- /dev/null +++ b/src/engine/effects/fade_out_scene.cpp @@ -0,0 +1,19 @@ +// +// Created by Wouter Groeneveld on 04/08/18. +// + +#include "fade_out_scene.h" + + +void FadeOutScene::update() { + sceneToAffect->getForegroundPalette()->increaseBrightness(speed); + + auto bgPalette = sceneToAffect->getBackgroundPalette(); + if(bgPalette) { + bgPalette->increaseBrightness(speed); + } else { + BackgroundPaletteManager defaultBg({}); + defaultBg.increaseBrightness(speed); + } + timesUpdated++; +} \ No newline at end of file diff --git a/src/engine/effects/fade_out_scene.h b/src/engine/effects/fade_out_scene.h new file mode 100644 index 0000000..bb77171 --- /dev/null +++ b/src/engine/effects/fade_out_scene.h @@ -0,0 +1,25 @@ +// +// Created by Wouter Groeneveld on 04/08/18. +// + +#ifndef GBA_SPRITE_ENGINE_FADE_OUT_SCENE_H +#define GBA_SPRITE_ENGINE_FADE_OUT_SCENE_H + + +#include +#include "scene_effect.h" + +class FadeOutScene : public SceneEffect { +private: + int timesUpdated; + int speed; + +public: + FadeOutScene(int speed) : timesUpdated(0), speed(speed) {} + void update() override; + bool isDone() override { return timesUpdated >= (32 / speed); } + +}; + + +#endif //GBA_SPRITE_ENGINE_FADE_OUT_SCENE_H diff --git a/src/engine/effects/scene_effect.h b/src/engine/effects/scene_effect.h new file mode 100644 index 0000000..25b2b01 --- /dev/null +++ b/src/engine/effects/scene_effect.h @@ -0,0 +1,21 @@ +// +// Created by Wouter Groeneveld on 04/08/18. +// + +#ifndef GBA_SPRITE_ENGINE_SCENE_EFFECT_H +#define GBA_SPRITE_ENGINE_SCENE_EFFECT_H + +#include + +class SceneEffect { +protected: + Scene* sceneToAffect; +public: + void setSceneToAffect(Scene* scene) { sceneToAffect = scene; }; + + virtual void update() = 0; + virtual bool isDone() = 0; + +}; + +#endif //GBA_SPRITE_ENGINE_SCENE_EFFECT_H diff --git a/src/engine/gba/tonc_core_stub.h b/src/engine/gba/tonc_core_stub.h new file mode 100644 index 0000000..cb35d9b --- /dev/null +++ b/src/engine/gba/tonc_core_stub.h @@ -0,0 +1,573 @@ +// +// Core functionality +// +//! \file tonc_core.h +//! \author J Vijn +//! \date 20060508 - 20080128 +// +/* === NOTES === + * Contents: bits, random, dma, timer + * 20080129,jv: added tonccpy/set routines. +*/ + + +#ifndef TONC_CORE_STUB +#define TONC_CORE_STUB + +#include "tonc_memmap.h" +#include "tonc_memdef.h" + + +// -------------------------------------------------------------------- +// BITS and BITFIELDS +// -------------------------------------------------------------------- + + +/*! \defgroup grpCoreBit Bit(field) macros + \ingroup grpCore +*/ +/*! \{ */ + +//! \name Simple bit macros +//\{ + +//! Create value with bit \a n set +#define BIT(n) ( 1<<(n) ) + +//! Shift \a a by \a n +#define BIT_SHIFT(a, n) ( (a)<<(n) ) + +//! Create a bitmask \a len bits long +#define BIT_MASK(len) ( BIT(len)-1 ) + +//! Set the \a flag bits in \a word +#define BIT_SET(y, flag) ( y |= (flag) ) + +//! Clear the \a flag bits in \a word +#define BIT_CLEAR(y, flag) ( y &= ~(flag) ) + +//! Flip the \a flag bits in \a word +#define BIT_FLIP(y, flag) ( y ^= (flag) ) + +//! Test whether all the \a flag bits in \a word are set +#define BIT_EQ(y, flag) ( ((y)&(flag)) == (flag) ) + + + +//! Create a bitmask of length \a len starting at bit \a shift. +#define BF_MASK(shift, len) ( BIT_MASK(len)<<(shift) ) + + +//! Retrieve a bitfield mask of length \a starting at bit \a shift from \a y. +#define _BF_GET(y, shift, len) ( ((y)>>(shift))&BIT_MASK(len) ) + +//! Prepare a bitmask for insertion or combining. +#define _BF_PREP(x, shift, len) ( ((x)&BIT_MASK(len))<<(shift) ) + +//! Insert a new bitfield value \a x into \a y. +#define _BF_SET(y, x, shift, len) \ + ( y= ((y) &~ BF_MASK(shift, len)) | _BF_PREP(x, shift, len) ) + +//\} + + +/*! \name some EVIL bit-field operations, >:) +* These allow you to mimic bitfields with macros. Most of the +* bitfields in the registers have foo_SHIFT and +* foo_SHIFT macros indicating the mask and shift values +* of the bitfield named foo in a variable. +* These macros let you prepare, get and set the bitfields. +*/ +//\{ + +//! Prepare a named bit-field for for insterion or combination. +#define BFN_PREP(x, name) ( ((x)<>name##_SHIFT ) + +//! Set a named bitfield in \a y to \a x. Equivalent to y.name= x. +#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) ) + +//! Compare a named bitfield to named literal \a x. +#define BFN_CMP(y, x, name) ( ((y)&name##_MASK) == (x) ) + + +//! Massage \a x for use in bitfield \a name with pre-shifted \a x +#define BFN_PREP2(x, name) ( (x) & name##_MASK ) + +//! Get the value of bitfield \a name from \a y, but don't down-shift +#define BFN_GET2(y, name) ( (y) & name##_MASK ) + +//! Set bitfield \a name from \a y to \a x with pre-shifted \a x +#define BFN_SET2(y,x,name) ( y = ((y)&~name##_MASK) | BFN_PREP2(x,name) ) + +//\} + +INLINE u32 bf_get(u32 y, uint shift, uint len); +INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len); +INLINE u32 bf_clamp(int x, uint len); + +INLINE int bit_tribool(u32 x, uint plus, uint minus); +INLINE u32 ROR(u32 x, uint ror); + + +/*! \} */ + + +// -------------------------------------------------------------------- +// DATA +// -------------------------------------------------------------------- + + +/*! \defgroup grpData Data routines + \ingroup grpCore +*/ +/*! \{ */ + +//! Get the number of elements in an array +#define countof(_array) ( sizeof(_array)/sizeof(_array[0]) ) + +//! Align \a x to the next multiple of \a width. +INLINE uint align(uint x, uint width); + +//! \name Copying and filling routines +//\{ + +//! Simplified copier for GRIT-exported data. +#define GRIT_CPY(dst, name) memcpy16(dst, name, name##Len/2) + + +// Base memcpy/set replacements. +void *tonccpy(void *dst, const void *src, uint size); + +void *__toncset(void *dst, u32 fill, uint size); +INLINE void *toncset(void *dst, u8 src, uint count); +INLINE void *toncset16(void *dst, u16 src, uint count); +INLINE void *toncset32(void *dst, u32 src, uint count); + + +// Fast memcpy/set +void memset16(void *dst, u16 hw, uint hwcount); +void memcpy16(void *dst, const void* src, uint hwcount); + +void memset32(void *dst, u32 wd, uint wcount); +void memcpy32(void *dst, const void* src, uint wcount); + + +//! Fastfill for halfwords, analogous to memset() +/*! Uses memset32() if \a hwcount>5 +* \param dst Destination address. +* \param hw Source halfword (not address). +* \param hwcount Number of halfwords to fill. +* \note \a dst must be halfword aligned. +* \note \a r0 returns as \a dst + \a hwcount*2. +*/ +void memset16(void *dst, u16 hw, uint hwcount); + +//! \brief Copy for halfwords. +/*! Uses memcpy32() if \a hwn>6 and + \a src and \a dst are aligned equally. + \param dst Destination address. + \param src Source address. + \param hwcount Number of halfwords to fill. + \note \a dst and \a src must be halfword aligned. + \note \a r0 and \a r1 return as + \a dst + \a hwcount*2 and \a src + \a hwcount*2. +*/ +void memcpy16(void *dst, const void* src, uint hwcount); + + +//! Fast-fill by words, analogous to memset() +/*! Like CpuFastSet(), only without the requirement of + 32byte chunks and no awkward store-value-in-memory-first issue. + \param dst Destination address. + \param wd Fill word (not address). + \param wdcount Number of words to fill. + \note \a dst must be word aligned. + \note \a r0 returns as \a dst + \a wdcount*4. +*/ +void memset32(void *dst, u32 wd, uint wdcount); + + +//! \brief Fast-copy by words. +/*! Like CpuFastFill(), only without the requirement of 32byte chunks + \param dst Destination address. + \param src Source address. + \param wdcount Number of words. + \note \a src and \a dst must be word aligned. + \note \a r0 and \a r1 return as + \a dst + \a wdcount*4 and \a src + \a wdcount*4. +*/ +void memcpy32(void *dst, const void* src, uint wdcount); + +//\} + + +/*! \name Repeated-value creators + These function take a hex-value and duplicate it to all fields, + like 0x88 -> 0x88888888. +*/ +//\{ +INLINE u16 dup8(u8 x); +INLINE u32 dup16(u16 x); +INLINE u32 quad8(u8 x); +INLINE u32 octup(u8 x); +//\} + +//! \name Packing routines. +//\{ +INLINE u16 bytes2hword(u8 b0, u8 b1); +INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3); +INLINE u32 hword2word(u16 h0, u16 h1); +//\} + +/*! \} */ + + +// -------------------------------------------------------------------- +// DMA +// -------------------------------------------------------------------- + + +/*! \addtogroup grpDma */ +/*! \{ */ + +//! General purpose DMA transfer macro +/*! \param _dst Destination address. + \param _src Source address. + \param count Number of transfers. + \param ch DMA channel. + \param mode DMA mode. +*/ +#define DMA_TRANSFER(_dst, _src, count, ch, mode) \ +do { \ + REG_DMA[ch].cnt= 0; \ + REG_DMA[ch].src= (const void*)(_src); \ + REG_DMA[ch].dst= (void*)(_dst); \ + REG_DMA[ch].cnt= (count) | (mode); \ +} while(0) + + +INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode); +INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode); + +INLINE void dma3_cpy(void *dst, const void *src, uint size); +INLINE void dma3_fill(void *dst, volatile u32 src, uint size); + +/*! \} */ + + +// -------------------------------------------------------------------- +// TIMER +// -------------------------------------------------------------------- + + +INLINE void profile_start(void); +INLINE uint profile_stop(void); + + +// -------------------------------------------------------------------- +// TONE GENERATOR +// -------------------------------------------------------------------- + + +typedef enum +{ + NOTE_C=0, NOTE_CIS, NOTE_D, NOTE_DIS, + NOTE_E, NOTE_F, NOTE_FIS, NOTE_G, + NOTE_GIS, NOTE_A, NOTE_BES, NOTE_B +} eSndNoteId; + +extern const uint __snd_rates[12]; + +//! Gives the period of a note for the tone-gen registers. +/*! GBA sound range: 8 octaves: [-2, 5]; 8*12= 96 notes (kinda). +* \param note ID (range: [0,11>). See eSndNoteId. +* \param oct octave (range [-2,4)>). +*/ +#define SND_RATE(note, oct) ( 2048-(__snd_rates[note]>>(4+(oct))) ) + + +// -------------------------------------------------------------------- +// MISC +// -------------------------------------------------------------------- + + +/*! \defgroup grpCoreMisc Miscellaneous routines +* \ingroup grpCore +*/ +/*! \{ */ + +#define STR(x) #x + +//! Create text string from a literal +#define XSTR(x) STR(x) + + +//! \name Inline assembly +//\{ + +//! Assembly comment +#define ASM_CMT(str) asm volatile("@# " str) + +//! No$gba breakpoint +#define ASM_BREAK() asm volatile("\tmov\t\tr11, r11") + +//! No-op; wait a bit. +#define ASM_NOP() asm volatile("\tnop") + +//\} + + +//! \name Sector checking +//\{ + +u32 octant(int x, int y); +u32 octant_rot(int x0, int y0); + +//\} + +//! \name Random numbers +//\{ + +#define QRAN_SHIFT 15 +#define QRAN_MASK ((1<>shift) & ( (1<>len; + if(y) + x= (~y)>>(32-len); + return x; +} + + +//! Gives a tribool (-1, 0, or +1) depending on the state of some bits. +/*! Looks at the \a plus and \a minus bits of \a flags, and subtracts + their status to give a +1, -1 or 0 result. Useful for direction flags. + \param flags Value with bit-flags. + \param plus Bit number for positive result. + \param minus Bit number for negative result. + \return +1 if \a plus bit is set but \a minus bit isn't
+ -1 if \a minus bit is set and \a plus bit isn't
+ 0 if neither or both are set. +*/ +INLINE int bit_tribool(u32 flags, uint plus, uint minus) +{ return ((flags>>plus)&1) - ((flags>>minus)&1); } + + +//! Rotate bits right. Yes, this does lead to a ror instruction. +INLINE u32 ROR(u32 x, uint ror) +{ return (x<<(32-ror)) | (x>>ror); } + + +// --- Data ----------------------------------------------------------- + +INLINE uint align(uint x, uint width) +{ return (x+width-1)/width*width; } + + +//! VRAM-safe memset, byte version. Size in bytes. +INLINE void *toncset(void *dst, u8 src, uint count) +{ return __toncset(dst, quad8(src), count); } + +//! VRAM-safe memset, halfword version. Size in hwords. +INLINE void *toncset16(void *dst, u16 src, uint count) +{ return __toncset(dst, src|src<<16, count*2); } + +//! VRAM-safe memset, word version. Size in words. +INLINE void *toncset32(void *dst, u32 src, uint count) +{ return __toncset(dst, src, count*4); } + + + +//! Duplicate a byte to form a halfword: 0x12 -> 0x1212. +INLINE u16 dup8(u8 x) { return x|(x<<8); } + +//! Duplicate a halfword to form a word: 0x1234 -> 0x12341234. +INLINE u32 dup16(u16 x) { return x|(x<<16); } + +//! Quadruple a byte to form a word: 0x12 -> 0x12121212. +INLINE u32 quad8(u8 x) { return x*0x01010101; } + +//! Octuple a nybble to form a word: 0x1 -> 0x11111111 +INLINE u32 octup(u8 x) { return x*0x11111111; } + + +//! Pack 2 bytes into a word. Little-endian order. +INLINE u16 bytes2hword(u8 b0, u8 b1) +{ return b0 | b1<<8; } + +//! Pack 4 bytes into a word. Little-endian order. +INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3) +{ return b0 | b1<<8 | b2<<16 | b3<<24; } + +INLINE u32 hword2word(u16 h0, u16 h1) +{ return h0 | h1<<16; } + + +// --- DMA ------------------------------------------------------------ + +/*! \addtogroup grpDma */ +/*! \{ */ + +//! Generic DMA copy routine. +/*! \param dst Destination address. +* \param src Source address. +* \param count Number of copies to perform. +* \param ch DMA channel. +* \param mode DMA transfer mode. +* \note \a count is the number of copies, not the size in bytes. +*/ +INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode) +{ + REG_DMA[ch].cnt= 0; + REG_DMA[ch].src= src; + REG_DMA[ch].dst= dst; + REG_DMA[ch].cnt= mode | count; +} + +//! Generic DMA fill routine. +/*! \param dst Destination address. +* \param src Source value. +* \param count Number of copies to perform. +* \param ch DMA channel. +* \param mode DMA transfer mode. +* \note \a count is the number of copies, not the size in bytes. +*/ +INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode) +{ + REG_DMA[ch].cnt= 0; + REG_DMA[ch].src= (const void*)&src; + REG_DMA[ch].dst= dst; + REG_DMA[ch].cnt= count | mode | DMA_SRC_FIXED; +} + +//! Specific DMA copier, using channel 3, word transfers. +/*! \param dst Destination address. +* \param src Source address. +* \param size Number of bytes to copy +* \note \a size is the number of bytes +*/ +INLINE void dma3_cpy(void *dst, const void *src, uint size) +{ dma_cpy(dst, src, size/4, 3, DMA_CPY32); } + +//! Specific DMA filler, using channel 3, word transfers. +/*! \param dst Destination address. +* \param src Source value. +* \param size Number of bytes to copy +* \note \a size is the number of bytes +*/ +INLINE void dma3_fill(void *dst, volatile u32 src, uint size) +{ dma_fill(dst, src, size/4, 3, DMA_FILL32); } + +/*! \} */ + + +// --- Random --------------------------------------------------------- + +//! Quick (and very dirty) pseudo-random number generator +/*! \return random in range [0,8000h> +*/ +INLINE int qran(void) +{ + __qran_seed= 1664525*__qran_seed+1013904223; + return (__qran_seed>>16) & QRAN_MAX; +} + + +//! Ranged random number +/*! \return random in range [\a min, \a max> +* \note (max-min) must be lower than 8000h +*/ +INLINE int qran_range(int min, int max) +{ return (qran()*(max-min)>>QRAN_SHIFT)+min; } + + +// --- Timer ---------------------------------------------------------- + +/*! \addtogroup grpTimer */ +/*! \{ */ + +//! Start a profiling run +/*! \note Routine uses timers 3 and 3; if you're already using these +* somewhere, chaos is going to ensue. +*/ +INLINE void profile_start(void) +{ + REG_TM2D= 0; REG_TM3D= 0; + REG_TM2CNT= 0; REG_TM3CNT= 0; + REG_TM3CNT= TM_ENABLE | TM_CASCADE; + REG_TM2CNT= TM_ENABLE; +} + +//! Stop a profiling run and return the time since its start. +/*! \return 32bit cycle count +*/ +INLINE uint profile_stop(void) +{ + REG_TM2CNT= 0; + return (REG_TM3D<<16)|REG_TM2D; +} + +/*! \} /addtogroup */ + + +#endif // TONC_CORE + diff --git a/src/engine/gba_engine.cpp b/src/engine/gba_engine.cpp index f677075..36172d9 100644 --- a/src/engine/gba_engine.cpp +++ b/src/engine/gba_engine.cpp @@ -10,11 +10,27 @@ void GBAEngine::update() { vsync(); + if(sceneToTransitionTo) { + currentEffectForTransition->update(); + + if(currentEffectForTransition->isDone()) { + setScene(sceneToTransitionTo); + sceneToTransitionTo = nullptr; + delete currentEffectForTransition; + } + } + spriteManager.render(); u16 keys = readKeys(); this->currentScene->tick(keys); } +void GBAEngine::transitionIntoScene(Scene *scene, SceneEffect* effect) { + sceneToTransitionTo = scene; + currentEffectForTransition = effect; + currentEffectForTransition->setSceneToAffect(this->currentScene); +} + u16 GBAEngine::readKeys() { return ~REG_KEYS & KEY_ANY; } diff --git a/src/engine/gba_engine.h b/src/engine/gba_engine.h index 84f4ad6..d797a0a 100644 --- a/src/engine/gba_engine.h +++ b/src/engine/gba_engine.h @@ -8,6 +8,7 @@ #include #include +#include #include "Scene.h" class GBAEngine { @@ -15,6 +16,9 @@ private: Scene* currentScene; SpriteManager spriteManager; + Scene* sceneToTransitionTo; + SceneEffect* currentEffectForTransition; + void vsync() { while (REG_VCOUNT >= 160); while (REG_VCOUNT < 160); @@ -27,6 +31,8 @@ public: GBAEngine(); void setScene(Scene* scene); + void transitionIntoScene(Scene* scene, SceneEffect* effect); + u16 readKeys(); void update(); void delay(int times) { diff --git a/src/engine/palette_manager.cpp b/src/engine/palette_manager.cpp index 0795466..5cc60e9 100644 --- a/src/engine/palette_manager.cpp +++ b/src/engine/palette_manager.cpp @@ -2,9 +2,18 @@ // Created by Wouter Groeneveld on 27/07/18. // +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else #include +#endif +#include #include "palette_manager.h" +int getBits(int number, int k, int p) { + return (((1 << k) - 1) & (number >> p)); +} + void PaletteManager::persist() { dma3_cpy(this->paletteAddress(), this->data, this->size); } @@ -17,9 +26,42 @@ COLOR PaletteManager::change(int bank, int index, COLOR newColor) { } COLOR PaletteManager::color(u32 red, u32 green, u32 blue) { + if(red > 31) red = 31; + if(green > 31) green = 31; + if(blue > 31) blue = 31; return red | (green<<5) | (blue<<10); } +u32 PaletteManager::red(COLOR r) { + return getBits(r, 5, 0); +} + +u32 PaletteManager::green(COLOR r) { + return getBits(r, 5, 5); +} + +u32 PaletteManager::blue(COLOR r) { + return getBits(r, 5, 10); +} + +void PaletteManager::increaseBrightness(u32 intensity) { + if(intensity > 31) { + failure(Brightness_Intensity_Too_High); + return; + } + + auto palBank = this->paletteBank(); + for(int bank = 0; bank < PALETTE_BANK_SIZE; bank++) { + for(int index = 0; index < PALETTE_BANK_SIZE; index++) { + auto current = palBank[bank][index]; + auto next = PaletteManager::color(PaletteManager::red(current) + intensity, + PaletteManager::green(current) + intensity, PaletteManager::blue(current) + intensity); + + change(bank, index, next); + } + } +} + void PaletteManager::persistToBank(int bank) { auto palBank = this->paletteBank(); dma3_cpy(palBank[bank], this->data, PALETTE_BANK_SIZE); diff --git a/src/engine/palette_manager.h b/src/engine/palette_manager.h index 5ce326f..7fbe541 100644 --- a/src/engine/palette_manager.h +++ b/src/engine/palette_manager.h @@ -11,6 +11,7 @@ #define PALETTE_BANK_SIZE 16 #define PALETTE_MAX_SIZE 256 +int getBits(int number, int k, int p); class PaletteManager { protected: @@ -25,8 +26,13 @@ public: void persist(); void persistToBank(int bank); - static COLOR color(u32 r, u32 g, u32 b); COLOR change(int bank, int index, COLOR newColor); + void increaseBrightness(u32 intensity); + + static COLOR color(u32 r, u32 g, u32 b); + static u32 red(COLOR r); + static u32 green(COLOR r); + static u32 blue(COLOR r); }; class BackgroundPaletteManager : public PaletteManager { diff --git a/src/engine/sprites/animated_sprite.cpp b/src/engine/sprites/animated_sprite.cpp new file mode 100644 index 0000000..5bd7b09 --- /dev/null +++ b/src/engine/sprites/animated_sprite.cpp @@ -0,0 +1,43 @@ +// +// Created by Wouter Groeneveld on 03/08/18. +// + +#include +#include "animated_sprite.h" + +AnimatedSprite::AnimatedSprite(const void *imageData, int imageSize, int x, int y, SpriteSize size, int frameTileSize, int delay) + : Sprite(imageData, imageSize, x, y, size), frameTileSize(frameTileSize), frame(0), counter(0), delay(delay) {} + +void AnimatedSprite::flipHorizontally(bool flip) { + if(flip) { + oam.get()->attr1 |= ATTR1_HFLIP; + } else { + oam.get()->attr1 &= FLIP_HORIZONTAL_CLEAR; + } +} + +void AnimatedSprite::flipVertically(bool flip) { + if(flip) { + oam.get()->attr1 |= ATTR1_VFLIP; + } else { + oam.get()->attr1 &= FLIP_VERTICAL_CLEAR; + } +} + +void AnimatedSprite::update() { + counter++; + if(counter > delay) { + frame += frameTileSize; + if(frame > frameTileSize) { + frame = 0; + } + + updateTileOffsetInOam(); + counter = 0; + } +} + +void AnimatedSprite::updateTileOffsetInOam() { + oam.get()->attr2 &= OAM_TILE_OFFSET_CLEAR; + oam.get()->attr2 |= (frame & OAM_TILE_OFFSET_NEW); +} \ No newline at end of file diff --git a/src/engine/sprites/animated_sprite.h b/src/engine/sprites/animated_sprite.h new file mode 100644 index 0000000..09ba1b4 --- /dev/null +++ b/src/engine/sprites/animated_sprite.h @@ -0,0 +1,34 @@ +// +// Created by Wouter Groeneveld on 03/08/18. +// + +#ifndef GBA_SPRITE_ENGINE_ANIMATED_SPRITE_H +#define GBA_SPRITE_ENGINE_ANIMATED_SPRITE_H + + +#include "sprite.h" + +#define FLIP_VERTICAL_CLEAR 0xdfff +#define FLIP_HORIZONTAL_CLEAR 0xefff +#define OAM_TILE_OFFSET_CLEAR 0xfc00 +#define OAM_TILE_OFFSET_NEW 0x03ff + +class AnimatedSprite : public Sprite { +private: + int delay; + int frame, frameTileSize; + int counter; + + void updateTileOffsetInOam(); + +public: + AnimatedSprite(const void *imageData, int imageSize, int x, int y, SpriteSize size, int frameTileSize, int delay); + + void flipVertically(bool flip); + void flipHorizontally(bool flip); + + void update(); +}; + + +#endif //GBA_SPRITE_ENGINE_ANIMATED_SPRITE_H diff --git a/src/engine/sprites/sprite_manager.cpp b/src/engine/sprites/sprite_manager.cpp index 9ff1789..45877ef 100644 --- a/src/engine/sprites/sprite_manager.cpp +++ b/src/engine/sprites/sprite_manager.cpp @@ -58,7 +58,7 @@ void SpriteManager::copyOverSpriteOAMToVRAM() { // WHY warning: can't do this: obj_aff_mem[affineIndex] = *affineShadow; // because that would override OAM also! only want to set non-overlapping affine attribs - affine->setTransformationMatrix(&obj_aff_mem[1]); + affine->setTransformationMatrix(&obj_aff_mem[affineIndex]); affine->setAffineIndex(affineIndex); affineIndex++; } diff --git a/src/sample_start_scene.cpp b/src/sample_start_scene.cpp index 37dfdab..f723ca1 100644 --- a/src/sample_start_scene.cpp +++ b/src/sample_start_scene.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "sample_start_scene.h" #include "kul.h" #include "flying_stuff_scene.h" @@ -42,7 +43,6 @@ void SampleStartScene::tick(u16 keys) { if(keys & KEY_START) { TextStream::instance() << "entered: starting next scene"; - FlyingStuffScene* nextScene = new FlyingStuffScene(); - engine->setScene(nextScene); + engine->transitionIntoScene(new FlyingStuffScene(), new FadeOutScene(2)); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bd0c25c..994330e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,5 +11,5 @@ add_definitions(-DCODE_COMPILED_AS_PART_OF_TEST) include_directories(${GTEST_LIBRARY}/include) -add_executable(unittest maintest.cpp gbatest.cpp spritetest.cpp ../src/engine/sprites/sprite.cpp ../src/engine/allocator.cpp scenetest.cpp allocatortest.cpp) +add_executable(unittest maintest.cpp gbatest.cpp spritetest.cpp ../src/engine/background/background.cpp ../src/engine/background/text_stream.cpp ../src/engine/palette_manager.cpp ../src/engine/sprites/sprite.cpp ../src/engine/allocator.cpp scenetest.cpp allocatortest.cpp palettetest.cpp) target_link_libraries(unittest ${GTEST_LIBRARY}/build/libgtest.a ${GTEST_LIBRARY}/build/libgtest_main.a) diff --git a/test/palettetest.cpp b/test/palettetest.cpp new file mode 100644 index 0000000..7f37cd6 --- /dev/null +++ b/test/palettetest.cpp @@ -0,0 +1,69 @@ +// +// Created by Wouter Groeneveld on 04/08/18. +// + +#include +#include "gtest/gtest.h" + +class SomePaletteManager : public PaletteManager { +public: + PALBANK* palette; + SomePaletteManager() : SomePaletteManager({}) {}; + SomePaletteManager(const u16 *paletteData) : PaletteManager(paletteData) { + palette = (PALBANK*) malloc(sizeof(PALBANK) * 16); + } + + virtual ~SomePaletteManager() { + delete palette; + } + +protected: + PALBANK *paletteBank() override { + return palette; + } + + void *paletteAddress() override { + return nullptr; + } +}; + +class PaletteSuite : public ::testing::Test { +protected: + SomePaletteManager* palette; + + virtual void TearDown() { + delete palette; + } + + virtual void SetUp() { + palette = new SomePaletteManager(); + } +}; + +TEST_F(PaletteSuite, Increase_Intensity_Updates_For_RGB_Values) { + palette->palette[0][0] = PaletteManager::color(10, 10, 10); + palette->palette[1][1] = PaletteManager::color(11, 11, 11); + palette->palette[2][2] = PaletteManager::color(12, 12, 12); + + palette->increaseBrightness(1); + + ASSERT_EQ(11, PaletteManager::red(palette->palette[0][0])); + ASSERT_EQ(11, PaletteManager::green(palette->palette[0][0])); + ASSERT_EQ(11, PaletteManager::blue(palette->palette[0][0])); + + ASSERT_EQ(12, PaletteManager::red(palette->palette[1][1])); + ASSERT_EQ(12, PaletteManager::green(palette->palette[1][1])); + ASSERT_EQ(12, PaletteManager::blue(palette->palette[1][1])); + + ASSERT_EQ(13, PaletteManager::red(palette->palette[2][2])); + ASSERT_EQ(13, PaletteManager::green(palette->palette[2][2])); + ASSERT_EQ(13, PaletteManager::blue(palette->palette[2][2])); +} + +TEST_F(PaletteSuite, Extract_Individual_Colors) { + auto color = PaletteManager::color(10, 20, 30); + + ASSERT_EQ(10, PaletteManager::red(color)); + ASSERT_EQ(20, PaletteManager::green(color)); + ASSERT_EQ(30, PaletteManager::blue(color)); +} diff --git a/test/scenetest.cpp b/test/scenetest.cpp index c2cd1f8..98312a8 100644 --- a/test/scenetest.cpp +++ b/test/scenetest.cpp @@ -22,9 +22,6 @@ private: std::unique_ptr someSprite1; std::unique_ptr someSprite2; public: - void moveToNextSceneTest(Scene& next) { - this->onNextSceneFn(next); - } std::vector sprites() override { return { someSprite1.get(), someSprite2.get() @@ -48,19 +45,6 @@ public: } }; -TEST_F(SceneSuite, OnGoToNextScene_Callback_Test) { - SomeScene scene; - bool called = false; - - auto goToNextScene = [&called](Scene& sceneFromArgs) { - called = true; - }; - scene.onNextScene(goToNextScene); - scene.moveToNextSceneTest(scene); - - ASSERT_TRUE(called); -} - TEST_F(SceneSuite, GetSpritesReturnsPointersOfBuiltSprites) { SomeScene scene; scene.load();