Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
391 lines (330 sloc) 9.21 KB
#define RAND (S_=vec2(rand(S_), rand(S_+9.))).x
#define AnyADJ(e) (NW==e||SE==e||NE==e||SW==e||NN==e||SS==e||EE==e||WW==e)
precision highp float;
/**
* Game Rule Interactions.
*
* Each interaction use various probability. Some are very rare, some frequent.
/!\ here air means wind /!\ it is different of empty, the empty space is called "Nothing" aka N)
*
* Primary elements: Water, Fire, Earth, Air
* =======
* Water + Nothing => fall / slide
* Fire + Nothing => grow
* Air + Nothing => move (directional wind)
* Water + Air => Water is deviated (wind)
* Fire + Air => Fire decrease
* Earth + Water => rarely creates Water Source (water infiltration)
* Earth + Fire => rarely creates Volcano (fire melt ground into lava)
*
* Secondary elements: Source, Volcano
* =========
* Source + Nothing => creates Water (on bottom).
* Volcano + Nothing => creates Fire (on top)
* Volcano + Source => IF source on top of volcano: sometimes creates Ground. OTHERWISE: sometimes creates volcano.
* Volcano + Water => rarely creates Source.
* Earth + Volcano => rarely Volcano expand / grow up in the Earth.
* Earth + Source => rarely Source expand / infiltrate in the Earth.
* Source + Fire => Source die.
*
* Cases where nothing happens:
* Water + Fire
* Earth + Nothing
* Volcano + Fire
* Volcano + Air
* Earth + Air
* Source + Air
* Source + Water
*/
// Elements
int A = 0;
int E = 1;
int F = 2;
int W = 3;
int V = 4;
int S = 5;
int Al = 6;
int Ar = 7;
int G = 8;
uniform vec2 SZ;
uniform float SD;
uniform float TI;
uniform float TS;
uniform float ST;
uniform sampler2D state;
uniform bool RU;
uniform bool draw;
uniform ivec2 DP;
uniform float DR;
uniform int DO;
int get (int x_, int y_) {
vec2 uv = (gl_FragCoord.xy + vec2(x_, y_)) / SZ;
return (uv.x < 0.0 || uv.x >= 1.0 || uv.y < 0.0 || uv.y >= 1.0) ? 0 :
int(floor(.5 + 9. * texture2D(state, uv).r));
}
bool between (float f, float a, float b) {
return a <= f && f <= b;
}
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
float grassDistrib (vec2 p) {
return mix(
rand(vec2(p.x)),
0.5*(1.0+(cos(sin(p.y*0.01 + p.x * 0.05) + (1.0 + 0.3*sin(p.x*0.01)) * p.y * 0.08))),
0.6
);
}
bool hellTriggerPosition (vec2 p) {
if (TS==0.0) return false;
float hellTickStart = 800.0;
float hellTickInterv = 70.0;
float hellSize = 5.0;
float dt = TI - TS - hellTickStart;
float x = floor(dt / hellTickInterv);
float y = (dt - x * hellTickInterv);
return distance(vec2(2.0 * (hellSize * x - ST), hellSize * y), p) <= hellSize;
}
void main () {
vec2 p = gl_FragCoord.xy;
vec2 S_ = p + 0.001 * TI;
int NW = get(-1, 1);
int NN = get( 0, 1);
int NE = get( 1, 1);
int WW = get(-1, 0);
int CC = get( 0, 0);
int EE = get( 1, 0);
int SW = get(-1,-1);
int SS = get( 0,-1);
int SE = get( 1,-1);
bool prevIsSolid = CC==E||CC==G||CC==V||CC==S;
int r = A;
int grassMaxHeight = int(20.0 * pow(grassDistrib(p), 1.3));
float rainRelativeTime = mod(TI, 300.0);
float volcRelativeTime = mod(TI, 25.0);
//////// FIRE RULES ///////
// Fire grow / Fire + Water
if (
-0.05 * float(NW==W) + -0.40 * float(NN==W) + -0.05 * float(NE==W) + // If water drop...
-0.50 * float(WW==W) + -0.50 * float(CC==W) + -0.50 * float(EE==W) + // ...or water nearby.
0.35 * float(SW==F) + 0.90 * float(SS==F) + 0.35 * float(SE==F) // Fire will move up and expand a bit.
>= 0.9 - 0.6 * RAND // The sum of matched weights must be enough important, also with some randomness
|| // Fire propagation: When fire met grass, fire can stay to continue to consume it
CC == F && RAND < 0.9 && AnyADJ(G)
) {
r = F;
}
////// WATER RULES ///////
if (
// Water drop / Water + Fire
between(
0.3 * float(NW==W) + 0.9 * float(NN==W) + 0.3 * float(NE==W) +
0.1 * float(WW==W) + -0.3 * float(CC==F) + 0.1 * float(EE==W) +
-0.3 * float(SS==F)
,
0.9 - 0.6 * RAND,
1.4 + 0.3 * RAND
)
|| // Water flow on earth rules
!prevIsSolid &&
RAND < 0.98 &&
( (WW==W||NW==W) && SW==E || (EE==W||NE==W) && SE==E )
|| // Occasional rain
!prevIsSolid &&
p.y >= SZ.y-1.0 &&
rainRelativeTime < 100.0 &&
between(
p.x -
(rand(vec2(SD*0.7 + TI - rainRelativeTime)) * SZ.x) // Rain Start
,
0.0,
100.0 * rand(vec2(SD + TI - rainRelativeTime)) // Rain Length
)
|| // Source creates water
!prevIsSolid && (
0.9 * float(NW==S) + 1.0 * float(NN==S) + 0.9 * float(NE==S) +
0.7 * float(WW==S) + 0.7 * float(EE==S)
>= 1.0 - 0.3 * RAND
)
) {
r = W;
}
////// EARTH RULES ////
if (CC == E) {
// Hack to workaround with the bug in the terrain seamless
if (!(WW==A && EE==A)) r = E;
// Earth -> Source
if (
// water sometimes create sources
RAND < 0.3 && (
1.0 * float(NW==W) + 1.2 * float(NN==W) + 1.0 * float(NE==W) +
0.5 * float(WW==W) + 0.5 * float(EE==W) +
0.3 * float(SW==W) + 0.2 * float(SS==W) + 0.3 * float(SE==W)
>= 3.0 - 2.5 * RAND
)
||
// Source is going down
RAND < 0.01 && ( WW==S || NN==S || EE==S )
) {
r = S;
}
// Earth -> Volcano
if (
RAND < 0.006 + 0.02 * smoothstep(0.0, 10000.0, ST + p.x) && (
0.3 * float(NW==F) + 0.2 * float(SS==F) + 0.3 * float(NE==F) +
0.5 * float(WW==F) + 0.5 * float(EE==F) +
1.0 * float(SW==F) + 1.2 * float(NN==F) + 1.0 * float(SE==F)
>= 3.0 - 2.1 * RAND
)
||
// Volcano is going up
RAND < 0.01 + 0.02 * smoothstep(500.0, 5000.0, ST + p.x) && ( int(WW==V) + int(SS==V) + int(EE==V) + int(SE==V) + int(SW==V) > 1 )
) {
r = V;
}
}
////// Grass RULES ////
if (grassMaxHeight > 0) {
if (CC == G) {
r = G;
if (
CC == G &&
RAND < 0.9 && (
AnyADJ(F) ||
AnyADJ(V)
)) {
r = F;
}
}
else if (!prevIsSolid && (AnyADJ(E) || AnyADJ(G) || AnyADJ(S))) {
if (RAND < 0.03 &&
get(0, -grassMaxHeight) != G && (
SS==G && RAND < 0.07 || // The grass sometimes grow
SS==E && RAND < 0.02 || // The grass rarely spawn by itself
AnyADJ(W) ||
AnyADJ(S)
)
) {
r = G;
}
}
}
////// VOLCANO RULES /////
// Volcano creates fire
if ((!prevIsSolid || CC==F) && SS==V) {
r = F;
}
if (CC == V) {
r = V;
// if Water: Volcano -> Earth
if (
NW==W || NN==W || NE==W || EE==W || WW==W
) {
r = RAND < 0.8 ? S : E;
}
// cool down: Volcano -> Earth
if (
RAND<0.005 &&
( int(SW==F||SW==V) + int(SS==F||SS==V) + int(SE==F||SE==V) < 2 )
) {
r = E;
}
// Volcano <-> Source : A volcano can disappear near source
if (
int(NW==S) + int(SE==S) + int(NE==S) + int(SW==S) + int(NN==S) + int(SS==S) + int(EE==S) + int(WW==S)
> 1
) {
r = RAND < 0.2 ? V : (RAND < 0.8 ? S : E);
}
}
// Occasional volcano
if (prevIsSolid &&
p.y <= 2.0 &&
volcRelativeTime <= 1.0 &&
RAND < 0.3 &&
between(
p.x -
rand(vec2(SD*0.01 + TI - volcRelativeTime)) * SZ.x // Volc start
,
0.0,
10.0 * rand(vec2(SD*0.07 + TI - volcRelativeTime)) // Volc length
)
) {
r = V;
}
////// SOURCE RULES /////
if (CC == S) {
r = S;
// Dry: Source -> Earth
if (
RAND<0.06 &&
( int(NW==W||NW==S) + int(NN==W||NN==S) + int(NE==W||NE==S) < 1 )
|| // if Fire: Source -> Earth
( EE==F || WW==F || SS==F || SW==F || SE==F )
) {
r = E;
}
// Volcano <-> Source : A source can disappear near volcano
if (AnyADJ(V)) {
r = RAND < 0.15 ? V : (RAND < 0.6 ? S : E);
}
}
////// AIR RULES //////
if (r == A) {
if (RAND < 0.00001) r = Al;
if (RAND < 0.00001) r = Ar;
}
int wind = r==Al ? Al : r == Ar ? Ar : 0;
float f =
(-0.1+0.05*(RAND-0.5)) * float(NW==Ar) + 0.12 * float(NE==Al) +
-0.65 * float(WW==Ar) + 0.65 * float(EE==Al) +
-0.1 * float(SW==Ar) + (0.2+0.05*(RAND-0.5)) * float(SE==Al) ;
if (between(f, 0.4 * RAND, 0.95)) {
wind = Al;
}
else if (between(f, -0.95, -0.4 * RAND)) {
wind = Ar;
}
if (wind != 0) {
if (r == A) {
r = wind;
}
else if(r == F) {
if (RAND < 0.4) r = wind;
}
else if (r == W) {
if (RAND < 0.1) r = wind;
}
}
//// DRAW //////
if (draw) {
vec2 pos = floor(p);
if (distance(pos, vec2(DP)) <= DR) {
if (DO == W) {
if (prevIsSolid && CC!=G) {
r = S;
}
else if (!prevIsSolid && mod(pos.x + pos.y, 2.0)==0.0) {
r = W;
}
}
else if (DO == F) {
r = prevIsSolid ? V : F;
}
else {
r = DO;
}
}
}
//// Hell /////
if (hellTriggerPosition(p)) {
r = prevIsSolid ? V : F;
}
///// keep only simple elements when not RU /////
if (!RU) {
if (r == F || r == W|| r == G) r = A;
if (r == V || r == S) r = E;
}
///// Return the color result /////
gl_FragColor = vec4(float(r) / 9.0, 0.0, 0.0, 1.0);
}