SHARE
TWEET

Untitled

a guest May 22nd, 2019 85 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #ifndef LIQUID_CELLUAR_H_
  2. #define LIQUID_CELLUAR_H_
  3.  
  4. #ifndef LC_NO_STDLIB
  5. #include <stdlib.h>
  6. #define LC_MALLOC malloc
  7. #define LC_FREE free
  8. #else
  9. #ifndef LC_MALLOC
  10. #error "No stdlib mode, but LC_MALLOC not defined"
  11. #endif
  12. #ifndef LC_FREE
  13. #error "No stdlib mode, but LC_FREE not defined"
  14. #endif
  15. #endif
  16.  
  17. #ifdef LC_STATIC
  18. #define LCDEF static
  19. #else
  20. #define LCDEF extern
  21. #endif
  22.  
  23. enum LCCellType {
  24.     LC_CELL_VACUUM,
  25.     LC_CELL_SOLID,
  26. };
  27.  
  28. enum LCFlowDirection {
  29.     LC_TOP = 0,
  30.     LC_RIGHT = 1,
  31.     LC_BOTTOM = 2,
  32.     LC_LEFT = 3
  33. };
  34.  
  35. typedef struct LCCell LCCell;
  36.  
  37. struct LCCell {
  38.     int Type;
  39.     float Liquid;
  40.     int Settled;   
  41.     int SettleCount;
  42.     int FlowDirections[4];
  43.     LCCell *Neighbors[4];
  44. };
  45.  
  46. typedef struct LCWorld {
  47.     // Max and min cell liquid values
  48.     float MaxValue;
  49.     float MinValue;
  50.  
  51.     // Extra liquid a cell can store than the cell above it
  52.     float MaxCompression;
  53.  
  54.     // Lowest and highest amount of liquids allowed to flow per iteration
  55.     float MinFlow;
  56.     float MaxFlow;
  57.  
  58.     // Adjusts flow speed (0.0f - 1.0f)
  59.     float FlowSpeed;
  60.  
  61.     // Keep track of modifications to cell liquid values
  62.     float *Diffs;
  63.  
  64.     LCCell *Cells;
  65.     int width;
  66.     int height;
  67. } LCWorld;
  68.  
  69. LCDEF void LCInit(LCWorld *world, int width, int height);
  70. LCDEF void LCClose(LCWorld *world);
  71. LCDEF void LCSimulate(LCWorld *world);
  72.  
  73. #endif // LIQUID_CELLUAR_H_
  74.  
  75.  
  76. #ifdef LIQUID_CELLUAR_IMPLEMENTATION
  77.  
  78. #ifndef LIQUID_CELLUAR_IMPLEMENTATION_SINGLE
  79. #define LIQUID_CELLUAR_IMPLEMENTATION_SINGLE
  80. #else
  81. #error "liquid_celluar header included second time"
  82. #endif // LIQUID_CELLUAR_IMPLEMENTATION_SINGLE
  83.  
  84. void LCInit(LCWorld *world, int width, int height) {
  85.     if (world->Diffs) LC_FREE(world->Diffs);
  86.     if (world->Cells) LC_FREE(world->Cells);
  87.     world->MaxValue = 1.0;
  88.     world->MinValue = 0.005;
  89.     world->MaxCompression = 0.25;
  90.     world->MinFlow = 0.005;
  91.     world->MaxFlow = 4;
  92.     world->FlowSpeed = 1;
  93.     world->width = width;
  94.     world->height = height;
  95.     world->Diffs = LC_MALLOC(sizeof *world->Diffs * width*height);
  96.     world->Cells = LC_MALLOC(sizeof *world->Cells * width*height);
  97. }
  98.  
  99. void LCClose(LCWorld *world) {
  100.     if (world->Diffs) LC_FREE(world->Diffs);
  101.     if (world->Cells) LC_FREE(world->Cells);
  102.     world->width = 0;
  103.     world->height = 0;
  104. }
  105.  
  106. #define LC_IDX(W,X,Y) ((X)*((W)->height)+(Y))
  107. #define LC_MIN(X,Y) ((X)<(Y)?(X):(Y))
  108. #define LC_MAX(X,Y) ((X)>(Y)?(X):(Y))
  109.  
  110. // Calculate how much liquid should flow to destination with pressure
  111. static float CalculateVerticalFlowValue(LCWorld *world, float remainingLiquid, LCCell *destination) {
  112.     float sum = remainingLiquid + destination->Liquid;
  113.     float value = 0;
  114.  
  115.     if (sum <= world->MaxValue) {
  116.         value = world->MaxValue;
  117.     } else if (sum < 2 * world->MaxValue + world->MaxCompression) {
  118.         value = (world->MaxValue * world->MaxValue + sum * world->MaxCompression) / (world->MaxValue + world->MaxCompression);
  119.     } else {
  120.         value = (sum + world->MaxCompression) / 2;
  121.     }
  122.  
  123.     return value;
  124. }
  125.  
  126. static void ResetFlowDirections(LCCell *cell) {
  127.     cell->FlowDirections[LC_TOP] = 0;
  128.     cell->FlowDirections[LC_RIGHT] = 0;
  129.     cell->FlowDirections[LC_BOTTOM] = 0;
  130.     cell->FlowDirections[LC_LEFT] = 0;
  131. }
  132.  
  133. // Force neighbors to simulate on next iteration
  134. static void UnsettleNeighbors(LCCell *cell) {
  135.     if (cell->Neighbors[LC_TOP]) cell->Neighbors[LC_TOP]->Settled = 0;
  136.     if (cell->Neighbors[LC_RIGHT]) cell->Neighbors[LC_RIGHT]->Settled = 0;
  137.     if (cell->Neighbors[LC_BOTTOM]) cell->Neighbors[LC_BOTTOM]->Settled = 0;
  138.     if (cell->Neighbors[LC_LEFT]) cell->Neighbors[LC_LEFT]->Settled = 0;
  139. }
  140.  
  141. // Run one simulation step
  142. void LCSimulate(LCWorld *world) {
  143.     int width = world->width;
  144.     int height = world->height;
  145.     LCCell *cells = world->Cells;
  146.     float flow = 0;
  147.  
  148.     // Reset the diffs array
  149.     for (int x = 0, index = 0; x < width; x++) {
  150.         for (int y = 0; y < height; y++, index++) {
  151.             world->Diffs[index] = 0;
  152.         }
  153.     }
  154.  
  155.     // Main loop
  156.     for (int x = 0, index = 0; x < width; x++) {
  157.         for (int y = 0; y < height; y++, index++) {
  158.  
  159.             // Get reference to Cell and reset flow
  160.             LCCell *cell = cells + index;
  161.             ResetFlowDirections(cell);
  162.  
  163.             // Validate cell
  164.             if (cell->Type == LC_CELL_SOLID) {
  165.                 cell->Liquid = 0;
  166.                 continue;
  167.             }
  168.             if (cell->Liquid == 0)
  169.                 continue;
  170.             if (cell->Settled)
  171.                 continue;
  172.             if (cell->Liquid < world->MinValue) {
  173.                 cell->Liquid = 0;
  174.                 continue;
  175.             }
  176.  
  177.             // Keep track of how much liquid this cell started off with
  178.             float startValue = cell->Liquid;
  179.             float remainingValue = cell->Liquid;
  180.             flow = 0;
  181.  
  182.             // Flow to bottom cell
  183.             if (cell->Neighbors[LC_BOTTOM] && cell->Neighbors[LC_BOTTOM]->Type == LC_CELL_VACUUM) {
  184.  
  185.                 // Determine rate of flow
  186.                 flow = CalculateVerticalFlowValue(world, cell->Liquid, cell->Neighbors[LC_BOTTOM]) - cell->Neighbors[LC_BOTTOM]->Liquid;
  187.                 if (cell->Neighbors[LC_BOTTOM]->Liquid > 0 && flow > world->MinFlow)
  188.                     flow *= world->FlowSpeed;
  189.  
  190.                 // Constrain flow
  191.                 flow = LC_MAX(flow, 0);
  192.                 if (flow > LC_MIN(world->MaxFlow, cell->Liquid))
  193.                     flow = LC_MIN(world->MaxFlow, cell->Liquid);
  194.  
  195.                 // Update temp values
  196.                 if (flow != 0) {
  197.                     remainingValue -= flow;
  198.                     world->Diffs[index] -= flow;
  199.                     world->Diffs[LC_IDX(world, x, y + 1)] += flow;
  200.                     cell->FlowDirections[LC_BOTTOM] = 1;
  201.                     cell->Neighbors[LC_BOTTOM]->Settled = 0;
  202.                 }
  203.             }
  204.  
  205.             // Check to ensure we still have liquid in this cell
  206.             if (remainingValue < world->MinValue) {
  207.                 world->Diffs[index] -= remainingValue;
  208.                 continue;
  209.             }
  210.  
  211.             // Flow to left cell
  212.             if (cell->Neighbors[LC_LEFT] && cell->Neighbors[LC_LEFT]->Type == LC_CELL_VACUUM) {
  213.  
  214.                 // Calculate flow rate
  215.                 flow = (remainingValue - cell->Neighbors[LC_LEFT]->Liquid) / 4;
  216.                 if (flow > world->MinFlow)
  217.                     flow *= world->FlowSpeed;
  218.  
  219.                 // constrain flow
  220.                 flow = LC_MAX(flow, 0);
  221.                 if (flow > LC_MIN(world->MaxFlow, remainingValue))
  222.                     flow = LC_MIN(world->MaxFlow, remainingValue);
  223.  
  224.                 // Adjust temp values
  225.                 if (flow != 0) {
  226.                     remainingValue -= flow;
  227.                     world->Diffs[index] -= flow;
  228.                     world->Diffs[LC_IDX(world, x - 1, y)] += flow;
  229.                     cell->FlowDirections[LC_LEFT] = 1;
  230.                     cell->Neighbors[LC_LEFT]->Settled = 0;
  231.                 }
  232.             }
  233.  
  234.             // Check to ensure we still have liquid in this cell
  235.             if (remainingValue < world->MinValue) {
  236.                 world->Diffs[index] -= remainingValue;
  237.                 continue;
  238.             }
  239.  
  240.             // Flow to right cell
  241.             if (cell->Neighbors[LC_RIGHT] && cell->Neighbors[LC_RIGHT]->Type == LC_CELL_VACUUM) {
  242.  
  243.                 // calc flow rate
  244.                 flow = (remainingValue - cell->Neighbors[LC_RIGHT]->Liquid) / 3;                                       
  245.                 if (flow > world->MinFlow)
  246.                     flow *= world->FlowSpeed;
  247.  
  248.                 // constrain flow
  249.                 flow = LC_MAX(flow, 0);
  250.                 if (flow > LC_MIN(world->MaxFlow, remainingValue))
  251.                     flow = LC_MIN(world->MaxFlow, remainingValue);
  252.  
  253.                 // Adjust temp values
  254.                 if (flow != 0) {
  255.                     remainingValue -= flow;
  256.                     world->Diffs[index] -= flow;
  257.                     world->Diffs[LC_IDX(world, x + 1, y)] += flow;
  258.                     cell->FlowDirections[LC_RIGHT] = 1;
  259.                     cell->Neighbors[LC_RIGHT]->Settled = 0;
  260.                 }
  261.             }
  262.  
  263.             // Check to ensure we still have liquid in this cell
  264.             if (remainingValue < world->MinValue) {
  265.                 world->Diffs[index] -= remainingValue;
  266.                 continue;
  267.             }
  268.  
  269.             // Flow to Top cell
  270.             if (cell->Neighbors[LC_TOP] && cell->Neighbors[LC_TOP]->Type == LC_CELL_VACUUM) {
  271.  
  272.                 flow = remainingValue - CalculateVerticalFlowValue(world, remainingValue, cell->Neighbors[LC_TOP]);
  273.                 if (flow > world->MinFlow)
  274.                     flow *= world->FlowSpeed;
  275.  
  276.                 // constrain flow
  277.                 flow = LC_MAX(flow, 0);
  278.                 if (flow > LC_MIN(world->MaxFlow, remainingValue))
  279.                     flow = LC_MIN(world->MaxFlow, remainingValue);
  280.  
  281.                 // Adjust values
  282.                 if (flow != 0) {
  283.                     remainingValue -= flow;
  284.                     world->Diffs[index] -= flow;
  285.                     world->Diffs[LC_IDX(world, x, y - 1)] += flow;
  286.                     cell->FlowDirections[LC_TOP] = 1;
  287.                     cell->Neighbors[LC_TOP]->Settled = 0;
  288.                 }
  289.             }
  290.  
  291.             // Check to ensure we still have liquid in this cell
  292.             if (remainingValue < world->MinValue) {
  293.                 world->Diffs[index] -= remainingValue;
  294.                 continue;
  295.             }
  296.  
  297.             // Check if cell is settled
  298.             if (startValue == remainingValue) {
  299.                 cell->SettleCount++;
  300.                 if (cell->SettleCount >= 10) {
  301.                     ResetFlowDirections(cell);
  302.                     cell->Settled = 1;
  303.                 }
  304.             } else {
  305.                 UnsettleNeighbors(cell);
  306.             }
  307.         }
  308.     }
  309.  
  310.     // Update Cell values
  311.     for (int x = 0, index = 0; x < width; x++) {
  312.         for (int y = 0; y < height; y++, index++) {
  313.             LCCell *cell = cells + index;
  314.             cell->Liquid += world->Diffs[index];
  315.             if (cell->Liquid < world->MinValue) {
  316.                 cell->Liquid = 0;
  317.                 cell->Settled = 0;
  318.             }              
  319.         }
  320.     }          
  321. }
  322.  
  323. #undef LC_IDX
  324. #undef LC_MIN
  325. #undef LC_MAX
  326.  
  327. #endif // LIQUID_CELLUAR_IMPLEMENTATION
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top