Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions Sources/libnoise/Cache.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package libnoise;

/**
* A 3D array of data, used internally.
*/
class Cache<T> {

var data : Array<Array<Array<T>>> = null;

public var empty(get, never) : Bool;

public var xMin(default, null) : Int = 0;
public var yMin(default, null) : Int = 0;
public var zMin(default, null) : Int = 0;
public var xMax(default, null) : Int = -1;
public var yMax(default, null) : Int = -1;
public var zMax(default, null) : Int = -1;

public inline function new() {
}

/**
* Allocates enough space to cache the given region. This will overwrite any existing data.
*/
public inline function allocate(xMin : Int, yMin : Int, zMin : Int, xMax : Int, yMax : Int, zMax : Int) : Void {
data = [];
for (x in 0...(xMax - xMin + 1)) {
data.push([]);
for (y in 0...(yMax - yMin + 1)) {
data[x].push([]);

// `resize()` will insert appropriate default values.
data[x][y].resize(zMax - zMin + 1);
}
}

this.xMin = xMin;
this.yMin = yMin;
this.zMin = zMin;
this.xMax = xMax;
this.yMax = yMax;
this.zMax = zMax;
}

public inline function clear() : Void {
data = null;
xMin = 0;
xMax = -1;
}

public inline function inBounds(x : Int, y : Int, z : Int) : Bool {
return x >= xMin && x <= xMax && y >= yMin && y <= yMax && z >= zMin && z <= zMax;
}

/**
* Precondition: (`x`, `y`, `z`) is in bounds.
*/
public inline function get(x : Int, y : Int, z : Int) : T {
return data[x - xMin][y - yMin][z - zMin];
}

/**
* Precondition: (`x`, `y`, `z`) is in bounds.
*/
public inline function set(x : Int, y : Int, z : Int, value : T) : T {
return data[x - xMin][y - yMin][z - zMin] = value;
}

inline function get_empty() : Bool {
return data == null;
}
}
33 changes: 33 additions & 0 deletions Sources/libnoise/ModuleBase.hx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,39 @@ class ModuleBase {
return 0;
}

/**
* Caches values within the given region of space. Certain classes will use this to improve performance
* when you call `getValue()` within that region.
*
* Only one region can be cached at a time. Trying to cache another will overwrite the first. To avoid
* this, create a new module instance if `isCacheEmpty()` returns false.
*/
public function cache(xMin : Float, yMin : Float, zMin : Float, xMax : Float, yMax : Float, zMax : Float) : Void {
if (modules != null) {
for (module in modules) {
module.cache(xMin, yMin, zMin, xMax, yMax, zMax);
}
}
}

public function isCacheEmpty() : Bool {
if (modules != null) {
for (module in modules) {
if (!module.isCacheEmpty()) {
return false;
}
}
}

return true;
}

public function clearCache() : Void {
for (module in modules) {
module.clearCache();
}
}

//TODO: see if dispose functions need to be implemented (cf https://github.com/ricardojmendez/LibNoise.Unity/blob/master/ModuleBase.cs#L158)

}
138 changes: 104 additions & 34 deletions Sources/libnoise/generator/Voronoi.hx
Original file line number Diff line number Diff line change
@@ -1,61 +1,131 @@
package libnoise.generator;

import libnoise.Cache;

