initial commit

This commit is contained in:
indiana-dev 2022-10-26 18:07:04 +02:00
parent d3970e3f18
commit 496943a5bd
15 changed files with 989 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
# Build files
/build-fx
/build-cg
/*.g1a
/*.g3a
# Python bytecode
__pycache__/
# Common IDE files
*.sublime-project
*.sublime-workspace
.vscode

50
CMakeLists.txt Normal file
View File

@ -0,0 +1,50 @@
# Configure with [fxsdk build-fx] or [fxsdk build-cg], which provide the
# toolchain file and module path of the fxSDK
cmake_minimum_required(VERSION 3.15)
project(MyAddin)
include(GenerateG1A)
include(GenerateG3A)
include(Fxconv)
find_package(Gint 2.9 REQUIRED)
find_package(LibProf 2.4 REQUIRED)
set(SOURCES
src/main.c
# src/menus.c
# src/fluid64x64.c
# src/fluid128x64.c
)
# Shared assets, fx-9860G-only assets and fx-CG-50-only assets
set(ASSETS
# ...
)
set(ASSETS_fx
assets-fx/example.png
assets-fx/myfont.png
# ...
)
set(ASSETS_cg
assets-cg/example.png
# ...
)
fxconv_declare_assets(${ASSETS} ${ASSETS_fx} ${ASSETS_cg} WITH_METADATA)
add_executable(myaddin ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}})
target_compile_options(myaddin PRIVATE -Wall -Wextra -O3)
target_link_options(myaddin PRIVATE
-Wl,-Map=map -Wl,--print-memory-usage)
target_link_libraries(myaddin LibProf::LibProf)
target_link_libraries(myaddin Gint::Gint)
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
generate_g1a(TARGET myaddin OUTPUT "MyAddin.g1a"
NAME "MyAddin" ICON assets-fx/icon.png)
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
generate_g3a(TARGET myaddin OUTPUT "MyAddin.g3a"
NAME "MyAddin" ICONS assets-cg/icon-uns.png assets-cg/icon-sel.png)
endif()

BIN
assets-cg/example.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,3 @@
example.png:
type: bopti-image
name: img_example

BIN
assets-cg/icon-sel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
assets-cg/icon-uns.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
assets-fx/example.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,11 @@
example.png:
type: bopti-image
name: img_example
myfont.png:
type: font
name: myfont
charset: print
grid.size: 5x7
grid.padding: 1
proportional: true

BIN
assets-fx/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
assets-fx/myfont.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

85
src/defines.h Normal file
View File

@ -0,0 +1,85 @@
#ifndef DEFINES_H
#define DEFINES_H
// Fixed Point
typedef int fix;
#define DB 16
#define FIX(x) ((x)<<DB) // int to fixed
#define FFIX(x) ((x)*(1<<DB)) // float to fixed
#define UNFIX(x) ((x)>>DB) // fixed to int
fix fmul(fix x, fix y) {
int64_t p = (int64_t)x * (int64_t)y;
return (int32_t)(p >> DB);
}
fix ffrac(fix f) {
return f & ((1 << DB) - 1);
}
float fixtof(fix f) {
return ((float)f)/(1<<DB);
}
// 64x64 simulation
#define W 64//29
#define H 64//19
#define ID(x, y) ((x) + (y) * W)
#define fH (float)H
#define MAX_X FIX(W - 1)
#define MAX_Y FIX(H - 1)
#define rdx FIX(H/2)
#define dx FFIX(1./(fH/2.))
#define dtRdx fmul(dt, rdx)
#define halfRdx rdx/2
#define alpha fmul(-ONE, fmul(dx, dx))
#define alphaHalfRdx fmul(alpha, halfRdx)
// 128x64 simulation
#define W2 128
#define ID2(x, y) ((x) + (y) * W2)
#define MAX_X2 FIX(W2 - 1)
// Global Defines
#define ONE FIX(1)
#define ONE_HALF ONE/2
#define dt FFIX(1./14.)
#define PREC_STEP FFIX(0.001)
#define swap(a, b) { fix *tmp=a; a=b; b=tmp; }
#define div tx
#define p ty
#define K_RESET KEY_DEL
#define K_PARAMS KEY_SHIFT
#define K_COLORS KEY_ALPHA
#define K_FPS KEY_MUL
#define K_HELP KEY_OPTN
fix radius = FIX(4);
fix dyeDiffusion = FFIX(0.999);
fix velDiffusion = FFIX(1);
fix dyeIntensity = FFIX(0.5);
fix velIntensity = FFIX(0.5);
const fix maxDyeIntensity = FIX(4);
fix emptyTreshold = FFIX(0.4);
fix saturateTreshold = FFIX(0.8);
int pressureIterations = 10;
fix *vx, *vy, *dye, *tx, *ty, *tz;
int addX, addY;
int currentView = 0; // 0 : simulation only, 1 : simulation + parameters, 2 : simulation + color settings
#endif // DEFINES_H

196
src/fluid128x64.c Normal file
View File

@ -0,0 +1,196 @@
void advect128x64(fix* dye, fix* vx, fix* vy, fix* outx, fix* outy, fix* outz) {
for (int j = 1; j < H-1; j++) {
for (int i = 1; i < W2-1; i++) {
int id = ID2(i, j);
fix x = FIX(i) - fmul(dtRdx, vx[id]);
fix y = FIX(j) - fmul(dtRdx, vy[id]);
if (x < 0) x = 0;
else if (x > MAX_X2) x = MAX_X2;
if (y < 0) y = 0;
else if (y > MAX_Y) y = MAX_Y;
int x1 = UNFIX(x);
int y1 = UNFIX(y);
int x2 = x1 + 1;
int y2 = y1 + 1;
int tl = ID2(x1, y1);
int tr = ID2(x2, y1);
int bl = ID2(x1, y2);
int br = ID2(x2, y2);
fix s1 = ffrac(x);
fix s0 = ONE - s1;
fix t1 = ffrac(y);
fix t0 = ONE - t1;
outx[id] = fmul(s0, fmul(t0, vx[tl]) + fmul(t1, vx[bl])) +
fmul(s1, fmul(t0, vx[tr]) + fmul(t1, vx[br]));
outy[id] = fmul(s0, fmul(t0, vy[tl]) + fmul(t1, vy[bl])) +
fmul(s1, fmul(t0, vy[tr]) + fmul(t1, vy[br]));
outz[id] = fmul(s0, fmul(t0, dye[tl]) + fmul(t1, dye[bl])) +
fmul(s1, fmul(t0, dye[tr]) + fmul(t1, dye[br]));
}
}
}
void fluid_step128x64() {
// Advect the velocity field and the dye (27.7ms)
advect128x64(dye, vx, vy, tx, ty, tz);
// Swap the pointers
swap(vx, tx);
swap(vy, ty);
swap(dye, tz);
// Velocity boundary conditions (0.1ms)
for (int i = 0; i < W2; i++) {
vy[ID2(i, 0)] = -vy[ID2(i, 1)];
vy[ID2(i, H-1)] = -vy[ID2(i, H-2)];
vx[ID2(i, 0)] = vx[ID2(i, 1)];
vx[ID2(i, H-1)] = vx[ID2(i, H-2)];
}
for (int j = 0; j < H; j++) {
vx[ID2(0, j)] = -vx[ID2(1, j)];
vx[ID2(W2-1, j)] = -vx[ID2(W2-2, j)];
vy[ID2(0, j)] = vy[ID2(1, j)];
vy[ID2(W2-1, j)] = vy[ID2(W2-2, j)];
}
// Calculate divergence & first pressure iteration (3.6ms)
for (int j = 1; j < H-1; j++) {
for (int i = 1; i < W2-1; i++) {
div[ID2(i, j)] = fmul(alphaHalfRdx, vx[ID2(i+1,j)] - vx[ID2(i-1, j)] + vy[ID2(i, j+1)] - vy[ID2(i, j-1)]);
p[ID2(i, j)] = div[ID2(i, j)] >> 2;
}
}
// Initial Pressure boundary conditions (0ms)
for (int i = 0; i < W2; i++) {
p[ID2(i, 0)] = p[ID2(i, 1)];
p[ID2(i, H-1)] = p[ID2(i, H-2)];
}
for (int j = 0; j < H; j++) {
p[ID2(0, j)] = p[ID2(1, j)];
p[ID2(W2-1, j)] = p[ID2(W2-2, j)];
}
// Poisson pressure solver (20.8ms)
for (int k = 0; k < pressureIterations-1; k++) {
// Jacobi iteration (20.4ms)
for (int j = 1; j < H-1; j++) {
for (int i = 1; i < W2-1; i++) {
int id = ID2(i, j);
p[id] = (div[id] + p[ID2(i-1, j)] + p[ID2(i+1, j)] + p[ID2(i, j-1)] + p[ID2(i, j+1)]) >> 2;
}
}
// Pressure boundary conditions (0.4ms)
for (int i = 0; i < W2; i++) {
p[ID2(i, 0)] = p[ID2(i, 1)];
p[ID2(i, H-1)] = p[ID2(i, H-2)];
}
for (int j = 0; j < H; j++) {
p[ID2(0, j)] = p[ID2(1, j)];
p[ID2(W2-1, j)] = p[ID2(W2-2, j)];
}
}
// Apply Forces
if (currentView == 0) {
int lastX = addX;
int lastY = addY;
if (keydown(KEY_LEFT)) { addX -= 1; if (addX < 1) addX = W2-2; }
else if (keydown(KEY_RIGHT)) { addX += 1; if (addX > W2-2) addX = 1; }
if (keydown(KEY_UP)) { addY -= 1; if (addY < 1) addY = H-2; }
else if (keydown(KEY_DOWN)) { addY += 1; if (addY > H-2) addY = 1; }
fix adx = FIX(addX - lastX);
fix ady = FIX(addY - lastY);
if (adx || ady) {
fix ddx = abs(adx);
fix ddy = abs(ady);
fix vdx = adx / 10;
fix vdy = ady / 10;
fix ddxy = ddx + ddy;
for (int j = addY-3; j < addY+3; j++) {
for (int i = addX-3; i < addX+3; i++) {
if (i<=0||j<=0||i>=W2-1||j>=H-1) continue;
int id = ID2(i, j);
fix dist = radius - FIX((addX-i)*(addX-i)+(addY-j)*(addY-j));
if (dist < 0) continue;
fix dyeD = fmul(dist, dyeIntensity);
fix velD = fmul(dist, velIntensity);
dye[id] = dye[id] + fmul(ddxy, dyeD);
if (dye[id] > maxDyeIntensity) dye[id] = maxDyeIntensity;
vx[id] = vx[id] + fmul(vdx, velD);
vy[id] = vy[id] + fmul(vdy, velD);
}
}
}
}
// Subtract pressure from velocity & Diffuse dye and velocity (3.4ms)
for (int j = 1; j < H-1; j++) {
for (int i = 1; i < W2-1; i++) {
int id = ID2(i, j);
vx[id] = fmul(vx[id] - fmul(halfRdx, p[ID2(i+1, j)] - p[ID2(i-1, j)]), velDiffusion);
vy[id] = fmul(vy[id] - fmul(halfRdx, p[ID2(i, j+1)] - p[ID2(i, j-1)]), velDiffusion);
}
}
// Draw dye to vram (3.1ms)
uint32_t *vram = gint_vram+4;
for (int j = 1; j < H-1; j++) {
uint32_t data = 0;
data = (data << 1);
for(int k = 1; k < 32; k++) {
int x = 32*0+k;
int id = ID2(x, j);
dye[id] = fmul(dye[id], dyeDiffusion);
data = (data << 1) | (dye[id] > saturateTreshold || (dye[id] > emptyTreshold && (x+j)%2));
}
vram[0] = data;
data = 0;
data = (data << 1);
for(int k = 0; k < 32; k++) {
int x = 32*1+k;
int id = ID2(x, j);
dye[id] = fmul(dye[id], dyeDiffusion);
data = (data << 1) | (dye[id] > saturateTreshold || (dye[id] > emptyTreshold && (x+j)%2));
}
vram[1] = data;
data = 0;
data = (data << 1);
for(int k = 0; k < 32; k++) {
int x = 32*2+k;
int id = ID2(x, j);
dye[id] = fmul(dye[id], dyeDiffusion);
data = (data << 1) | (dye[id] > saturateTreshold || (dye[id] > emptyTreshold && (x+j)%2));
}
vram[2] = data;
data = 0;
for(int k = 0; k < 31; k++) {
int x = 32*3+k;
int id = ID2(x, j);
dye[id] = fmul(dye[id], dyeDiffusion);
data = (data << 1) | (dye[id] > saturateTreshold || (dye[id] > emptyTreshold && (x+j)%2));
}
data = (data << 1);
vram[3] = data;
vram+=4;
}
}

176
src/fluid64x64.c Normal file
View File

@ -0,0 +1,176 @@
void advect64x64(fix* dye, fix* vx, fix* vy, fix* outx, fix* outy, fix* outz) {
for (int j = 1; j < H-1; j++) {
for (int i = 1; i < W-1; i++) {
int id = ID(i, j);
fix x = FIX(i) - fmul(dtRdx, vx[id]);
fix y = FIX(j) - fmul(dtRdx, vy[id]);
if (x < 0) x = 0;
else if (x > MAX_X) x = MAX_X;
if (y < 0) y = 0;
else if (y > MAX_Y) y = MAX_Y;
int x1 = UNFIX(x);
int y1 = UNFIX(y);
int x2 = x1 + 1;
int y2 = y1 + 1;
int tl = ID(x1, y1);
int tr = ID(x2, y1);
int bl = ID(x1, y2);
int br = ID(x2, y2);
fix s1 = ffrac(x);
fix s0 = ONE - s1;
fix t1 = ffrac(y);
fix t0 = ONE - t1;
outx[id] = fmul(s0, fmul(t0, vx[tl]) + fmul(t1, vx[bl])) +
fmul(s1, fmul(t0, vx[tr]) + fmul(t1, vx[br]));
outy[id] = fmul(s0, fmul(t0, vy[tl]) + fmul(t1, vy[bl])) +
fmul(s1, fmul(t0, vy[tr]) + fmul(t1, vy[br]));
outz[id] = fmul(s0, fmul(t0, dye[tl]) + fmul(t1, dye[bl])) +
fmul(s1, fmul(t0, dye[tr]) + fmul(t1, dye[br]));
}
}
}
void fluid_step64x64() {
// Advect the velocity field and the dye (27.7ms)
advect64x64(dye, vx, vy, tx, ty, tz);
// Swap the pointers
swap(vx, tx);
swap(vy, ty);
swap(dye, tz);
// Velocity boundary conditions (0.1ms)
for (int i = 0; i < W; i++) {
vy[ID(i, 0)] = -vy[ID(i, 1)];
vy[ID(i, H-1)] = -vy[ID(i, H-2)];
vx[ID(i, 0)] = vx[ID(i, 1)];
vx[ID(i, H-1)] = vx[ID(i, H-2)];
}
for (int j = 0; j < H; j++) {
vx[ID(0, j)] = -vx[ID(1, j)];
vx[ID(W-1, j)] = -vx[ID(W-2, j)];
vy[ID(0, j)] = vy[ID(1, j)];
vy[ID(W-1, j)] = vy[ID(W-2, j)];
}
// Calculate divergence & first pressure iteration (3.6ms)
for (int j = 1; j < H-1; j++) {
for (int i = 1; i < W-1; i++) {
div[ID(i, j)] = fmul(alphaHalfRdx, vx[ID(i+1,j)] - vx[ID(i-1, j)] + vy[ID(i, j+1)] - vy[ID(i, j-1)]);
p[ID(i, j)] = div[ID(i, j)] >> 2;
}
}
// Initial Pressure boundary conditions (0ms)
for (int i = 0; i < W; i++) {
p[ID(i, 0)] = p[ID(i, 1)];
p[ID(i, H-1)] = p[ID(i, H-2)];
}
for (int j = 0; j < H; j++) {
p[ID(0, j)] = p[ID(1, j)];
p[ID(W-1, j)] = p[ID(W-2, j)];
}
// Poisson pressure solver (20.8ms)
for (int k = 0; k < pressureIterations-1; k++) {
// Jacobi iteration (20.4ms)
for (int j = 1; j < H-1; j++) {
for (int i = 1; i < W-1; i++) {
int id = ID(i, j);
p[id] = (div[id] + p[ID(i-1, j)] + p[ID(i+1, j)] + p[ID(i, j-1)] + p[ID(i, j+1)]) >> 2;
}
}
// Pressure boundary conditions (0.4ms)
for (int i = 0; i < W; i++) {
p[ID(i, 0)] = p[ID(i, 1)];
p[ID(i, H-1)] = p[ID(i, H-2)];
}
for (int j = 0; j < H; j++) {
p[ID(0, j)] = p[ID(1, j)];
p[ID(W-1, j)] = p[ID(W-2, j)];
}
}
// Apply Forces
if (currentView == 0) {
int lastX = addX;
int lastY = addY;
if (keydown(KEY_LEFT)) { addX -= 1; if (addX < 1) addX = W-2; }
else if (keydown(KEY_RIGHT)) { addX += 1; if (addX > W-2) addX = 1; }
if (keydown(KEY_UP)) { addY -= 1; if (addY < 1) addY = H-2; }
else if (keydown(KEY_DOWN)) { addY += 1; if (addY > H-2) addY = 1; }
fix adx = FIX(addX - lastX);
fix ady = FIX(addY - lastY);
if (adx || ady) {
fix ddx = abs(adx);
fix ddy = abs(ady);
fix vdx = adx / 10;
fix vdy = ady / 10;
fix ddxy = ddx + ddy;
for (int j = addY-3; j < addY+3; j++) {
for (int i = addX-3; i < addX+3; i++) {
if (i<=0||j<=0||i>=W-1||j>=H-1) continue;
int id = ID(i, j);
fix dist = radius - FIX((addX-i)*(addX-i)+(addY-j)*(addY-j));
if (dist < 0) continue;
fix dyeD = fmul(dist, dyeIntensity);
fix velD = fmul(dist, velIntensity);
dye[id] = dye[id] + fmul(ddxy, dyeD);
if (dye[id] > maxDyeIntensity) dye[id] = maxDyeIntensity;
vx[id] = vx[id] + fmul(vdx, velD);
vy[id] = vy[id] + fmul(vdy, velD);
}
}
}
}
// Subtract pressure from velocity & Diffuse dye and velocity (3.4ms)
for (int j = 1; j < H-1; j++) {
for (int i = 1; i < W-1; i++) {
int id = ID(i, j);
vx[id] = fmul(vx[id] - fmul(halfRdx, p[ID(i+1, j)] - p[ID(i-1, j)]), velDiffusion);
vy[id] = fmul(vy[id] - fmul(halfRdx, p[ID(i, j+1)] - p[ID(i, j-1)]), velDiffusion);
}
}
// Draw dye to vram (3.1ms)
uint32_t *vram = gint_vram+4;
for (int j = 1; j < H-1; j++) {
uint32_t data = 0;
data = (data << 1);
for(int k = 1; k < 32; k++) {
int x = 32*0+k;
int id = ID(x, j);
dye[id] = fmul(dye[id], dyeDiffusion);
data = (data << 1) | (dye[id] > saturateTreshold || (dye[id] > emptyTreshold && (x+j)%2));
}
vram[0] = data;
data = 0;
for(int k = 0; k < 31; k++) {
int x = 32*1+k;
int id = ID(x, j);
dye[id] = fmul(dye[id], dyeDiffusion);
data = (data << 1) | (dye[id] > saturateTreshold || (dye[id] > emptyTreshold && (x+j)%2));
}
data = (data << 1);
vram[1] = data;
vram+=4;
}
}

134
src/main.c Normal file
View File

@ -0,0 +1,134 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gint/display.h>
#include <gint/hardware.h>
#include <gint/keyboard.h>
#include <gint/clock.h>
#include <fxlibc/printf.h>
#include <libprof.h>
#include "defines.h"
#include "menus.c"
#include "fluid64x64.c"
#include "fluid128x64.c"
int main(void) {
if(gint[HWCALC] == HWCALC_G35PE2) {
dtext_opt(64, 10, C_BLACK, C_WHITE, DTEXT_CENTER, DTEXT_TOP, "Cette calculatrice", -1);
dtext_opt(64, 25, C_BLACK, C_WHITE, DTEXT_CENTER, DTEXT_TOP, "est incompatible.", -1);
dtext_opt(64, 45, C_BLACK, C_WHITE, DTEXT_CENTER, DTEXT_TOP, "[EXE] Quitter", -1);
dupdate();
getkey();
return 1;
}
fix (*buffers)[W*H] = (void *)0x88040000; // magic pointer to extra memory
fix (*buffersFS)[W2*H] = (void *)0x88040000;
dye = buffers[0]; // dye concentration
vx = buffers[1]; // fluid x velocity
vy = buffers[2]; // fluid y velocity
tx = buffers[3]; // temp buffer (used to advect vx & to compute div)
ty = buffers[4]; // temp buffer (used to advect vy & to compute p)
tz = buffers[5]; // temp buffer (used to advect dye)
// Empty all buffers
memset(buffers, 0, W*H*sizeof(fix)*6);
// Init timer
prof_init();
prof_t prof = prof_make();
uint32_t lastTime = 0, time = 0;
// Init font
extern font_t myfont;
dfont(&myfont);
__printf_enable_fp();
main_menu();
// settings_menu(true);
/// Main Loop ///
bool fullscreen = false;
bool showFPS = true;
addX = W/2;
addY = H/2;
while (1) {
prof_enter(prof); // resume timer
clearevents(); // Read all events
if (fullscreen) { // fullscreen 128x64 simulation
fluid_step128x64();
drect_border(0, 0, 127, H-1, C_NONE, 1, C_BLACK);
// Handle fullscreen exit
if (keydown(KEY_MINUS) || keydown(K_PARAMS) || keydown(K_COLORS)) {
fullscreen = false;
memset(buffers, 0, W*H*sizeof(fix)*6);
dye = buffers[0];
vx = buffers[1];
vy = buffers[2];
tx = buffers[3];
ty = buffers[4];
tz = buffers[5];
dclear(C_WHITE);
if (keydown(K_PARAMS)) settings_menu(true);
}
} else { // halfscreen 64x64 simulation
fluid_step64x64();
settings_menu(false);
// Handle fullscreen enter
if (keydown(KEY_PLUS)) {
fullscreen = true;
currentView = 0;
memset(buffers, 0, W2*H*sizeof(fix)*6);
dye = buffersFS[0];
vx = buffersFS[1];
vy = buffersFS[2];
tx = buffersFS[3];
ty = buffersFS[4];
tz = buffersFS[5];
dclear(C_WHITE);
}
}
// Simulation reset
if (keydown(K_RESET)) {
memset(buffers, 0, (fullscreen ? W2 : W)*H*sizeof(fix)*6);
} // Toggle FPS
else if (keydown(K_FPS)) {
showFPS = !showFPS;
} // Toggle help screen
else if (keydown(K_HELP) || keydown(KEY_EXIT)) {
int res = help_menu();
currentView = 0;
if (res == -1) return 1;
}
color_menu();
// Show FPS
if (showFPS) {
int ms = (time - lastTime)/1000;
sprintf(strFPS, "%dms / %dfps", (time - lastTime)/100, 1000/ms);
dtext_opt(66, 1, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, strFPS, -1);
dline(64, 8, 127, 8, C_BLACK);
}
// Update vram
dupdate();
// pause timer
prof_leave(prof);
lastTime = time;
time = prof_time(prof);
}
return 1;
}

321
src/menus.c Normal file
View File

@ -0,0 +1,321 @@
char strFPS[20];
char strDyeDiff[20];
char strVelDiff[20];
char strDyeForce[20];
char strVelForce[20];
char strRadius[20];
char strPressureIter[20];
char strEmptyTreshold[20];
char strSaturateTreshold[20];
// Main Screen
void main_menu() {
const int gap = 8;
const int startY = 11;
dhline(5, C_BLACK);
dtext_opt(64, 2, C_BLACK, C_WHITE, DTEXT_CENTER, DTEXT_TOP, " CASIO FLUID SIMULATION ", -1);
dtext_opt(3, startY, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "Implementation of Jos Stam's", -1);
dtext_opt(3, startY + gap, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "\"Real Time Fluid Dynamics for", -1);
dtext_opt(3, startY + gap*2, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "games\" paper on monochrome", -1);
dtext_opt(3, startY + gap*3, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "Casio calculators", -1);
dtext_opt(3, startY + gap*4+2, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[Optn] key displays the help", -1);
dtext_opt(124, 62, C_BLACK, C_WHITE, DTEXT_RIGHT, DTEXT_BOTTOM, "[EXE] Start", -1);
dupdate();
dclear(C_WHITE);
sleep_ms(500);
while(1) {
clearevents();
uint key = getkey().key;
if (key == KEY_EXE || key == K_HELP) break;
}
}
// Controls/Help Menu
int help_menu() {
const int margin = 2;
drect_border(margin, margin, 128 - margin-1, 64 - margin-1, C_WHITE, 1, C_BLACK);
dtext_opt(margin+2, margin+3+8*0, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[Arrows]: Interact", -1);
dtext_opt(margin+2, margin+3+8*1, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[Shift]: Toggle edit mode", -1);
dtext_opt(margin+2, margin+3+8*2, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[Alpha]: Toggle color mode", -1);
dtext_opt(margin+2, margin+3+8*3, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[Del]: Reset the simulation", -1);
dtext_opt(margin+2, margin+3+8*4, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[+]/[-]: Toggle fullscreen", -1);
dtext_opt(margin+2, margin+3+8*5, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[x]: Hide/Show FPS", -1);
dtext_opt(margin+2, margin+3+8*6, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[Exit]: Quit [EXE]: Continue", -1);
dupdate();
dclear(C_WHITE);
while(1) {
uint key = getkey().key;
if (key == KEY_EXIT) return -1;
if (key == KEY_EXE || key == K_HELP) return 0;
}
// if (getkey().key == KEY_EXIT) return -1;
// return 0;
}
// Fluid Simulation Settings Menu
void settings_menu(bool redraw) {
static int selected = 0;
const int menuX = 66;
const int menuY = 2;
const int gap = 8;
const int rx = 64;
const bool activated = redraw;
bool updateRect = false, updateDyeD = activated, updateVelD = activated, updateDyeF = activated, updateVelF = activated, updateRadius = activated, updatePressureIter = activated;
static bool releaseParams = true;
if (currentView == 1 && releaseParams && keydown(K_PARAMS)) {
releaseParams = false;
currentView = 0;
int ry = menuY+gap*(selected+1)+2;
drect(rx, ry, rx, ry+2, C_WHITE);
drect_border(63, 0, 127, H-1, C_NONE, 1, C_WHITE);
}
else if (currentView != 1 && releaseParams && keydown(K_PARAMS)) {
releaseParams = false;
currentView = 1;
updateRect = true;
updateDyeD = true, updateVelD = true, updateDyeF = true, updateVelF = true, updateRadius = true, updatePressureIter = true;
drect(64, 0, 128, 64, C_WHITE);
drect_border(0, 0, W-1, H-1, C_NONE, 1, C_WHITE);
} else if (!keydown(K_PARAMS)) {
releaseParams = true;
}
if (currentView == 0) {
drect_border(0, 0, W-1, H-1, C_NONE, 1, C_BLACK);
if (!redraw) return;
} else if (currentView == 2) return;
{
drect_border(0, 0, W-1, H-1, C_NONE, 1, C_WHITE);
drect_border(63, 0, 127, H-1, C_NONE, 1, C_BLACK);
}
if (keydown(KEY_LEFT)) {
if (selected == 0 && dyeDiffusion > 0) {
dyeDiffusion -= PREC_STEP;
updateDyeD = true;
}
else if (selected == 1 && velDiffusion > 0) {
velDiffusion -= PREC_STEP;
updateVelD = true;
}
else if (selected == 2 && dyeIntensity > 0) {
dyeIntensity -= PREC_STEP;
updateDyeF = true;
}
else if (selected == 3 && velIntensity > 0) {
velIntensity -= PREC_STEP;
updateVelF = true;
}
else if (selected == 4 && radius > ONE) {
radius -= FIX(1);
updateRadius = true;
}
else if (selected == 5 && pressureIterations > 0) {
pressureIterations--;
updatePressureIter = true;
}
}
else if (keydown(KEY_RIGHT)) {
if (selected == 0 && dyeDiffusion < ONE) {
dyeDiffusion += PREC_STEP;
updateDyeD = true;
}
else if (selected == 1 && velDiffusion < ONE) {
velDiffusion += PREC_STEP;
updateVelD = true;
}
else if (selected == 2 && dyeIntensity < ONE*2) {
dyeIntensity += PREC_STEP;
updateDyeF = true;
}
else if (selected == 3 && velIntensity < ONE*2) {
velIntensity += PREC_STEP;
updateVelF = true;
}
else if (selected == 4 && radius < ONE*10) {
radius += FIX(1);
updateRadius = true;
}
else if (selected == 5) {
pressureIterations++;
updatePressureIter = true;
}
}
else if (keydown(KEY_UP)) {
int ry = menuY+gap*(selected+1)+2;
drect(rx, ry, rx, ry+2, C_WHITE);
if (--selected < 0) selected = 5;
updateRect = true;
}
else if (keydown(KEY_DOWN)) {
int ry = menuY+gap*(selected+1)+2;
drect(rx, ry, rx, ry+2, C_WHITE);
if (++selected > 5) selected = 0;
updateRect = true;
}
if (updateDyeD) {
sprintf(strDyeDiff, "dye diff:%.3f ", fixtof(dyeDiffusion));
dtext_opt(menuX, menuY + gap, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, strDyeDiff, -1);
}
if (updateVelD) {
sprintf(strVelDiff, "vel diff:%.3f ", fixtof(velDiffusion));
dtext_opt(menuX, menuY + gap*2, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, strVelDiff, -1);
}
if (updateDyeF) {
sprintf(strDyeForce, "dye force:%.1f ", fixtof(dyeIntensity));
dtext_opt(menuX, menuY + gap*3, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, strDyeForce, -1);
}
if (updateVelF) {
sprintf(strVelForce, "vel force:%.1f ", fixtof(velIntensity));
dtext_opt(menuX, menuY + gap*4, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, strVelForce, -1);
}
if (updateRadius) {
sprintf(strRadius, "radius: %d ", UNFIX(radius));
dtext_opt(menuX, menuY + gap*5, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, strRadius, -1);
}
if (updatePressureIter) {
sprintf(strPressureIter, "iterations: %d ", pressureIterations);
dtext_opt(menuX, menuY + gap*6, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, strPressureIter, -1);
}
if (updateRect) {
int ry = menuY+gap*(selected+1)+2;
drect(rx, ry, rx, ry+2, C_BLACK);
}
}
// Fluid color settings Menu
void color_menu() {
const int xmargin = 3;
const float fw = 64. - (float)xmargin * 2.;
const int y = 22;
const int h = 5;
const int bar_overflow = 3;
const int STEP_MULT = 20;
const float tresh1 = fixtof(emptyTreshold);
const float tresh2 = fixtof(saturateTreshold);
const int x1 = 64 + xmargin + (int)(tresh1 * fw);
const int x2 = 64 + xmargin + (int)(tresh2 * fw);
static bool x1Selected = true;
static bool releaseKey = true;
bool updated = false;
// Manage keys
if (keydown(K_COLORS) && releaseKey) {
releaseKey = false;
if (currentView == 2) {
currentView = 0;
drect_border(63, 0, 127, H-1, C_NONE, 1, C_WHITE);
}
else {
currentView = 2;
updated = true;
}
} else if (!keydown(K_COLORS) && !releaseKey) {
releaseKey = true;
}
if (currentView != 2) return;
if (keydown(KEY_F1)) {
emptyTreshold -= PREC_STEP * STEP_MULT;
if (emptyTreshold < 0) emptyTreshold = 0;
x1Selected = true;
updated = true;
}
else if (keydown(KEY_F2)) {
emptyTreshold += PREC_STEP * STEP_MULT;
if (emptyTreshold > ONE) emptyTreshold = ONE;
if (emptyTreshold > saturateTreshold) saturateTreshold = emptyTreshold;
x1Selected = true;
updated = true;
}
else if (keydown(KEY_F3)) {
saturateTreshold -= PREC_STEP * STEP_MULT;
if (saturateTreshold < 0) saturateTreshold = 0;
if (saturateTreshold < emptyTreshold) emptyTreshold = saturateTreshold;
x1Selected = false;
updated = true;
}
else if (keydown(KEY_F4)) {
saturateTreshold += PREC_STEP * STEP_MULT;
if (saturateTreshold > ONE) saturateTreshold = ONE;
x1Selected = false;
updated = true;
}
else if (keydown(KEY_F5)) {
fix diff = saturateTreshold - emptyTreshold;
emptyTreshold -= PREC_STEP * STEP_MULT;
if (emptyTreshold < 0) emptyTreshold = 0;
saturateTreshold = emptyTreshold + diff;
x1Selected = true;
updated = true;
}
else if (keydown(KEY_F6)) {
fix diff = saturateTreshold - emptyTreshold;
saturateTreshold += PREC_STEP * STEP_MULT;
if (saturateTreshold > ONE) saturateTreshold = ONE;
emptyTreshold = saturateTreshold - diff;
x1Selected = false;
updated = true;
}
if (updated) {
// Clear
drect(64, 0, 128, 64, C_WHITE);
drect_border(0, 0, W-1, H-1, C_NONE, 1, C_WHITE);
// Draw selectors
if (x1Selected) {
drect(x1-1, y - bar_overflow-1, x1+1, y+h+bar_overflow+1, C_BLACK);
dline(x2, y - bar_overflow, x2, y+h+bar_overflow, C_BLACK);
} else {
dline(x1, y - bar_overflow, x1, y+h+bar_overflow, C_BLACK);
drect(x2-1, y - bar_overflow-1, x2+1, y+h+bar_overflow+1, C_BLACK);
}
// Draw white band
drect(64 + xmargin, y, x1, y + h, C_WHITE);
// Draw checker band
for (int j = y; j <= y+h; j++) {
dline(x1, j, x2, j, j%2 ? C_BLACK : C_WHITE);
}
for (int i = x1; i < x2; i+=2) {
dline(i, y, i, y+h, C_BLACK);
}
// Draw black band
drect(x2, y, 128 - xmargin, y + h, C_BLACK);
// Draw border
drect_border(64 + xmargin-1, y-1, 128 - xmargin+1, y+h+1, C_NONE, 1, C_BLACK);
// Draw float values
sprintf(strEmptyTreshold, "%.2f", tresh1);
sprintf(strSaturateTreshold, "%.2f", tresh2);
dtext_opt(x1, y - bar_overflow - 1, C_BLACK, C_WHITE, DTEXT_CENTER, DTEXT_BOTTOM, strEmptyTreshold, -1);
dtext_opt(x2, y + h + bar_overflow + 1, C_BLACK, C_WHITE, DTEXT_CENTER, DTEXT_TOP, strSaturateTreshold, -1);
dtext_opt(65, 40, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[F1/F2] Lower", -1);
dtext_opt(65, 48, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[F3/F4] Upper", -1);
dtext_opt(65, 56, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, "[F5/F6] Both", -1);
}
drect_border(63, 0, 127, H-1, C_NONE, 1, C_BLACK);
}