scene transition added for fun

This commit is contained in:
wgroeneveld 2018-08-04 10:43:27 +02:00
parent 3a5463d251
commit d3ef6a5160
18 changed files with 865 additions and 25 deletions

View File

@ -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

View File

@ -5,7 +5,11 @@
#include <engine/gba/tonc_memmap.h>
#include <stdexcept>
#include <engine/allocator.h>
#ifdef CODE_COMPILED_AS_PART_OF_TEST
#include <engine/gba/tonc_core_stub.h>
#else
#include <engine/gba/tonc_core.h>
#endif
#include "background.h"
#include "text_stream.h"

View File

@ -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();

View File

@ -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++;
}

View File

@ -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 <engine/Scene.h>
#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

View File

@ -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 <engine/Scene.h>
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

View File

@ -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 <i>foo</i>_SHIFT and
* <i>foo</i>_SHIFT macros indicating the mask and shift values
* of the bitfield named <i>foo</i> 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) & name##_MASK )
//! Get the value of a named bitfield from \a y. Equivalent to (var=) y.name
#define BFN_GET(y, name) ( ((y) & name##_MASK)>>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 <code>memset32()</code> if \a hwcount>5
* \param dst Destination address.
* \param hw Source halfword (not address).
* \param hwcount Number of halfwords to fill.
* \note \a dst <b>must</b> 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 <code>memcpy32()</code> 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 <b>must</b> 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 <b>must</b> 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 <b>must</b> 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<<QRAN_SHIFT)-1)
#define QRAN_MAX QRAN_MASK
int sqran(int seed);
INLINE int qran(void);
INLINE int qran_range(int min, int max);
//\}
/*! \} */
// --------------------------------------------------------------------
// GLOBALS
// --------------------------------------------------------------------
extern const u8 oam_sizes[3][4][2];
extern const BG_AFFINE bg_aff_default;
extern COLOR *vid_page;
extern int __qran_seed;
// --------------------------------------------------------------------
// INLINES
// --------------------------------------------------------------------
// --- Bit and bitfields -----------------------------------------------
//! Get \a len long bitfield from \a y, starting at \a shift.
/*! \param y Value containing bitfield.
\param shift Bitfield Start;
\param len Length of bitfield.
\return Bitfield between bits \a shift and \a shift + \a length.
*/
INLINE u32 bf_get(u32 y, uint shift, uint len)
{ return (y>>shift) & ( (1<<len)-1 ); }
//! Merge \a x into an \a len long bitfield from \a y, starting at \a shift.
/*! \param y Value containing bitfield.
\param x Value to merge (will be masked to fit).
\param shift Bitfield Start;
\param len Length of bitfield.
\return Result of merger: (y&~M) | (x<<s & M)
\note Does \e not write the result back into \a y (Because pure C
does't have references, that's why)
*/
INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len)
{
u32 mask= ((u32)(1<<len)-1);
return (y &~ (mask<<shift)) | (x & mask)<<shift;
}
//! Clamp \a to within the range allowed by \a len bits
INLINE u32 bf_clamp(int x, uint len)
{
u32 y=x>>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 <b>+1</b> if \a plus bit is set but \a minus bit isn't<br>
<b>-1</b> if \a minus bit is set and \a plus bit isn't<br>
<b>0</b> 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

View File

@ -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;
}

View File

@ -8,6 +8,7 @@
#include <engine/sprites/sprite_manager.h>
#include <engine/gba/tonc_memmap.h>
#include <engine/effects/scene_effect.h>
#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) {

View File

@ -2,9 +2,18 @@
// Created by Wouter Groeneveld on 27/07/18.
//
#ifdef CODE_COMPILED_AS_PART_OF_TEST
#include <engine/gba/tonc_core_stub.h>
#else
#include <engine/gba/tonc_core.h>
#endif
#include <engine/background/text_stream.h>
#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);

View File

@ -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 {

View File

@ -0,0 +1,43 @@
//
// Created by Wouter Groeneveld on 03/08/18.
//
#include <engine/gba/tonc_memdef.h>
#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);
}

View File

@ -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

View File

@ -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++;
}

View File

@ -6,6 +6,7 @@
#include <engine/background/text_stream.h>
#include <engine/gba/tonc_memdef.h>
#include <engine/gba_engine.h>
#include <engine/effects/fade_out_scene.h>
#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));
}
}

View File

@ -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)

69
test/palettetest.cpp Normal file
View File

@ -0,0 +1,69 @@
//
// Created by Wouter Groeneveld on 04/08/18.
//
#include <engine/palette_manager.h>
#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));
}

View File

@ -22,9 +22,6 @@ private:
std::unique_ptr<Sprite> someSprite1;
std::unique_ptr<Sprite> someSprite2;
public:
void moveToNextSceneTest(Scene& next) {
this->onNextSceneFn(next);
}
std::vector<Sprite *> 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();