GRRLIB/examples/particle/source/main.cpp
2024-02-10 14:15:46 -05:00

285 lines
8.6 KiB
C++

/*===========================================
GRRLIB (GX Version)
Example code by Xane
This example shows a basic particle
engine creating a Smokebomb.
============================================*/
#include <grrlib.h>
#include <algorithm>
#include <cstdlib>
#include <wiiuse/wpad.h>
#include <cmath>
#include <ogc/lwp_watchdog.h>
#include <vector>
// Include Graphics
#include "RGFX_Background_jpg.h"
#include "RGFX_Crosshair_png.h"
#include "RGFX_Smoke_png.h"
#include "RGFX_Font_png.h"
// Effects
enum class Effect : u8 {
SMOKEBOMB
};
// Random Number (0 - 1) in float
static float RandomFloat() {
return (static_cast<float>(rand() % 12) / 12.0f) - 0.5f;
}
// Basic structure to hold particle data
struct Particle {
u8 id;
float x, y;
float sx, sy;
u16 rot;
u8 frame, framecnt, framedelay;
u8 red, green, blue;
float scale, alpha;
float sscale, salpha;
float scolor;
GRRLIB_texImg *tex;
};
// Vector used as a container to iterate through all members of GRRLIB_texImg
static std::vector<GRRLIB_texImg *> TextureList;
static std::vector<Particle *> ParticleList;
static std::vector<Particle *> ParticleListTmp;
// Declare static functions
static void ExitGame();
static void createEffect( Effect id, int _x, int _y );
static void createParticle( u8 _id, int _x, int _y, float _scale, float _alpha, u8 _red, u8 _green, u8 _blue );
static bool updateParticle( Particle *part );
static u8 CalculateFrameRate();
static constexpr u8 ClampVar8 (f32 Value);
// Prepare Graphics
GRRLIB_texImg *GFX_Background;
GRRLIB_texImg *GFX_Crosshair;
GRRLIB_texImg *GFX_Smoke;
GRRLIB_texImg *GFX_Font;
int main() {
ir_t P1Mote;
u8 FPS = 0;
// Init GRRLIB & WiiUse
GRRLIB_Init();
const u16 WinW = rmode->fbWidth;
const u16 WinH = rmode->efbHeight;
WPAD_Init();
WPAD_SetIdleTimeout( 60 * 10 );
WPAD_SetDataFormat( WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR );
// Load textures
GFX_Background = GRRLIB_LoadTextureJPG(RGFX_Background_jpg);
GFX_Crosshair = GRRLIB_LoadTexturePNG(RGFX_Crosshair_png);
GFX_Smoke = GRRLIB_LoadTexturePNG(RGFX_Smoke_png);
GFX_Font = GRRLIB_LoadTexturePNG(RGFX_Font_png);
GRRLIB_InitTileSet( GFX_Font, 8, 16, 32 );
// Set handles
GRRLIB_SetMidHandle( GFX_Crosshair, true );
GRRLIB_SetMidHandle( GFX_Smoke, true );
// Feed the vector with the textures
TextureList = { GFX_Background, GFX_Crosshair, GFX_Smoke, GFX_Font };
while (true) {
WPAD_ScanPads();
u32 WPADKeyDown = WPAD_ButtonsDown(WPAD_CHAN_0);
WPAD_SetVRes(WPAD_CHAN_0, WinW, WinH);
WPAD_IR(WPAD_CHAN_0, &P1Mote);
// Resetting Vars
GRRLIB_SetBlend( GRRLIB_BLEND_ALPHA );
// Wii Remote IR Viewport correction
const int P1MX = P1Mote.sx - 150;
const int P1MY = P1Mote.sy - 150;
// Drawing Background
GRRLIB_DrawImg( 0, 0, GFX_Background, 0, 1, 1, RGBA(255, 255, 255, 255) );
// Add any pending objects into the main container
if(ParticleListTmp.empty() == false) {
ParticleList.insert(ParticleList.end(), ParticleListTmp.begin(), ParticleListTmp.end());
ParticleListTmp.clear();
}
// Update and draw all particles
for (auto PartIter = ParticleList.begin(); PartIter != ParticleList.end();) {
if (updateParticle((*PartIter)) == true) {
GRRLIB_DrawImg( (*PartIter)->x, (*PartIter)->y, (*PartIter)->tex, (*PartIter)->rot, (*PartIter)->scale, (*PartIter)->scale, RGBA( (*PartIter)->red, (*PartIter)->green, (*PartIter)->blue, ClampVar8((*PartIter)->alpha*255) ) );
} else {
delete (*PartIter);
ParticleList.erase(PartIter);
continue;
}
PartIter++;
}
// Draw Crosshair
GRRLIB_DrawImg( P1MX, P1MY, GFX_Crosshair, 0, 1, 1, RGBA(255, 255, 255, 255) );
// Draw Text
GRRLIB_Rectangle( 28, 28, 292, 20, RGBA(0, 0, 0, 160), 1 );
GRRLIB_Printf ( 32, 32, GFX_Font, 0xFFFFFFFF, 1, "Point your Wii Remote on the screen." );
GRRLIB_Rectangle( 28, 48, 200, 16, RGBA(0, 0, 0, 160), 1 );
GRRLIB_Printf ( 32, 48, GFX_Font, 0xFFFFFFFF, 1, "Number of Particle: %d", ParticleList.size() );
GRRLIB_Rectangle( 28, 64, 64, 16, RGBA(0, 0, 0, 160), 1 );
GRRLIB_Printf ( 32, 64, GFX_Font, 0xFFFFFFFF, 1, "FPS: %d", FPS );
// Renders the Scene
GRRLIB_Render();
FPS = CalculateFrameRate();
if (WPADKeyDown & WPAD_BUTTON_B) {
createEffect( Effect::SMOKEBOMB, P1MX, P1MY );
}
if (WPADKeyDown & WPAD_BUTTON_HOME) {
break;
}
}
ExitGame();
return 0;
}
static void createEffect( Effect id, int _x, int _y ) {
switch (id) {
case Effect::SMOKEBOMB:
for (u8 i = 0; i < 5; i++) {
createParticle( 1, (_x + (RandomFloat() * 10)), (_y + (RandomFloat() * 10)), (1.4f+(RandomFloat()*0.20)), 1.0f, 64, 64, 64 );
}
for (u8 i = 0; i < 20; i++) {
createParticle( 3, (_x + (RandomFloat() * 50)), (_y + (RandomFloat() * 50)), 1.25f, 1.5f, 92, 92, 92 );
}
for (u8 i = 0; i < 5; i++) {
u8 _ColorAdd = (RandomFloat() * 75);
createParticle( 2, (_x + (RandomFloat() * 40)), (_y + (RandomFloat() * 40)), (1.0f+(RandomFloat()*0.20)), 1.0f, 128+_ColorAdd, 128+_ColorAdd, 128+_ColorAdd );
}
break;
}
}
static void createParticle( u8 _id, int _x, int _y, float _scale, float _alpha, u8 _red, u8 _green, u8 _blue ) {
Particle *part = new Particle();
part->id = _id;
part->x = _x;
part->y = _y;
part->rot = rand() % 360;
part->red = _red;
part->green = _green;
part->blue = _blue;
part->scale = _scale;
part->alpha = _alpha;
part->tex = GFX_Smoke;
part->sy = RandomFloat();
part->sx = RandomFloat();
part->sscale = 0.9985;
part->salpha = 0.985;
switch (part->id) {
case 1:
part->sy = RandomFloat() * 0.5;
part->sx = RandomFloat() * 0.5;
part->sscale = 0.999;
part->salpha = 0.992;
part->framedelay = 10;
part->framecnt = 2;
break;
case 2:
part->scolor = 0.98;
part->salpha = 0.95;
break;
case 3:
part->sy = (RandomFloat() * 8);
part->sx = (RandomFloat() * 8);
part->salpha = 0.85;
part->scolor = 0.95;
break;
}
ParticleListTmp.push_back( part );
}
static bool updateParticle( Particle *part ) {
if (part->alpha < 0.05) { part->alpha -= 0.001; }
if (part->alpha < 0.1) { part->alpha -= 0.001; }
part->x += part->sx;
part->y += part->sy;
part->scale *= part->sscale;
part->alpha *= part->salpha;
switch (part->id) {
case 1:
if (part->alpha < 0.25) { part->alpha -= 0.001; }
if (part->framecnt == 0) {
part->framecnt = 20;
part->red -= 1;
part->green -= 1;
part->blue -= 1;
}
part->framecnt -= 1;
break;
case 2:
case 3:
part->red *= part->scolor;
part->green *= part->scolor;
part->blue *= part->scolor;
break;
}
if ((part->scale < 0) || (part->alpha < 0)) {
return false;
}
return true;
}
static void ExitGame() {
// Free all memory used by textures.
for (auto &TexIter : TextureList) {
GRRLIB_FreeTexture(TexIter);
}
TextureList.clear();
// Deinitialize GRRLIB & Video
GRRLIB_Exit();
// Exit application
exit(0);
}
/**
* This function calculates the number of frames we render each second.
*/
static u8 CalculateFrameRate() {
static u8 frameCount = 0;
static u32 lastTime;
static u8 FPS = 0;
const u32 currentTime = ticks_to_millisecs(gettime());
frameCount++;
if(currentTime - lastTime > 1000) {
lastTime = currentTime;
FPS = frameCount;
frameCount = 0;
}
return FPS;
}
/**
* A helper function for the YCbCr -> RGB conversion.
* Clamps the given value into a range of 0 - 255 and thus preventing an overflow.
* @param Value The value to clamp. Using float to increase the precision. This makes a full spectrum (0 - 255) possible.
* @return Returns a clean, clamped unsigned char.
*/
static constexpr u8 ClampVar8 (f32 Value) {
Value = std::roundf(Value);
return static_cast<u8>(std::clamp(Value, 0.0f, 255.0f));
}