scene transition added for fun
This commit is contained in:
parent
3a5463d251
commit
d3ef6a5160
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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++;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue