Terrain-feature map.
--------
Used to quickly and easily decorate an entire terrain with organic (tree and rock, etc) mapobjects, combined with the heightmap of the terrain, by painting a very simple image in something like MS-Paint. It is a crude procedural generation technique for quick and easy terrain decoration, even for very large environments.
--------
(category-hues)
R1 Y1 G1 C1 B1 M1
R2 Y2 G2 C2 B2 M2
R3 Y3 G3 C3 B3 M3
R4 Y4 G4 C4 B4 M4
R5 Y5 G5 C5 B5 M5
R6 Y6 G6 C6 B6 M6
(1-6 for variations)
--------
(brightness-size)(36)
255 255 255 255 255 255
220 220 220 220 220 220
180 180 180 180 180 180
140 140 140 140 140 140
100 100 100 100 100 100
060 060 060 060 060 060
000 (=6x6x6)
--------
...
216 total unique items can be placed by category, variation and size. The image is decoded by hue for category and brightness for size, and variation is done randomly in the scripting. White and greys in the image will be interpreted as red hue (category) and black is for empty. The 6 categories, or hues, can be determined by the mapper and entire tiered presets will exist to flesh out a very basic terrain decoration layout, although simple, the potential is there for very curated and crafted environments if the time is taken to create such an image. For the 6 hues, a further 6 related variations exist, and this applies to every brightness-threshold down to black. This essentially means that any cat meme can be used and you'll get some kind of appropriate result. Both presets and content for presets should evolve over time, as more and more mappers do this. The random variations exist not only for the hues but every brightness-size, and random z-axis rotations are also applied as well as slight offsets on the other axes. That means no matter what you do, the results should be seemingly very random and slightly untidy, thus more organic and believable.
The image itself is usually the same resolution as the heightmap, which is 64x64 units for 1 pixel. In practical terms, it translates to 8192x8192 for about 1.5 square kilometres, although this value is an estimate of a rough standard and can vary, you generally won't need to have a tighter area for a single pixel to represent than a square metre, then an 8K image is plenty for what will already be a large terrain. Having larger terrains is very possible, but I recommend splitting them into geographical segments.
There are 2 parts involved in creating these kind of "terrain-feature" maps out of images. The first is the painting or input, and the second is the processing of different maps together, especially the heightmap, to further refine the end result. It's all processed in such a way that any part can be manually edited at any time and there are a lot of easily configurable parameters that is read from the "master script".
First, you already have everything you need. MS-Paint is definitely enough and I even recommend staying with a very simplistic tool and not to overthink the image as an artwork unto itself. The image is purely information. Category-hues can be fully customised, but for the sake of example we'll say green is forest and red is rocks. So logically, a slash of very bright green with no use of brushes or fading will result in a sudden thick dense forest. Using a fade and/or brushes to soften all of the edges and painting a stroke of green will result in a gradual build-up of forest trees, with surrounding plants, weeds and grasses, as the darker green hues result in the lesser vegetation. All of these can also be created and "grown" by how the image is processed.
Finally, these images are broken up into their different hues and filters are applied to use noise, checkerboarding, and natural "growth" (see "growsum") (which is how any given hue is naturally decorated by itself outward procedurally), as well as being filtered by the heightmap to exclude depths and heights, as well as angles which are derived from a normalmap version of the terrain. Various further randomisations can be applied and everything is controlled by a master script to make it easy for mappers to have complete control. A very random result can still be achieved from even the most rudimentary inputs.
The end result is a map file that can be loaded into Radiant to place each mapobject as its own unique entity, for every pixel of the terrain-feature image. The elevation is automatically matched to the heightmap and should be an exact match to the terrain mesh itself, also in Radiant and perhaps even derived from Radiant itself. Each mapobject is then still its own entity that can be further moved or deleted, so think of this tool as a way to get terrain-features into Radiant rather than into the engine. The complete freedom to map in Radiant, create the terrain first, and shoehorn things in, or create a heightmap around an existing design, or a bit of both or anything - this tool can be used in any way, at any time.
The "master script" (concept) used to control how everything is processed:-
//======
hues
Red=cat1
Yellow=cat2
Green=cat3
Cyan=cat4
Blue=cat5
Magenta=cat6
categories
Cat1=rocks03
Cat2=desert02
Cat3=pine01
Cat4=birch01
Car5=rocks02
Cat6=bush02
rotate-vars //how much extra rotation variance should be added.
Rot1=100
Rot2=100
Rot3=100
Rot4=100
Rot5=100
Rot6=100
offset-vars //how much to offset the XY position of the mapobject in 3D units in a random direction.
Shft1=30
=30
Shft3=30
Shft4=30
Shft5=30
Shft6=30
tilt-vars //how much to tilt the object with a final rotation on the X or Y axis.
Tilt1=10
Tilt2=10
Tilt3=10
Tilt4=10
Tilt5=10
Tilt6=10
processsing
Pixels=8192 // squared value of image size, must be the same with both proportions.
Priority=1 // which colour gets priority in the output. Covers 6 permutations for each colour with default spectrum.
// RYGCBM / MRYGCB / BMRYGC / CBMRYG / GCBMRY / YGCBMR
//(=First to last colour in order of prominence, as used to isolate each following colour through the spectrum.)
//(=Used in combination with hue-category assignments to fine-tune to balance persistency of asset decorations.)
Growsum=6 // how many pixels to dilate and compound. This controls automated surrounding decorations.
Growcurve=6 // 0-10 how much more dilated the darker compounds are, resulting in smaller automated decorations.
Growshape=0 // 0 or 1, circle or diamond, defines the method of the dilation.
Noiseinner=3 // 0-10 to apply noise to all pixels above 10 brightness, which can give more randomisation.
Noiseouter=3 // 0-10 to apply noise to all pixels below 10 brightness, to populate areas devoid of detail.
Dimtex=2 // 0-3 which texture to use to diminish, 0 being coarse and 3 being fine.
Dimdark=2 // 0-10 strength to darken patches using a diminish texture, for randomisation.
Checkboard=3 // 0-10 for how much to darken all alternating pixels. Can reduce overcrowding.
OffsetP=1 // how many pixels to offset using a checkerboard pattern, to offset every other pixel, for randomisation.
OffsetD=1 // which direction to offset, N=1, E=2, S=3, W=4.
Heightblur=10 // how much to blur the high and low filters, resulting in smaller decorations being more persistent.
Heightlow=10 // lowest heightmap brightness to filter terrain-features.
Heighthigh=230 // highest heightmap brightness to filter terrain-features.
Heightangle=45 // attempts to predict terrain angle by deriving a filter from normalmap saturation values.
//======
...
Hues just define their categories. This could be condensed to the same parameter, but I wanted it easy to switch categories without needing to edit images. Categories reference prefixes in the mapobject filenames. In the default example, cat1 is pine01, which then uses pine01_6a, pine01_6b, pine01_5a, pine01_5b, etc. Mapobject pine01_6 is matched to the brightest pixel and therefore the biggest and tallest tree. Mapobject pine01_1 is the smallest, matched to the darkest pixel and therefore most likely a tiny shrub or a few weeds, and these are more likely to be shared among different presets used by other categories. Essentially, "pine" is actually a hierarchy of mapobjects that all relate to the theme of 'mountain alpine', and use everything that would be surrounding a pine tree. This means that even placing 1 green pixel thus 1 tree will still result in surrounding flora. Many of these mapobjects are shared among different presets, so pine01 is a preset, pine02 may be a variation of that, and further variations are created by mixing all assets used, in all different permutations. Essentially, the more presets that can exist, the more randomisation that can exist among all other presets automatically. The rest is just all a matter of providing the properly named assets for the appropriate decoration.
.
.
.
====
Terrain-features need only 2 images to process - featuremap (like a colourmap) and the heightmap.
The 'master script' controls all values referenced by the processing scripts, which can also be edited.
...
"Pixels" is used to resample the image to the squared value, incase the input image is different.
Featuremap hues split using a channel separation, followed by a full hue shift with another channel separation. (all 6 hues)
Refined by subtracting all from one another to balance persistence. (order changed by "Priority" in master-script).
hue1 - red / hue2 - yellow / hue3 - green / hue4 - cyan / hue5 - blue / hue6 - magenta
Red is subtracted with green and blue. Yellow is subtracted with cyan and magenta. Green with blue and red. Etc.
In this default order, green has the most persistence. Master-script order value to change this persistence.
1 - green / 2 - cyan / 3 - blue / 4 - magenta / 5 - red / 6 - yellow
Entire image has luminance lowered and uses "Growcurve" to control this, resulting in 3 versions each.
"Growsum" is used to determine dilation, with that dilation doubling and tripling for darker versions.
"Growshape" is used to dilate these darker versions even more.
All darker images are combined back into a compiled version using a 'lightest' arithmetic.
(essentially works like a bloom process...)
"Dimtex" is used to darken all of the images with a cloudy texture, with "Dimdark" controlling the amount.
Noise is applied to the (inner) positive (above 1) colours of each hue image, by value "Noisefill".
Noise is applied to the (outer) empty (below 1) colour of each hue image, by value "Outernoise".
A checkerboard is used to create 2 versions of each alternative. "Checkboard" is used to darken the second version.
"OffsetP" and "OffsetD" are used to recombine these versions, with a distance in pixels and a direction.
"Heightlow" & "Heighthigh" create a mask to remove elevations from hue images. "Heightblur" blurs this heightmap mask.
A normalmap version of the heightmap is generated, determines angle by a saturation value "Heightangle".
This removes flat lavender from the normalmap by variance, creates a mask to combine with final heightmap trim mask.
The trim mask is used to remove features from all hue-images.
A final subtraction is done so that no image shares the same pixel with another image, using "Priority" again.
...
Each hue-image is now ready to be compiled into a map file of mapobjects using the PPM format.
Coords of pixels determine "origin" of that pixel as translated to the map file.
Assets are determined by brightness of the pixel coord, with each hue already assigned to each category.
Randomisation of each asset is done by GAWK cycling the filename number throughout the output.
Rotation of z-axis is done with "Rot" master-script value, affecting (Z) "rotation" key of mapobject.
Nudging of objects uses "Shft" value, with a random vector to affect the "origin" key of mapobject.
Tilt of objects uses "Tilt" value, affecting (X&Y together) "rotation" key of mapobject.
Commandline conversion of euler angles to matrix is done enmasse before compiling segments using concatenation.
Finally, elevation information is derived from the heightmap to adjust "origin" key of mapobject.
Map file is now ready to load into Radiant...
.
.
.
.
.
.