/**
* Provides a noise module that outputs Voronoi cells. [GENERATOR]
*/
class Voronoi extends ModuleBase {

var sites : Cache<Site>;
var frequency : Float;
var displacement : Float;
var seed : Int;
var distance : Bool;
var useDistance : Bool;

public function new(frequency : Float, displacement : Float, seed : Int, distance : Bool) {
/**
* @param frequency The frequency of the site points.
* @param displacement The intensity of the output value. Multiplies everything except distance.
* @param seed The random seed.
* @param useDistance Indicates whether the distance from the nearest site is added to the output value.
*/
public function new(frequency : Float, displacement : Float, seed : Int, useDistance : Bool) {
this.frequency = frequency;
this.displacement = displacement;
this.seed = seed;
this.distance = distance;
this.useDistance = useDistance;
sites = new Cache();
super(0);
}

override public function getValue(x : Float, y : Float, z : Float) : Float {
x *= frequency;
y *= frequency;
z *= frequency;
var xi = (x > 0.0 ? Std.int(x) : Std.int(x - 1));
var iy = (y > 0.0 ? Std.int(y) : Std.int(y - 1));
var iz = (z > 0.0 ? Std.int(z) : Std.int(z - 1));
var md = 2147483647.0;
var xc : Float = 0;
var yc : Float = 0;
var zc : Float = 0;
for (zcu in (iz - 2)...(iz + 3)) {
for (ycu in (iy - 2)...(iy + 3)) {
for (xcu in (xi - 2)...(xi + 3)) {
var xp = xcu + Utils.ValueNoise3D(xcu, ycu, zcu, seed);
var yp = ycu + Utils.ValueNoise3D(xcu, ycu, zcu, seed + 1);
var zp = zcu + Utils.ValueNoise3D(xcu, ycu, zcu, seed + 2);
var xd = xp - x;
var yd = yp - y;
var zd = zp - z;
var d = xd * xd + yd * yd + zd * zd;
if (d < md) {
md = d;
xc = xp;
yc = yp;
zc = zp;

var xInt = Math.floor(x);
var yInt = Math.floor(y);
var zInt = Math.floor(z);

var minDist = Math.POSITIVE_INFINITY;
var xCandidate : Float = 0;
var yCandidate : Float = 0;
var zCandidate : Float = 0;

// Inside each unit cube, there is a site at a random position. Go
// through each of the nearby cubes until we find a cube with a site
// that is closest to (x, y, z).
for (zCur in (zInt - 2)...(zInt + 3)) {
for (yCur in (yInt - 2)...(yInt + 3)) {
for (xCur in (xInt - 2)...(xInt + 3)) {
// Calculate the position and distance to the site inside of
// this unit cube.
var siteX : Float;
var siteY : Float;
var siteZ : Float;
if (sites.inBounds(xCur, yCur, zCur)) {
var site : Site = sites.get(xCur, yCur, zCur);
siteX = site.x;
siteY = site.y;
siteZ = site.z;
} else {
siteX = xCur + Utils.ValueNoise3D(xCur, yCur, zCur, seed);
siteY = yCur + Utils.ValueNoise3D(xCur, yCur, zCur, seed + 1);
siteZ = zCur + Utils.ValueNoise3D(xCur, yCur, zCur, seed + 2);
}

var dist = (siteX - x) * (siteX - x) + (siteY - y) * (siteY - y) + (siteZ - z) * (siteZ - z);
if (dist < minDist) {
// This site is closer than any others found so far.
minDist = dist;
xCandidate = siteX;
yCandidate = siteY;
zCandidate = siteZ;
}
}
}
}
var v : Float;
if (distance) {
var xd = xc - x;
var yd = yc - y;
var zd = zc - z;
v = (Math.sqrt(xd * xd + yd * yd + zd * zd)) * Utils.SQRT3 - 1.0;

var value : Float;
if (useDistance) {
// Determine the distance to the nearest site.
value = Math.sqrt(minDist) * Utils.SQRT3 - 1.0;
}
else {
v = 0.0;
value = 0.0;
}
return v + (displacement * Utils.ValueNoise3D(Std.int(Math.floor(xc)), Std.int(Math.floor(yc)),
Std.int(Math.floor(zc)), 0));
return value + (displacement * Utils.ValueNoise3D(Std.int(Math.floor(xCandidate)), Std.int(Math.floor(yCandidate)),
Std.int(Math.floor(zCandidate)), 0));
}

@:inheritDoc
public override function cache(xMin : Float, yMin : Float, zMin : Float, xMax : Float, yMax : Float, zMax : Float) {
sites.allocate(
Math.floor(xMin * frequency) - 2,
Math.floor(yMin * frequency) - 2,
Math.floor(zMin * frequency) - 2,
Math.floor(xMax * frequency) + 2,
Math.floor(yMax * frequency) + 2,
Math.floor(zMax * frequency) + 2);

for(z in sites.zMin...(sites.zMax + 1)) {
for(y in sites.yMin...(sites.yMax + 1)) {
for(x in sites.xMin...(sites.xMax + 1)) {
sites.set(x, y, z, new Site(x + Utils.ValueNoise3D(x, y, z, seed),
y + Utils.ValueNoise3D(x, y, z, seed + 1),
z + Utils.ValueNoise3D(z, y, z, seed + 2)));
}
}
}
}

@:inheritDoc
public override function clearCache() : Void {
sites.clear();
}
}

/**
* The central point defining a Voronoi cell. Also known as a seed or a generator.
*/
class Site {
public var x : Float;
public var y : Float;
public var z : Float;

public inline function new(x : Float, y : Float, z : Float) {
this.x = x;
this.y = y;
this.z = z;
}
}