I have a basic implementation of this which I use with the output of DiscretizeGraphics
:
sphTerrainGenCore =
Compile[
{
{tcoords, _Real, 2},
{coords, _Real, 2},
{center, _Real, 1},
{perturbation, _Real},
{offset, _Real}
},
MapIndexed[
center +
(coords[[#2[[1]]]] - center)*
(1 + perturbation*If[#[[1]] > center[[1]] + offset, 1, -1]) &,
tcoords
]
];
sphTerrainGenStep[{coords_, cells_, center_}, {normal_, perturbation_,
offset_}] :=
{
sphTerrainGenCore[
RotationTransform[{normal, {1, 0, 0}}, center]@coords,
coords,
center,
perturbation,
offset
],
cells,
center
};
sphTerrainGenStep[
{coords_, cells_, center_},
steps_Integer,
perturbationBounds : {_, _} : {.00001, .001},
offsetBounds : {_, _} : {0, .1}
] :=
Fold[
sphTerrainGenStep[#, #2] &,
{coords, cells, center},
Transpose@{
RandomReal[{-1, 1}, {steps, 3}],
RandomReal[perturbationBounds, steps],
RandomReal[offsetBounds, steps]
}
];
sphTerrainGenStep[{r_?RegionQ, center_},
steps_Integer,
perturbationBounds : {_, _} : {.00001, .001},
offsetBounds : {_, _} : {0, .1}
] :=
With[{ret =
sphTerrainGenStep[{
MeshCoordinates[r],
MeshCells[r, All],
center
},
steps,
perturbationBounds
]
},
{
MeshRegion[ret[[1]], ret[[2]]],
ret[[3]]
}
]
I then initialize a MeshRegion
to work with:
Options[sphTerrainGenInit] =
Options@DiscretizeGraphics;
sphTerrainGenInit[
pointNum : _Integer : 10000,
center : {_?NumericQ, _?NumericQ, _?NumericQ} : {0, 0, 0},
rad : _Real : 1,
ops : OptionsPattern[]
] :=
{DiscretizeGraphics[Ball[center, rad], ops], center};
Options[sphTerrainGen] =
Options@sphTerrainGenInit;
sphTerrainGen[
steps : _Integer : 100,
perturbationBounds : {_, _} : {.00001, .001},
ops : OptionsPattern[]
] :=
With[{base = sphTerrainGenInit[ops]},
MeshRegion @@ Take[sphTerrainGenStep[base, steps], 2]
]
I've then got a basic caching generator and a plotting function:
Options[planetTerrainDataCached] =
Options[sphTerrainGenInit];
planetTerrainDataCached[0, stepSize_Integer, ops_] :=
planetTerrainDataCached[0, stepSize, ops] =
sphTerrainGenInit[FilterRules[{ops}, Options@sphTerrainGenInit]];
planetTerrainDataCached[step_Integer, stepSize_Integer, ops_] :=
planetTerrainDataCached[step, stepSize, ops] =
sphTerrainGenStep[planetTerrainData[step - 1, stepSize, ops],
stepSize];
Options[planetTerrainData] =
Options[planetTerrainDataCached];
planetTerrainData[step_Integer, stepSize : _Integer : 100,
ops : OptionsPattern[]] :=
With[{o =
SortBy[Flatten@
FilterRules[{ops}, Options@planetTerrainDataCached], First]},
planetTerrainDataCached[step, stepSize, o]
];
Options[planetTerrain] =
Options[planetTerrainData];
planetTerrain[step_Integer, stepSize : _Integer : 100,
ops : OptionsPattern[]] :=
planetTerrainData[step, stepSize, ops][[1]]
Options[planetTerrainPlot] =
Join[
Options[SliceDensityPlot3D],
Options[planetTerrain]
];
planetTerrainPlot[i_Integer, stepSize : _Integer : 100,
ops : OptionsPattern[]] :=
planetTerrainPlot[
planetTerrain[i, stepSize,
FilterRules[{ops}, Options[planetTerrain]]],
ops
];
planetTerrainPlot[reg_?RegionQ, ops : OptionsPattern[]] :=
With[{rb = RegionBounds[reg]},
SliceDensityPlot3D[
Norm[{x, y, z}],
reg,
{x, rb[[1, 1]], rb[[1, 2]]},
{y, rb[[2, 1]], rb[[2, 2]]},
{z, rb[[3, 1]], rb[[3, 2]]},
Sequence @@
FilterRules[
{
ops,
ColorFunction -> "AlpineColors",
Boxed -> False,
Axes -> False
},
Options[SliceDensityPlot3D]
] // Evaluate
]
]
Put this all together:
planetTerrainPlot[1, MaxCellMeasure -> .0001]
We can also animate the steps of the algorithm:
slides1 =
Map[planetTerrainPlot[#, 1, ImageSize -> 250] &, Range@25];
slides1 // ListAnimate
We can really see how it's a sort-of addition of hemispheres.
Finally we can go to large numbers of steps and see just how craggy it becomes:
planetTerrainPlot[25, MaxCellMeasure -> .0001]
That's after ~25,000 steps. We could always decrease the cragginess some by playing with the perturbationBounds
parameter I defined in my step function, though.
To get more visible terrain contrast, we can take one of our less-craggy meshes and scale its points based on the histogram of Norm
values we have:
Rescale[Norm /@
MeshCoordinates[
planetTerrain[3, MaxCellMeasure -> .0001]]] // SmoothHistogram
This might lead us to do something like this:
regs = {
#,
MeshRegion[
MapThread[
Which[
#2 > .99, #3*#,
#2 < .98, #4*#,
True, #
] &,
{
MeshCoordinates[#],
Rescale[Norm /@ MeshCoordinates[#]],
RandomReal[{1.01, 1.03}, Length@MeshCoordinates[#]],
RandomReal[{.97, .98}, Length@MeshCoordinates[#]]
}],
MeshCells[#, All]
]
} &@
planetTerrain[3, MaxCellMeasure -> .0001];
regs // Map[planetTerrainPlot] // Row
At a minimum, the method described by the author in the link can be implemented like this