|
local max_octaves = 20 |
|
local octave_scale = 1024 |
|
local noise_scale = 1024 |
|
local ratio_scale = 1000 |
|
|
|
DefineClass.PerlinNoiseBase = |
|
{ |
|
__parents = { "PropertyObject" }, |
|
properties = { |
|
{ category = "Noise", id = "Frequency", name = "Frequency (%)", editor = "number", default = false, min = 0, max = 100, slider = true, help = "A tool for changing the main noise frequency. Depends on the number of octaves and the chosen persistence" }, |
|
{ category = "Noise", id = "Persistence", name = "Persistence", editor = "number", default = 50, min = 1, max = 99, slider = true, help = "Defines the behavior of the noise octaves when changing the noise frequency" }, |
|
{ category = "Noise", id = "Octaves", name = "Octaves Count", editor = "number", default = 9, min = 1, max = max_octaves, help = "Number of octaves to use" }, |
|
{ category = "Noise", id = "OctavesList", name = "Octaves", editor = "text", default = "", dont_save = true, help = "Used to copy or paste a set octaves" }, |
|
{ category = "Noise", id = "BestSize", name = "Best Size", editor = "number", default = 0, read_only = true, dont_save = true, help = "Recomended noise grid size" }, |
|
}, |
|
octave_ids = {}, |
|
} |
|
|
|
function ExpandPerlinParams(count, persistence, main, amp) |
|
count = count or -1 |
|
persistence = persistence or 50 |
|
main = main or 1 |
|
|
|
if count == 0 then return "" end |
|
local octaves = {} |
|
amp = amp or octave_scale |
|
octaves[main] = amp |
|
local i = 0 |
|
while i ~= count do |
|
i = i + 1 |
|
local new_amp = amp * persistence / 100 |
|
if new_amp == amp and count <= 0 then |
|
break |
|
end |
|
amp = new_amp |
|
local left = main - i |
|
local right = main + i |
|
if count > 0 and left < 1 and right > count then |
|
break |
|
end |
|
if left >= 1 then |
|
octaves[left] = amp |
|
end |
|
if count < 0 or right <= count then |
|
octaves[right] = amp |
|
end |
|
end |
|
return octaves |
|
end |
|
|
|
do |
|
local params = ExpandPerlinParams(max_octaves, 50) |
|
local octave_ids = PerlinNoiseBase.octave_ids |
|
for i=1,max_octaves do |
|
local id = "Octave_"..i |
|
octave_ids[i] = id |
|
octave_ids[id] = i |
|
table.insert(PerlinNoiseBase.properties, { |
|
id = id, |
|
name = "Octave "..i, |
|
editor = "number", |
|
default = params[i] or 0, |
|
category = "Noise", |
|
min = 0, max = octave_scale, slider = true, |
|
no_edit = function(self) return self.Octaves < i end, |
|
}) |
|
end |
|
end |
|
|
|
function PerlinNoiseBase:GetOctavesList() |
|
return table.concat(self:ExportOctaves(), ', ') |
|
end |
|
|
|
function PerlinNoiseBase:SetOctavesList(list) |
|
local octaves = dostring("return {" .. list .. "}") |
|
if octaves then |
|
return self:ImportOctaves(octaves) |
|
end |
|
end |
|
|
|
function PerlinNoiseBase:GetBestSize() |
|
return 2 ^ self.Octaves |
|
end |
|
|
|
function PerlinNoiseBase:OnEditorSetProperty(prop_id, old_value, ged) |
|
if prop_id == "Frequency" or prop_id == "Persistence" or prop_id == "Octaves" then |
|
if self.Frequency then |
|
local mo = 1 + MulDivRound(self.Octaves - 1, self.Frequency, 100) |
|
self:SetMainOctave(mo) |
|
end |
|
elseif self.octave_ids[prop_id] then |
|
self.Frequency = nil |
|
end |
|
end |
|
|
|
function PerlinNoiseBase:SetMainOctave(mo) |
|
if not mo then |
|
return |
|
end |
|
local params = ExpandPerlinParams(self.Octaves, self.Persistence, mo) |
|
self:ImportOctaves(params) |
|
end |
|
|
|
function PerlinNoiseBase:ExportOctaves() |
|
local octaves = {} |
|
local octave_ids = self.octave_ids |
|
for i=1,self.Octaves do |
|
octaves[#octaves + 1] = self[octave_ids[i]] |
|
end |
|
for i=self.Octaves,1,-1 do |
|
if octaves[i] ~= 0 then |
|
break |
|
end |
|
octaves[i] = nil |
|
end |
|
return octaves |
|
end |
|
|
|
function PerlinNoiseBase:ImportOctaves(octaves) |
|
self.Octaves = #octaves |
|
local params = {} |
|
local octave_ids = self.octave_ids |
|
for i=1,max_octaves do |
|
self[octave_ids[i]] = octaves[i] |
|
end |
|
end |
|
|
|
function PerlinNoiseBase:GetNoiseRaw(rand_seed, g, ...) |
|
rand_seed = self.Seed + (rand_seed or 0) |
|
GridPerlin(rand_seed, self:ExportOctaves(), g, ...) |
|
return g, ... |
|
end |
|
|
|
|
|
|
|
DefineClass.PerlinNoise = |
|
{ |
|
__parents = { "PerlinNoiseBase" }, |
|
properties = { |
|
{ id = "Seed", name = "Random Seed", editor = "number", default = 0, category = "Noise", buttons = {{name = "Rand", func = "ActionRand"}}, help = "Fixed randomization seed"}, |
|
{ id = "Size", name = "Grid Size", editor = "number", default = 256, category = "Noise", min = 2, max = 2048, help = "Size of the noise grid" }, |
|
{ id = "Min", name = "Min Value", editor = "number", default = 0, category = "Noise", }, |
|
{ id = "Max", name = "Max Value", editor = "number", default = noise_scale, category = "Noise", }, |
|
{ id = "Preview", editor = "grid", default = false, category = "Noise", dont_save = true, interpolation = "nearest", frame = 1, min = 128, max = 512, read_only = true, no_validate = true }, |
|
{ id = "Clamp", name = "Clamp Range (%)", editor = "range", default = range(0, 100), category = "Post Process", min = 0, max = 100, slider = true, help = "Clamp the noise in that range and re-normalize afterwards" }, |
|
{ id = "Sin", name = "Sin Unity (%)", editor = "number", default = 0, category = "Post Process", min = 0, max = 100, slider = true, help = "Applies sinusoidal easing. Useful to smooth noise after clamping" }, |
|
{ id = "Mask", name = "Mask Area (%)", editor = "number", default = 0, category = "Post Process", min = 0, max = 100, slider = true, help = "Creates a mask with that percentage of area" }, |
|
}, |
|
} |
|
|
|
function PerlinNoise:OnNoiseChanged() |
|
end |
|
|
|
function PerlinNoise:GetPreview() |
|
return self:GetNoise() |
|
end |
|
|
|
function GetNoisePreview(noise_name) |
|
local noise_preset = NoisePresets[noise_name] |
|
return noise_preset and noise_preset:GetPreview() |
|
end |
|
|
|
function PerlinNoise:GetNoise(rand_seed, g, ...) |
|
g = g or NewComputeGrid(self.Size, self.Size, "F") |
|
return self:PostProcess(self:GetNoiseRaw(rand_seed, g, ...)) |
|
end |
|
|
|
function PerlinNoise:PostProcess(g, ...) |
|
if not g then |
|
return |
|
end |
|
local min, max = self.Min, self.Max |
|
local smin, smax = min * ratio_scale, max * ratio_scale |
|
local function pct(v) |
|
return smin + MulDivRound(smax - smin, v, 100) |
|
end |
|
|
|
GridNormalize(g, min, max) |
|
if self.Clamp.from > 0 or self.Clamp.to < 100 then |
|
local from = pct(self.Clamp.from) |
|
local to = pct(self.Clamp.to) |
|
GridClamp(g, from, to, ratio_scale) |
|
GridRemap(g, from, to, smin, smax, ratio_scale) |
|
end |
|
if self.Sin ~= 0 then |
|
local unity = pct(self.Sin) |
|
if unity > smin then |
|
GridSin(g, smin, unity, ratio_scale) |
|
GridRemap(g, -1, 1, min, max) |
|
end |
|
end |
|
if self.Mask ~= 0 then |
|
local level = GridLevel(g, self.Mask, 100, ratio_scale) |
|
GridMask(g, 0, level, ratio_scale) |
|
GridRemap(g, 0, 1, min, max) |
|
end |
|
|
|
return g, self:PostProcess(...) |
|
end |
|
|
|
function PerlinNoise:ActionRand(root, prop_id, ged) |
|
self.Seed = AsyncRand() |
|
ObjModified(self) |
|
end |
|
|
|
function NoisePresetsCombo() |
|
local items = table.values(NoisePresets) |
|
items = table.map(items, "id") |
|
table.sort(items) |
|
table.insert(items, 1, "") |
|
return items |
|
end |
|
|
|
DefineClass.WangPerlinNoise = { |
|
__parents = {"PerlinNoise"}, |
|
|
|
properties = { |
|
{id = "unique_edges", editor = "number", default = 2,}, |
|
{id = "tiles", editor = "point", default = point(4,4), read_only = true,}, |
|
{id = "cells_per_tile", editor = "point", default = point(4,4), }, |
|
} |
|
} |
|
|
|
function WangPerlinNoise:Gettiles() |
|
local size = self.unique_edges ^ 2 |
|
return point(size, size) |
|
end |
|
|
|
function WangPerlinNoise:GetNoiseRaw(rand_seed, g, ...) |
|
rand_seed = self.Seed + (rand_seed or 0) |
|
local n = GetPreciseTicks() |
|
GridWang(rand_seed, {octaves = self:ExportOctaves(), tiles = self:Gettiles(), cells_per_tile = self.cells_per_tile}, g) |
|
|
|
return g, ... |
|
end |
|
|