Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #ifndef LIQUID_CELLUAR_H_
- #define LIQUID_CELLUAR_H_
- #ifndef LC_NO_STDLIB
- #include <stdlib.h>
- #define LC_MALLOC malloc
- #define LC_FREE free
- #else
- #ifndef LC_MALLOC
- #error "No stdlib mode, but LC_MALLOC not defined"
- #endif
- #ifndef LC_FREE
- #error "No stdlib mode, but LC_FREE not defined"
- #endif
- #endif
- #ifdef LC_STATIC
- #define LCDEF static
- #else
- #define LCDEF extern
- #endif
- enum LCCellType {
- LC_CELL_VACUUM,
- LC_CELL_SOLID,
- };
- enum LCFlowDirection {
- LC_TOP = 0,
- LC_RIGHT = 1,
- LC_BOTTOM = 2,
- LC_LEFT = 3
- };
- typedef struct LCCell LCCell;
- struct LCCell {
- int Type;
- float Liquid;
- int Settled;
- int SettleCount;
- int FlowDirections[4];
- LCCell *Neighbors[4];
- };
- typedef struct LCWorld {
- // Max and min cell liquid values
- float MaxValue;
- float MinValue;
- // Extra liquid a cell can store than the cell above it
- float MaxCompression;
- // Lowest and highest amount of liquids allowed to flow per iteration
- float MinFlow;
- float MaxFlow;
- // Adjusts flow speed (0.0f - 1.0f)
- float FlowSpeed;
- // Keep track of modifications to cell liquid values
- float *Diffs;
- LCCell *Cells;
- int width;
- int height;
- } LCWorld;
- LCDEF void LCInit(LCWorld *world, int width, int height);
- LCDEF void LCClose(LCWorld *world);
- LCDEF void LCSimulate(LCWorld *world);
- #endif // LIQUID_CELLUAR_H_
- #ifdef LIQUID_CELLUAR_IMPLEMENTATION
- #ifndef LIQUID_CELLUAR_IMPLEMENTATION_SINGLE
- #define LIQUID_CELLUAR_IMPLEMENTATION_SINGLE
- #else
- #error "liquid_celluar header included second time"
- #endif // LIQUID_CELLUAR_IMPLEMENTATION_SINGLE
- void LCInit(LCWorld *world, int width, int height) {
- if (world->Diffs) LC_FREE(world->Diffs);
- if (world->Cells) LC_FREE(world->Cells);
- world->MaxValue = 1.0;
- world->MinValue = 0.005;
- world->MaxCompression = 0.25;
- world->MinFlow = 0.005;
- world->MaxFlow = 4;
- world->FlowSpeed = 1;
- world->width = width;
- world->height = height;
- world->Diffs = LC_MALLOC(sizeof *world->Diffs * width*height);
- world->Cells = LC_MALLOC(sizeof *world->Cells * width*height);
- }
- void LCClose(LCWorld *world) {
- if (world->Diffs) LC_FREE(world->Diffs);
- if (world->Cells) LC_FREE(world->Cells);
- world->width = 0;
- world->height = 0;
- }
- #define LC_IDX(W,X,Y) ((X)*((W)->height)+(Y))
- #define LC_MIN(X,Y) ((X)<(Y)?(X):(Y))
- #define LC_MAX(X,Y) ((X)>(Y)?(X):(Y))
- // Calculate how much liquid should flow to destination with pressure
- static float CalculateVerticalFlowValue(LCWorld *world, float remainingLiquid, LCCell *destination) {
- float sum = remainingLiquid + destination->Liquid;
- float value = 0;
- if (sum <= world->MaxValue) {
- value = world->MaxValue;
- } else if (sum < 2 * world->MaxValue + world->MaxCompression) {
- value = (world->MaxValue * world->MaxValue + sum * world->MaxCompression) / (world->MaxValue + world->MaxCompression);
- } else {
- value = (sum + world->MaxCompression) / 2;
- }
- return value;
- }
- static void ResetFlowDirections(LCCell *cell) {
- cell->FlowDirections[LC_TOP] = 0;
- cell->FlowDirections[LC_RIGHT] = 0;
- cell->FlowDirections[LC_BOTTOM] = 0;
- cell->FlowDirections[LC_LEFT] = 0;
- }
- // Force neighbors to simulate on next iteration
- static void UnsettleNeighbors(LCCell *cell) {
- if (cell->Neighbors[LC_TOP]) cell->Neighbors[LC_TOP]->Settled = 0;
- if (cell->Neighbors[LC_RIGHT]) cell->Neighbors[LC_RIGHT]->Settled = 0;
- if (cell->Neighbors[LC_BOTTOM]) cell->Neighbors[LC_BOTTOM]->Settled = 0;
- if (cell->Neighbors[LC_LEFT]) cell->Neighbors[LC_LEFT]->Settled = 0;
- }
- // Run one simulation step
- void LCSimulate(LCWorld *world) {
- int width = world->width;
- int height = world->height;
- LCCell *cells = world->Cells;
- float flow = 0;
- // Reset the diffs array
- for (int x = 0, index = 0; x < width; x++) {
- for (int y = 0; y < height; y++, index++) {
- world->Diffs[index] = 0;
- }
- }
- // Main loop
- for (int x = 0, index = 0; x < width; x++) {
- for (int y = 0; y < height; y++, index++) {
- // Get reference to Cell and reset flow
- LCCell *cell = cells + index;
- ResetFlowDirections(cell);
- // Validate cell
- if (cell->Type == LC_CELL_SOLID) {
- cell->Liquid = 0;
- continue;
- }
- if (cell->Liquid == 0)
- continue;
- if (cell->Settled)
- continue;
- if (cell->Liquid < world->MinValue) {
- cell->Liquid = 0;
- continue;
- }
- // Keep track of how much liquid this cell started off with
- float startValue = cell->Liquid;
- float remainingValue = cell->Liquid;
- flow = 0;
- // Flow to bottom cell
- if (cell->Neighbors[LC_BOTTOM] && cell->Neighbors[LC_BOTTOM]->Type == LC_CELL_VACUUM) {
- // Determine rate of flow
- flow = CalculateVerticalFlowValue(world, cell->Liquid, cell->Neighbors[LC_BOTTOM]) - cell->Neighbors[LC_BOTTOM]->Liquid;
- if (cell->Neighbors[LC_BOTTOM]->Liquid > 0 && flow > world->MinFlow)
- flow *= world->FlowSpeed;
- // Constrain flow
- flow = LC_MAX(flow, 0);
- if (flow > LC_MIN(world->MaxFlow, cell->Liquid))
- flow = LC_MIN(world->MaxFlow, cell->Liquid);
- // Update temp values
- if (flow != 0) {
- remainingValue -= flow;
- world->Diffs[index] -= flow;
- world->Diffs[LC_IDX(world, x, y + 1)] += flow;
- cell->FlowDirections[LC_BOTTOM] = 1;
- cell->Neighbors[LC_BOTTOM]->Settled = 0;
- }
- }
- // Check to ensure we still have liquid in this cell
- if (remainingValue < world->MinValue) {
- world->Diffs[index] -= remainingValue;
- continue;
- }
- // Flow to left cell
- if (cell->Neighbors[LC_LEFT] && cell->Neighbors[LC_LEFT]->Type == LC_CELL_VACUUM) {
- // Calculate flow rate
- flow = (remainingValue - cell->Neighbors[LC_LEFT]->Liquid) / 4;
- if (flow > world->MinFlow)
- flow *= world->FlowSpeed;
- // constrain flow
- flow = LC_MAX(flow, 0);
- if (flow > LC_MIN(world->MaxFlow, remainingValue))
- flow = LC_MIN(world->MaxFlow, remainingValue);
- // Adjust temp values
- if (flow != 0) {
- remainingValue -= flow;
- world->Diffs[index] -= flow;
- world->Diffs[LC_IDX(world, x - 1, y)] += flow;
- cell->FlowDirections[LC_LEFT] = 1;
- cell->Neighbors[LC_LEFT]->Settled = 0;
- }
- }
- // Check to ensure we still have liquid in this cell
- if (remainingValue < world->MinValue) {
- world->Diffs[index] -= remainingValue;
- continue;
- }
- // Flow to right cell
- if (cell->Neighbors[LC_RIGHT] && cell->Neighbors[LC_RIGHT]->Type == LC_CELL_VACUUM) {
- // calc flow rate
- flow = (remainingValue - cell->Neighbors[LC_RIGHT]->Liquid) / 3;
- if (flow > world->MinFlow)
- flow *= world->FlowSpeed;
- // constrain flow
- flow = LC_MAX(flow, 0);
- if (flow > LC_MIN(world->MaxFlow, remainingValue))
- flow = LC_MIN(world->MaxFlow, remainingValue);
- // Adjust temp values
- if (flow != 0) {
- remainingValue -= flow;
- world->Diffs[index] -= flow;
- world->Diffs[LC_IDX(world, x + 1, y)] += flow;
- cell->FlowDirections[LC_RIGHT] = 1;
- cell->Neighbors[LC_RIGHT]->Settled = 0;
- }
- }
- // Check to ensure we still have liquid in this cell
- if (remainingValue < world->MinValue) {
- world->Diffs[index] -= remainingValue;
- continue;
- }
- // Flow to Top cell
- if (cell->Neighbors[LC_TOP] && cell->Neighbors[LC_TOP]->Type == LC_CELL_VACUUM) {
- flow = remainingValue - CalculateVerticalFlowValue(world, remainingValue, cell->Neighbors[LC_TOP]);
- if (flow > world->MinFlow)
- flow *= world->FlowSpeed;
- // constrain flow
- flow = LC_MAX(flow, 0);
- if (flow > LC_MIN(world->MaxFlow, remainingValue))
- flow = LC_MIN(world->MaxFlow, remainingValue);
- // Adjust values
- if (flow != 0) {
- remainingValue -= flow;
- world->Diffs[index] -= flow;
- world->Diffs[LC_IDX(world, x, y - 1)] += flow;
- cell->FlowDirections[LC_TOP] = 1;
- cell->Neighbors[LC_TOP]->Settled = 0;
- }
- }
- // Check to ensure we still have liquid in this cell
- if (remainingValue < world->MinValue) {
- world->Diffs[index] -= remainingValue;
- continue;
- }
- // Check if cell is settled
- if (startValue == remainingValue) {
- cell->SettleCount++;
- if (cell->SettleCount >= 10) {
- ResetFlowDirections(cell);
- cell->Settled = 1;
- }
- } else {
- UnsettleNeighbors(cell);
- }
- }
- }
- // Update Cell values
- for (int x = 0, index = 0; x < width; x++) {
- for (int y = 0; y < height; y++, index++) {
- LCCell *cell = cells + index;
- cell->Liquid += world->Diffs[index];
- if (cell->Liquid < world->MinValue) {
- cell->Liquid = 0;
- cell->Settled = 0;
- }
- }
- }
- }
- #undef LC_IDX
- #undef LC_MIN
- #undef LC_MAX
- #endif // LIQUID_CELLUAR_IMPLEMENTATION
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement