liquid
This commit is contained in:
@@ -320,7 +320,7 @@ public:
|
||||
QueryDistanceNode(int32_t id, int32_t maxDist)
|
||||
: tileID(id), maxDistance(maxDist) {}
|
||||
|
||||
Type GetOutputType() const override { return Type::Int; }
|
||||
Type GetOutputType() const override { return Type::Float; }
|
||||
std::vector<Type> GetInputTypes() const override { return {}; }
|
||||
std::string GetName() const override { return "QueryDistance"; }
|
||||
Value Evaluate(const EvalContext& ctx, const std::vector<Value>&) const override {
|
||||
@@ -333,7 +333,57 @@ public:
|
||||
best = d;
|
||||
}
|
||||
}
|
||||
return Value::MakeInt(best);
|
||||
return Value::MakeFloat(1.f - static_cast<float>(best) / (maxDistance + 1));
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns true when the cell at (worldX, worldY) is AIR in the previous pass,
|
||||
/// has solid ground within maxDepth tiles below, and is enclosed by solid walls
|
||||
/// within maxWidth tiles on both the left and right at this cell's Y level.
|
||||
class LiquidNode : public Node {
|
||||
public:
|
||||
int32_t maxWidth { 8 };
|
||||
int32_t maxDepth { 4 };
|
||||
|
||||
LiquidNode() = default;
|
||||
LiquidNode(int32_t w, int32_t d) : maxWidth(w), maxDepth(d) {}
|
||||
|
||||
Type GetOutputType() const override { return Type::Bool; }
|
||||
std::vector<Type> GetInputTypes() const override { return {}; }
|
||||
std::string GetName() const override { return "QueryLiquid"; }
|
||||
Value Evaluate(const EvalContext& ctx, const std::vector<Value>&) const override {
|
||||
// Quick discard: cell itself must be AIR
|
||||
if (ctx.GetPrevTile(ctx.worldX, ctx.worldY) != 0)
|
||||
return Value::MakeBool(false);
|
||||
|
||||
// Ground below (Y increases upward, so below = decreasing Y)
|
||||
bool hasGround = false;
|
||||
for (int32_t dy = 1; dy <= maxDepth; ++dy)
|
||||
if (ctx.GetPrevTile(ctx.worldX, ctx.worldY - dy) != 0) { hasGround = true; break; }
|
||||
if (!hasGround) return Value::MakeBool(false);
|
||||
|
||||
// Left wall: scan left, but stop early if an intermediate AIR cell has
|
||||
// no ground below it — liquid would drain through that gap.
|
||||
bool hasLeft = false;
|
||||
for (int32_t dx = 1; dx <= maxWidth; ++dx) {
|
||||
if (ctx.GetPrevTile(ctx.worldX - dx, ctx.worldY) != 0) { hasLeft = true; break; }
|
||||
bool floored = false;
|
||||
for (int32_t dy = 1; dy <= maxDepth; ++dy)
|
||||
if (ctx.GetPrevTile(ctx.worldX - dx, ctx.worldY - dy) != 0) { floored = true; break; }
|
||||
if (!floored) break;
|
||||
}
|
||||
if (!hasLeft) return Value::MakeBool(false);
|
||||
|
||||
// Right wall: same floor-continuity check.
|
||||
for (int32_t dx = 1; dx <= maxWidth; ++dx) {
|
||||
if (ctx.GetPrevTile(ctx.worldX + dx, ctx.worldY) != 0)
|
||||
return Value::MakeBool(true);
|
||||
bool floored = false;
|
||||
for (int32_t dy = 1; dy <= maxDepth; ++dy)
|
||||
if (ctx.GetPrevTile(ctx.worldX + dx, ctx.worldY - dy) != 0) { floored = true; break; }
|
||||
if (!floored) return Value::MakeBool(false);
|
||||
}
|
||||
return Value::MakeBool(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -704,23 +754,65 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Cellular (Worley) noise — output in [-1, 1] (Float)
|
||||
/// Returns true for the single tile that is closest to the centre of its
|
||||
/// Voronoi cell. Exactly one tile per cell returns true. (Bool)
|
||||
///
|
||||
/// Works by finding the nearest jittered feature point in scaled lattice space,
|
||||
/// then rounding it back to the nearest integer world coordinate. A tile
|
||||
/// returns true only when it IS that rounded coordinate.
|
||||
class CellularNoiseNode : public Node {
|
||||
public:
|
||||
float frequency { 0.01f };
|
||||
|
||||
explicit CellularNoiseNode(float freq = 0.01f) : frequency(freq) {}
|
||||
|
||||
Type GetOutputType() const override { return Type::Float; }
|
||||
Type GetOutputType() const override { return Type::Bool; }
|
||||
std::vector<Type> GetInputTypes() const override { return {}; }
|
||||
std::string GetName() const override { return "CellularNoise"; }
|
||||
Value Evaluate(const EvalContext& ctx, const std::vector<Value>&) const override {
|
||||
FastNoiseLite fn;
|
||||
fn.SetSeed(static_cast<int>(ctx.seed));
|
||||
fn.SetNoiseType(FastNoiseLite::NoiseType_Cellular);
|
||||
fn.SetFrequency(frequency);
|
||||
return Value::MakeFloat(fn.GetNoise(static_cast<float>(ctx.worldX),
|
||||
static_cast<float>(ctx.worldY)));
|
||||
// Map world position into lattice space.
|
||||
float sx = ctx.worldX * frequency;
|
||||
float sy = ctx.worldY * frequency;
|
||||
int32_t cellX = static_cast<int32_t>(std::floor(sx));
|
||||
int32_t cellY = static_cast<int32_t>(std::floor(sy));
|
||||
|
||||
// Search the 3×3 neighbourhood for the nearest feature point.
|
||||
float bestDist2 = 1e30f;
|
||||
float bestFX = 0.0f, bestFY = 0.0f;
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
for (int dx = -1; dx <= 1; ++dx) {
|
||||
int32_t nx = cellX + dx;
|
||||
int32_t ny = cellY + dy;
|
||||
|
||||
// Hash (seed, nx, ny) → a jitter in [0, 1) for each axis.
|
||||
uint32_t h = static_cast<uint32_t>(ctx.seed)
|
||||
^ (static_cast<uint32_t>(nx) * 2654435761u)
|
||||
^ (static_cast<uint32_t>(ny) * 2246822519u);
|
||||
h ^= h >> 16;
|
||||
h *= 0x45d9f3bu;
|
||||
h ^= h >> 16;
|
||||
float jx = (h & 0xFFFFu) / 65536.0f;
|
||||
float jy = (h >> 16) / 65536.0f;
|
||||
|
||||
float fpX = static_cast<float>(nx) + jx;
|
||||
float fpY = static_cast<float>(ny) + jy;
|
||||
float ddx = fpX - sx;
|
||||
float ddy = fpY - sy;
|
||||
float d2 = ddx * ddx + ddy * ddy;
|
||||
|
||||
if (d2 < bestDist2) {
|
||||
bestDist2 = d2;
|
||||
bestFX = fpX;
|
||||
bestFY = fpY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The centre tile is the integer world coordinate nearest to the
|
||||
// feature point. Return true only when this tile IS that coordinate.
|
||||
auto centerX = static_cast<int32_t>(std::round(bestFX / frequency));
|
||||
auto centerY = static_cast<int32_t>(std::round(bestFY / frequency));
|
||||
return Value::MakeBool(ctx.worldX == centerX && ctx.worldY == centerY);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user