Files
commandergenius/project/jni/sdl_blitpool/planet.c

706 lines
13 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
// Blit pool test: big planet.
// press b key to switch UseBlitPool/Unuse.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "SDL.h"
#include "SDL_BlitPool.h"
#define MAX_BUFFER 256
#define MAX_PLANET (MAX_BUFFER * 2)
#define PATH_IMG_PLANET "bigplanet.bmp"
#define EPSILON 1.0e-3
struct Vector2 {
float x, y;
};
struct Planet {
struct Vector2 Position;
struct Vector2 Acceleration;
SDL_Rect ClearRect;
float Weight;
};
struct {
struct Planet PlanetArray[MAX_PLANET];
Uint32 PlanetCount;
Uint32 PlanetSize;
BlitPool *PlanetPool;
struct Video {
SDL_Surface *ScreenSurface;
SDL_Surface *PlanetSurface;
Uint32 FramePerSecond;
Uint32 FrameCounter;
struct Vector2 ViewportOffset;
} Video;
struct MouseState {
struct Vector2 Position;
struct Vector2 Acceleration;
int FlagViewportDragging;
} MouseState;
struct {
int CleanFill;
int Wall;
int UseBlitPool;
} Option;
} g;
void MainLoop(void);
int Initialize(void);
int InitializeVideo(void);
void DeInitialize(void);
int EventProcess(void);
void Step(Uint32 dt);
void Render(void);
void RenderUseBlitPool(void);
void MouseEvent(SDL_MouseButtonEvent *event);
void Clear(void);
void UpdateMouseState(void);
void UpdateTitleBar(void);
int CreatePlanet(float weight);
void DeleteAllPlanet(void);
void ApplyGravidy(Uint32 dt);
void ApplyMove(Uint32 dt);
void ApplyFrameCollide(void);
void ApplyGeneralResistance(void);
void Vec2_Plus(struct Vector2 *dest, struct Vector2 *src);
void Vec2_Minus(struct Vector2 *dest, struct Vector2 *src);
void Vec2_Multi(struct Vector2 *dest, float t);
float Vec2_Norm(struct Vector2 *vec);
void Vec2_Normalize(struct Vector2 *vec);
int main(int argc, char *argv[])
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
return 1;
}
if (Initialize()) {
goto end;
}
printf("press b key to switch UseBlitPool/Unuse.\n");
MainLoop();
end:
DeInitialize();
SDL_Quit();
return 0;
}
int Initialize(void)
{
if (InitializeVideo()) {
return 1;
}
g.PlanetCount = 0;
g.PlanetSize = g.Video.PlanetSurface->w;
g.Option.CleanFill = 1;
g.Option.Wall = 1;
g.Option.UseBlitPool = 1; /* switch by b key */
g.MouseState.FlagViewportDragging = 0;
return 0;
}
int InitializeVideo(void)
{
struct Video *p;
p = &g.Video;
p->ScreenSurface = SDL_SetVideoMode(
640, 480, 32,
SDL_SWSURFACE | SDL_RESIZABLE /* | SDL_FULLSCREEN */
);
{ /* 青を透明カラーに */
SDL_Surface *s;
s = SDL_LoadBMP(PATH_IMG_PLANET);
if (s == NULL) {
return 1;
}
if (SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(s->format, 0, 0, 0xff))) {
return 1;
}
p->PlanetSurface = SDL_DisplayFormatAlpha(s);
SDL_FreeSurface(s);
}
if (p->ScreenSurface == NULL || p->PlanetSurface == NULL) {
return 1;
}
assert((p->PlanetSurface->w == p->PlanetSurface->h) && "planet Image must be square (width == height)");
p->FramePerSecond = 0;
p->FrameCounter = 0;
p->ViewportOffset.x =
p->ViewportOffset.y = 0;
/* Make the planet surfaces pool */
g.PlanetPool = BlitPool_CreatePool(NULL);
BlitPool_PostByDescription(
g.PlanetPool,
p->PlanetSurface,
"pos=(0,0):"
"T:(29,0)-(170,200):"
"T:(0,29)-(200,170):"
"O:(29,29)-(170,170):"
);
BlitPool_Optimize(g.PlanetPool, BLIT_OPT_REMOVEOVERLAP);
SDL_FillRect(p->ScreenSurface, NULL, SDL_MapRGBA(p->ScreenSurface->format, 0, 0, 0, 255));
return 0;
}
void DeInitialize(void)
{
BlitPool_DeletePool(g.PlanetPool);
SDL_FreeSurface(g.Video.PlanetSurface);
SDL_FreeSurface(g.Video.ScreenSurface);
}
void MainLoop(void)
{
Uint32 tick, pretick, fpstick, dt;
void (*per_second_operator[])(void) = { ApplyGeneralResistance, UpdateTitleBar, NULL };
int i;
Clear();
UpdateTitleBar();
pretick = fpstick = SDL_GetTicks();
for (;;) {
tick = SDL_GetTicks();
dt = tick - pretick;
#if 0
if (dt <= 0) {
continue;
}
#endif
if (EventProcess()) {
break;
}
if ((tick - fpstick) >= 1000) {
fpstick = tick;
g.Video.FramePerSecond = g.Video.FrameCounter;
g.Video.FrameCounter = 0;
for (i = 0; per_second_operator[i] != NULL; i++) {
per_second_operator[i]();
}
}
Step(dt);
if (g.Option.UseBlitPool) {
RenderUseBlitPool();
} else {
Render();
}
g.Video.FrameCounter += 1;
pretick = tick;
}
}
void ApplyGeneralResistance(void)
{
Uint32 i;
for (i = 0; i < g.PlanetCount; i++) {
Vec2_Multi(&g.PlanetArray[i].Acceleration, 0.90); /* すべてのオブジェクトの速度を減衰 */
}
}
int EventProcess(void)
{
SDL_Event event;
SDL_Surface *surf;
Uint32 i;
surf = g.Video.ScreenSurface;
UpdateMouseState();
if (g.MouseState.FlagViewportDragging) {
g.Video.ViewportOffset.x -= g.MouseState.Acceleration.x;
g.Video.ViewportOffset.y -= g.MouseState.Acceleration.y;
}
while (SDL_PollEvent(&event)) {
switch(event.type){
case SDL_QUIT:
return 1;
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_ESCAPE: return 1; /* 終了 */
case SDLK_w: g.Option.Wall ^= 1; break; /* 壁への衝突判定フラグ */
case SDLK_b: g.Option.UseBlitPool ^= 1; break; /* Blitをプールするか */
case SDLK_SPACE: Clear(); break; /* 惑星&画面クリア */
case SDLK_a: CreatePlanet(1.0); break; /* 惑星生成 */
case SDLK_f:
SDL_FillRect(surf, NULL, SDL_MapRGBA(surf->format, 0, 0, 0, 0));
SDL_Flip(surf);
break; /* 画面クリア */
case SDLK_s: /* ストップ全速度0 */
for (i = 0; i < g.PlanetCount; i++) {
g.PlanetArray[i].Acceleration.x = 0;
g.PlanetArray[i].Acceleration.y = 0;
}
break;
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
MouseEvent(&event.button);
break;
case SDL_VIDEORESIZE:
g.Video.ScreenSurface = SDL_SetVideoMode(
event.resize.w,
event.resize.h,
surf->format->BitsPerPixel,
surf->flags
);
SDL_FreeSurface(surf);
break;
}
}
return 0;
}
void MouseEvent(SDL_MouseButtonEvent *event)
{
if (event->button == SDL_BUTTON_LEFT && event->type == SDL_MOUSEBUTTONDOWN) {
CreatePlanet(1.0);
}
}
void Clear(void)
{
DeleteAllPlanet();
SDL_FillRect(g.Video.ScreenSurface, NULL, SDL_MapRGBA(g.Video.ScreenSurface->format, 0, 0, 0, 255));
SDL_Flip(g.Video.ScreenSurface);
g.MouseState.Position.x = g.Video.ScreenSurface->w / 2;
g.MouseState.Position.y = g.Video.ScreenSurface->h / 2;
g.MouseState.Acceleration.x =
g.MouseState.Acceleration.y = 0;
g.Video.ViewportOffset.x =
g.Video.ViewportOffset.y = 0;
}
void UpdateMouseState(void)
{
int x, y;
SDL_GetMouseState(&x, &y);
g.MouseState.Position.x = x;
g.MouseState.Position.y = y;
SDL_GetRelativeMouseState(&x, &y);
g.MouseState.Acceleration.x = x;
g.MouseState.Acceleration.y = y;
}
void UpdateTitleBar(void)
{
SDL_Surface *surf;
unsigned char str[MAX_BUFFER];
static const unsigned char *title = "PlanetSDL";
surf = g.Video.ScreenSurface;
sprintf(str,
"%s | %2lu fps | [%lu x %lu x %lu bit] | %lu planet | [space] reset | [s] stop | [b]UsePool=%d",
title, g.Video.FramePerSecond, surf->clip_rect.w, surf->h, surf->format->BitsPerPixel, g.PlanetCount, g.Option.UseBlitPool
);
SDL_WM_SetCaption(str, title);
}
void Step(Uint32 dt)
{
if (dt == 0) {
return;
}
ApplyGravidy(dt); /* 惑星間重力 */
if (g.Option.Wall) {
ApplyFrameCollide(); /* 画面フレームとの衝突判定 */
}
ApplyMove(dt); /* 加速度適応 */
}
void ApplyGravidy(Uint32 dt)
{
Uint32 i, j;
float weight, distance, force, limit, delta;
struct Vector2 vec1, vec2;
register struct Planet *planet1, *planet2;
const float G = 30.0;
limit = g.PlanetSize / 3.0f;
delta = dt;
assert(limit >= 0.5 && "limit must be >= 0.5");
for (i = 0; i < g.PlanetCount; i++) {
planet1 = &g.PlanetArray[i];
for (j = i + 1; j < g.PlanetCount; j++) {
planet2 = &g.PlanetArray[j];
vec1 = planet1->Position;
Vec2_Minus(&vec1, &planet2->Position);
distance = Vec2_Norm(&vec1);
weight = planet1->Weight * planet2->Weight;
if (distance <= limit) {
distance = limit;
}
force = -G * weight / (distance * distance) * delta;
Vec2_Normalize(&vec1); /* 惑星間の単位方向ベクトル */
vec2 = vec1;
Vec2_Multi(&vec1, force / planet1->Weight);
Vec2_Plus(&planet1->Acceleration, &vec1);
Vec2_Multi(&vec2, force / -planet2->Weight);
Vec2_Plus(&planet2->Acceleration, &vec2);
}
}
}
void ApplyMove(Uint32 dt)
{
register Uint32 i;
register float delta;
struct Vector2 acc;
delta = dt / 10.0f;
for (i = 0; i < g.PlanetCount; i++) {
acc = g.PlanetArray[i].Acceleration;
Vec2_Multi(&acc, delta);
Vec2_Plus(&g.PlanetArray[i].Position, &acc);
}
}
void ApplyFrameCollide(void)
{
float frame_width, frame_height;
Uint32 i;
struct Planet *planet;
register float planet_radius;
frame_width = g.Video.ScreenSurface->w;
frame_height = g.Video.ScreenSurface->h;
planet_radius = g.PlanetSize / 2;
for (i = 0; i < g.PlanetCount; i++) {
planet = &g.PlanetArray[i];
if (planet->Position.x < planet_radius){
planet->Position.x = planet_radius;
planet->Acceleration.x *= -1;
} else if (planet->Position.x > (frame_width - planet_radius)){
planet->Position.x = frame_width - planet_radius;
planet->Acceleration.x *= -1;
}
if (planet->Position.y < planet_radius){
planet->Position.y = planet_radius;
planet->Acceleration.y *= -1;
} else if (planet->Position.y > (frame_height - planet_radius)){
planet->Position.y = frame_height - planet_radius;
planet->Acceleration.y *= -1;
}
}
}
void Render(void)
{
SDL_Surface *surf;
Uint32 i;
SDL_Rect rect;
Sint32 offset;
Uint32 numrect;
SDL_Rect rectbuf[MAX_PLANET * 2];
surf = g.Video.ScreenSurface;
rect.x = rect.y = 0;
rect.w = rect.h = 0;
offset = g.PlanetSize / 2;
numrect = 0;
for (i = 0; i < g.PlanetCount; i++) {
SDL_FillRect(surf, &g.PlanetArray[i].ClearRect, SDL_MapRGBA(surf->format, 0, 0, 0, 255));
rectbuf[numrect++] = g.PlanetArray[i].ClearRect;
}
/* 描画 */
for (i = 0; i < g.PlanetCount; i++) {
rect.x = g.PlanetArray[i].Position.x - offset - g.Video.ViewportOffset.x;
rect.y = g.PlanetArray[i].Position.y - offset - g.Video.ViewportOffset.y;
SDL_BlitSurface(g.Video.PlanetSurface, NULL, surf, &rect);
g.PlanetArray[i].ClearRect = rect;
rectbuf[numrect++] = rect;
}
SDL_UpdateRects(surf, numrect, rectbuf);
SDL_Flip(surf);
}
static unsigned char g_buf[1024 * 512];
static unsigned char *g_p;
#define InitAllocator() g_p = g_buf
static void *Allocator(unsigned long nbyte)
{
return (void *)((g_p += nbyte) - nbyte);
}
static void Releaser(void *p)
{
assert(p != NULL);
}
void RenderUseBlitPool(void)
{
Uint32 i;
SDL_Rect rect;
Sint32 offset;
BlitPool *pool;
SDL_Surface *screen, *planet;
rect.x = rect.y = 0;
offset = g.PlanetSize / 2;
screen = g.Video.ScreenSurface;
planet = g.Video.PlanetSurface;
rect.w = rect.h = g.PlanetSize;
pool = BlitPool_CreatePool(screen);
BlitPool_SetAllocator(pool, Allocator, Releaser);
InitAllocator();
for (i = 0; i < g.PlanetCount; i++) {
BlitPool_PostFill(
pool,
&g.PlanetArray[i].ClearRect,
SDL_MapRGBA(screen->format, 0, 0, 0, 255),
BLIT_ALPHA_OPAQUE
);
}
for (i = 0; i < g.PlanetCount; i++) {
rect.x = g.PlanetArray[i].Position.x - offset - g.Video.ViewportOffset.x;
rect.y = g.PlanetArray[i].Position.y - offset - g.Video.ViewportOffset.y;
BlitPool_PostPool(pool, g.PlanetPool, &rect);
g.PlanetArray[i].ClearRect = rect;
}
BlitPool_Optimize(pool, BLIT_OPT_ALL);
BlitPool_Execute(pool);
if ((BlitPool_GetArea(pool) / (float)(screen->w * screen->h)) >= 0.8) { /* update area ratio */
SDL_Flip(screen);
} else {
int numrect;
SDL_Rect rectbuf[1024];
void *p;
p = BlitPool_GetUpdateRectsObj(pool); /* work object */
while ((numrect = BlitPool_GetUpdateRects(pool, rectbuf, sizeof(rectbuf)/sizeof(rectbuf[0]), &p)) > 0) {
SDL_UpdateRects(screen, numrect, rectbuf);
}
}
BlitPool_DeletePool(pool);
}
int CreatePlanet(float weight)
{
struct Planet *new_planet;
if (g.PlanetCount >= MAX_PLANET) {
return 1;
}
new_planet = &g.PlanetArray[g.PlanetCount];
g.PlanetCount += 1;
new_planet->Position = g.MouseState.Position;
new_planet->Acceleration = g.MouseState.Acceleration;
new_planet->Weight = weight;
/* 調整 */
new_planet->Position.x += g.Video.ViewportOffset.x;
new_planet->Position.y += g.Video.ViewportOffset.y;
return 0;
}
void DeleteAllPlanet(void)
{
g.PlanetCount = 0;
}
void Vec2_Plus(struct Vector2 *dest, struct Vector2 *src)
{
dest->x += src->x;
dest->y += src->y;
}
void Vec2_Minus(struct Vector2 *dest, struct Vector2 *src)
{
dest->x -= src->x;
dest->y -= src->y;
}
void Vec2_Multi(struct Vector2 *dest, float t)
{
dest->x *= t;
dest->y *= t;
}
float Vec2_Norm(struct Vector2 *vec)
{
return sqrt(vec->x * vec->x + vec->y * vec->y);
}
void Vec2_Normalize(struct Vector2 *vec)
{
float norm;
norm = Vec2_Norm(vec);
if (norm <= EPSILON && norm >= -EPSILON) {
vec->x = 1;
vec->y = 0;
} else {
vec->x /= norm;
vec->y /= norm;
}
}