|
if const.BiomeTileSize then |
|
DefineMapGrid("BiomeGrid", 16, const.BiomeTileSize, 64, config.EditableBiomeGrid and "save_in_map") |
|
end |
|
|
|
local max_value = 254 |
|
local height_scale = const.TerrainHeightScale |
|
local height_max = const.MaxTerrainHeight |
|
local type_tile = const.TypeTileSize |
|
local wd_max = const.RandomMap.BiomeMaxWaterDist |
|
local h_max, h_scale = height_max/height_scale, guim/height_scale |
|
local min_sl, max_sl = const.RandomMap.BiomeMinSeaLevel/height_scale, const.RandomMap.BiomeMaxSeaLevel/height_scale |
|
assert(guim % height_scale == 0) |
|
|
|
BiomeMatchParams = { |
|
{ id = "Height", name = "Height", units = "(m)", min = 0, max = h_max, default = 0, scale = h_scale, help = "Absolute height value on the map" }, |
|
{ id = "Slope", name = "Slope", units = "(deg)", min = 0, max = 90*60, default = 0, scale = 60, help = "Slope angle: 0 flat, 90 vertical" }, |
|
{ id = "Wet", name = "Humidity", units = "(%)", min = 0, max = 100, default = 50, help = "Humidity % derived from erosion intensity" }, |
|
{ id = "Hardness", name = "Soil Hardness", units = "(%)", min = 0, max = 100, default = 0, scale = 1, help = "Defines how much rigid is the soil agains erosion: 100% is solid rock without any erosion" }, |
|
{ id = "Orient", name = "Orientation", min = 0, max = 1000, default = 0, scale = 1000, help = "Slope orientation towards the sun: 0 Shadow, 1 Sunlit" }, |
|
{ id = "SeaLevel", name = "Sea Level", units = "(m)", min = min_sl, max = max_sl, default = max_sl, scale = h_scale, help = "Height above or below Sea Level set it MapData" }, |
|
{ id = "WaterDist", name = "Water Dist", units = "(m)", min = -wd_max, max = wd_max, default = wd_max, scale = guim, help = "Distance in (-) or out (+) the water border line" }, |
|
} |
|
|
|
function BiomeWaterDist(water_grid) |
|
if not water_grid then return end |
|
local dist_out = GridDest(water_grid) |
|
if GridIsFlat(water_grid) then |
|
local value = GridGet(water_grid, 0, 0) |
|
dist_out:clear(value == 0 and wd_max or -wd_max) |
|
return dist_out |
|
end |
|
GridInvert(water_grid) |
|
GridDistance(water_grid, dist_out, type_tile, wd_max) |
|
GridInvert(water_grid) |
|
local dist_in = GridDest(water_grid) |
|
GridDistance(water_grid, dist_in, type_tile, wd_max) |
|
local dist = GridAddMulDiv(dist_out, dist_in, -1) |
|
return dist |
|
end |
|
|
|
function BiomeMatchItems() |
|
local items = {} |
|
for _, param in ipairs(BiomeMatchParams) do |
|
items[#items + 1] = { value = param.id , text = param.name } |
|
end |
|
return items |
|
end |
|
|
|
DefineClass.Biome = { |
|
__parents = { "Preset", }, |
|
properties = { |
|
{ category = "Biome", id = "grid_value", name = "Grid Value", editor = "number", default = 0, min = 0, max = max_value, read_only = true, help = "Value stored in the Biome grid" }, |
|
{ category = "Biome", id = "palette_color", name = "Palette Color", editor = "color", default = -16777216, }, |
|
|
|
{ category = "Prefabs", id = "PrefabTypeWeights", name = "Prefab Types", editor = "nested_list", default = false, base_class = "BiomePrefabTypeWeight", inclusive = true }, |
|
{ category = "Prefabs", id = "FilteredPrefabsPreview", name = "Filtered Prefabs", editor = "number", default = 0, dont_save = true, read_only = true, }, |
|
{ category = "Prefabs", id = "TypeMixingPreset", name = "Mixing Pattern", editor = "preset_id", default = "", preset_class = "NoisePreset", }, |
|
{ category = "Prefabs", id = "TypeMixingPreview", name = "Mixing Preview", editor = "grid", default = false, no_edit = function(self) return self.TypeMixingPreset == "" end, frame = 1, min = 512, dont_save = true, read_only = true }, |
|
|
|
{ category = "Matching", id = "CompareParam", name = "Compare Param", editor = "set", default = empty_table, items = BiomeMatchItems, max_items_in_set = 1, dont_save = true }, |
|
}, |
|
EditorMenubarName = "Biomes", |
|
EditorMenubar = "Map.Generate", |
|
EditorIcon = "CommonAssets/UI/Icons/biology plants seed.png", |
|
EditorView = Untranslated("<id> <color 0 128 0><grid_value></color>"), |
|
StoreAsTable = false, |
|
} |
|
|
|
for _, match in ipairs(BiomeMatchParams) do |
|
local maxw = 200 |
|
local id, name, units, min, max, scale, help = match.id, match.name, match.units or "", match.min, match.max, match.scale, match.help |
|
local function no_edit(self) |
|
local cmp_id = self:GetCompareId() |
|
return cmp_id and cmp_id ~= id |
|
end |
|
table.iappend(Biome.properties, { |
|
{ category = "Matching", id = id .. "From", name = name .. " From " .. units, editor = "number", default = false, min = min, max = max, scale = scale, no_edit = no_edit, slider = true, recalc_curve = id, help = help }, |
|
{ category = "Matching", id = id .. "Best", name = name .. " Best " .. units, editor = "number", default = false, min = min, max = max, scale = scale, no_edit = no_edit, slider = true, recalc_curve = id, help = help }, |
|
{ category = "Matching", id = id .. "To", name = name .. " To " .. units, editor = "number", default = false, min = min, max = max, scale = scale, no_edit = no_edit, slider = true, recalc_curve = id, help = help }, |
|
{ category = "Matching", id = id .. "Weight", name = name .. " Weight", editor = "number", default = 100, min = 0, max = maxw, scale = 100, no_edit = no_edit, slider = true, recalc_curve = id }, |
|
{ category = "Matching", id = id .. "Curve", name = name .. " Curve", editor = "grid", default = false, dont_save = true, read_only = true, no_edit = no_edit, dont_normalize = true, frame = 1, help = help }, |
|
}) |
|
Biome["Get" .. id .. "Curve"] = function(self) |
|
return self[id .. "Curve"] or self["CalcCurve" .. id](self) |
|
end |
|
Biome["CalcCurve" .. id] = function(self, notify) |
|
local grid = self[id .. "Curve"] |
|
local w, h = 256, 64 |
|
if not grid then |
|
grid = NewComputeGrid(w, h, "U", 8) |
|
self[id .. "Curve"] = grid |
|
end |
|
grid:clear() |
|
local weight, x_from, x_best, x_to = self[id .. "Weight"], self[id .. "From"], self[id .. "Best"], self[id .. "To"] |
|
local x0, x1 = x_from or min, x_to or max |
|
if not x_best or x_best >= x0 and x_best <= x1 then |
|
for gx = 0, w-1 do |
|
local x = min + MulDivRound(max - min, gx, w - 1) |
|
if x >= x0 and x <= x1 then |
|
local wi = weight |
|
if not x_best then |
|
|
|
elseif x_from and x >= x0 and x < x_best then |
|
wi = MulDivRound(weight, x - x0, x_best - x0) |
|
elseif x_to and x <= x1 and x > x_best then |
|
wi = MulDivRound(weight, x1 - x, x1 - x_best) |
|
end |
|
local gy = MulDivRound(h - 1, maxw - wi, maxw) |
|
GridDrawColumn(grid, gx, gy, 255, 128) |
|
end |
|
end |
|
end |
|
if notify then |
|
ObjModified(self) |
|
end |
|
return grid |
|
end |
|
end |
|
|
|
AppendClass.MapDataPreset = { properties = { |
|
{ category = "Random Map", id = "BiomeGroup", editor = "choice", default = "", items = PresetGroupsCombo("Biome") }, |
|
{ category = "Random Map", id = "HeightMin", editor = "number", default = 10 * guim, scale = "m", min = 0, max = height_max, slider = true, help = "Value corresponding to black grayscale level" }, |
|
{ category = "Random Map", id = "HeightMax", editor = "number", default = height_max - 10 * guim, scale = "m", min = 0, max = height_max, slider = true, help = "Value corresponding to white grayscale level" }, |
|
{ category = "Random Map", id = "WetMin", editor = "number", default = 0, scale = "%", min = 0, max = 100, slider = true, help = "Value corresponding to black grayscale level" }, |
|
{ category = "Random Map", id = "WetMax", editor = "number", default = 100, scale = "%", min = 0, max = 100, slider = true, help = "Value corresponding to white grayscale level" }, |
|
{ category = "Random Map", id = "SeaLevel", editor = "number", default = 0, scale = "m", min = 0, max = height_max, slider = true }, |
|
{ category = "Random Map", id = "SeaPreset", editor = "preset_id", default = false, preset_class = "WaterObjPreset" }, |
|
{ category = "Random Map", id = "SeaMinDist", editor = "number", default = 32*guim, scale = "m", min = 0 }, |
|
{ category = "Random Map", id = "MinBumpSlope", editor = "number", default = 10*60, scale = "deg", min = 0, max = 90*60, slider = true }, |
|
{ category = "Random Map", id = "MaxBumpSlope", editor = "number", default = 40*60, scale = "deg", min = 0, max = 90*60, slider = true }, |
|
}} |
|
|
|
function Biome:GetCompareId() |
|
return next(self.CompareParam) |
|
end |
|
|
|
function Biome:GetProperties() |
|
local compare_id = self:GetCompareId() |
|
if not compare_id then |
|
return self.properties |
|
end |
|
local props = table.icopy(self.properties) |
|
ForEachPreset("Biome", function(preset) |
|
if self.id ~= preset.id and self.group == preset.group then |
|
local id = preset.id .. "_Compare" |
|
props[#props + 1] = { category = "Compare", id = id, name = preset.id, editor = "grid", default = false, dont_save = true, read_only = true, dont_normalize = true, frame = 1 }, |
|
rawset(self, "Get" .. id, function() |
|
local getter = preset["Get" .. compare_id .. "Curve"] |
|
return getter and getter(preset) |
|
end) |
|
end |
|
end) |
|
return props |
|
end |
|
|
|
function Biome:GetFilteredPrefabs() |
|
local types = table.map(self.PrefabTypeWeights or empty_table, "PrefabType") |
|
types = table.invert(types) |
|
|
|
local result = {} |
|
for i,prefab in ipairs(PrefabMarkers) do |
|
if types[prefab.type] then |
|
table.insert(result, prefab.name) |
|
end |
|
end |
|
return result |
|
end |
|
|
|
function Biome:GetTypeMixingGrid(result, rand_seed, ptype_to_idx) |
|
local preset = NoisePresets[self.TypeMixingPreset] |
|
local weights = self.PrefabTypeWeights or empty_table |
|
if not preset or #weights < 2 then |
|
return false |
|
end |
|
|
|
local noise = GridDest(result) |
|
rand_seed = rand_seed and BraidRandom(rand_seed) or 0 |
|
preset:GetNoise(rand_seed, noise) |
|
local weights_sum = 0 |
|
for i=1,#weights do |
|
weights_sum = weights_sum + weights[i].Weight |
|
end |
|
local marks = 0 |
|
local levels = GridLevels(noise) |
|
local histogram = {} |
|
for level, count in sorted_pairs(levels) do |
|
histogram[#histogram + 1] = {count, level} |
|
end |
|
local w, h = noise:size() |
|
local total_area = w * h |
|
local mask = GridDest(noise) |
|
local prev_level = -1 |
|
local function Mark(level) |
|
marks = marks + 1 |
|
local idx = not ptype_to_idx and marks or ptype_to_idx[weights[marks].PrefabType] or 0 |
|
GridMask(noise, mask, prev_level + 1, level) |
|
prev_level = level |
|
GridPaint(result, mask, idx) |
|
end |
|
|
|
local idx, area, weight = 1, 0, 0 |
|
for i=1,#weights-1 do |
|
weight = weight + weights[i].Weight |
|
local target_area = MulDivRound(total_area, weight, weights_sum) |
|
while idx <= #histogram do |
|
local entry = histogram[idx] |
|
area = area + entry[1] |
|
idx = idx + 1 |
|
if area >= target_area then |
|
Mark(entry[2]) |
|
break |
|
end |
|
end |
|
end |
|
Mark(max_int) |
|
return result |
|
end |
|
|
|
function Biome:__paste(...) |
|
local res = Preset.__paste(self, ...) |
|
res.grid_value = nil |
|
return res |
|
end |
|
|
|
function Biome:PostLoad() |
|
self:AssignValue() |
|
Preset.PostLoad(self) |
|
end |
|
|
|
function Biome:AssignValue() |
|
if self.grid_value > 0 then return end |
|
local value = 0 |
|
ForEachPreset("Biome", function(p) |
|
value = Max(value, p.grid_value) |
|
end) |
|
if value < max_value then |
|
self.grid_value = value + 1 |
|
return |
|
end |
|
local map = BiomeValueToPreset() |
|
for i=1,max_value do |
|
if not map[i] then |
|
self.grid_value = i |
|
return |
|
end |
|
end |
|
assert(false, "No more biome grid values available!") |
|
self.grid_value = -1 |
|
end |
|
|
|
|
|
|
|
DefineClass.BiomePrefabTypeWeight = { |
|
__parents = { "PropertyObject" }, |
|
properties = { |
|
{ id = "PrefabType", name = "Type", editor = "preset_id", default = "", preset_class = "PrefabType" }, |
|
{ id = "Weight", name = "Weight", editor = "number", default = 100, min = 0, max = 100, slider = true }, |
|
}, |
|
EditorView = Untranslated("<PrefabType> (weight: <Weight>)"), |
|
} |
|
|
|
|
|
|
|
function BiomeValueToPreset() |
|
local map = {} |
|
ForEachPreset("Biome", function(preset, group, map) |
|
local value = preset.grid_value |
|
if value <= 0 then |
|
|
|
elseif map[value] then |
|
print("Biome value", value, "collision", map[value].id, "/", preset.group, "-", preset.id) |
|
else |
|
map[value] = preset |
|
end |
|
end, map) |
|
return map |
|
end |
|
|
|
function DbgGetBiomePalette() |
|
local palette = {} |
|
ForEachPreset("Biome", function(preset) |
|
palette[preset.grid_value] = preset.palette_color |
|
end) |
|
palette[255] = RGBA(255, 255, 255, 128) |
|
return palette |
|
end |