layers/boulders/boulder_shape.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief Procedural boulder meshes: styles, shape options and seeded shape generation. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/mesh/io/mesh_io.h" | ||
| 6 | #include "terraingen/mesh/mesh.h" | ||
| 7 | #include "terraingen/mesh/polyhedra.h" | ||
| 8 | |||
| 9 | #include <cstddef> | ||
| 10 | #include <cstdint> | ||
| 11 | #include <memory> | ||
| 12 | #include <string> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | namespace terraingen { | ||
| 16 | |||
| 17 | /// An interval to draw a value uniformly from. | ||
| 18 | struct ValueRange { | ||
| 19 | 460 | double min = 0.0; ///< Lower end. | |
| 20 | 460 | double max = 0.0; ///< Upper end, >= `min`. | |
| 21 | |||
| 22 | 460 | bool operator==(const ValueRange&) const = default; | |
| 23 | }; | ||
| 24 | |||
| 25 | /// One kind of boulder: the solid it starts from and the ranges its shape is drawn from. | ||
| 26 | /// | ||
| 27 | /// The solid is subdivided, stretched, rounded, roughened and cut by fracture planes, every vertex | ||
| 28 | /// moving only along its ray from the origin, so the mesh never folds over itself. Ratios and | ||
| 29 | /// lengths are in long-axis lengths. | ||
| 30 | struct BoulderStyle { | ||
| 31 | 92 | std::string name; ///< Label, e.g. "block". | |
| 32 | 92 | double weight = 1.0; ///< Relative share of boulders of this style. | |
| 33 | /// The solid the shape starts from. | ||
| 34 | 92 | BaseSolid solid = BaseSolid::Icosahedron; | |
| 35 | 92 | ValueRange intermediate{0.6, 0.85}; ///< b / a, the intermediate axis over the long one. | |
| 36 | 92 | ValueRange shortAxis{0.4, 0.6}; ///< c / a, the short (vertical) axis over the long one. | |
| 37 | 92 | ValueRange rounding{0.0, 0.0}; ///< Share toward the ellipsoid: 0 keeps corners, 1 is it. | |
| 38 | 92 | int minFractures = 0; ///< Fewest fracture planes. | |
| 39 | 92 | int maxFractures = 0; ///< Most fracture planes. | |
| 40 | 92 | ValueRange fractureDepth{0.6, 0.9}; ///< Plane offset as a share of the shape's reach. | |
| 41 | 92 | double maxFractureTilt = 1.0; ///< Largest |z| of a fracture normal, in [0, 1]. | |
| 42 | 92 | double noiseAmplitude = 0.05; ///< Largest relative change of the radius by noise. | |
| 43 | 92 | double noiseWavelength = 0.6; ///< Wavelength of the first noise octave. | |
| 44 | 92 | int noiseOctaves = 3; ///< Noise octaves, each half the wavelength. | |
| 45 | /// How far edges and corners are worn back where faces meet, as weathering rounds them, in | ||
| 46 | /// long-axis lengths; 0 keeps them sharp. | ||
| 47 | 92 | ValueRange edgeSoftness{0.0, 0.0}; | |
| 48 | /// How far fracture faces undulate, as a share of each plane's offset; 0 keeps them flat. | ||
| 49 | 92 | double faceRoughness = 0.0; | |
| 50 | |||
| 51 | 92 | bool operator==(const BoulderStyle&) const = default; | |
| 52 | }; | ||
| 53 | |||
| 54 | /// The default mix of boulder styles, every one cut irregularly, weathered at its edges and rough | ||
| 55 | /// on its faces: polyhedra from a sphere (angular, platy, subrounded) most of all, then blocks from | ||
| 56 | /// a cube, wedges, pyramids, rounded boulders, shards from a tetrahedron and near-spheres. | ||
| 57 | std::vector<BoulderStyle> DefaultBoulderStyles(); | ||
| 58 | |||
| 59 | /// Settings for `GenerateBoulderShape`. | ||
| 60 | struct BoulderShapeOptions { | ||
| 61 | 12 | std::vector<BoulderStyle> styles = DefaultBoulderStyles(); ///< Styles to pick from by weight. | |
| 62 | 12 | int subdivisions = 3; ///< Times the solid's triangles are split in four. | |
| 63 | 12 | std::uint32_t seed = 0; ///< Seed shared by every shape. | |
| 64 | /// Scales every style's edge softness: 0 gives sharp edges, 1 the styles' own, more is more | ||
| 65 | /// worn. | ||
| 66 | 12 | double weathering = 1.0; | |
| 67 | /// Rock meshes, such as `ReadMesh` reads, that replace the styles when not empty: each seed | ||
| 68 | /// picks one, normalised to the unit form of `BoulderShape` with its z axis kept vertical. | ||
| 69 | 12 | std::vector<std::shared_ptr<const SceneMesh>> library{}; | |
| 70 | |||
| 71 | 12 | bool operator==(const BoulderShapeOptions&) const = default; | |
| 72 | }; | ||
| 73 | |||
| 74 | /// Checks boulder shape options. | ||
| 75 | /// @throws std::invalid_argument for no styles, a style with a negative weight, all weights zero, | ||
| 76 | /// an empty or inverted range, axis ratios outside (0, 1], rounding or depths outside [0, 1], | ||
| 77 | /// fracture counts not in 0 <= min <= max <= 64, a tilt outside [0, 1], noise amplitude outside | ||
| 78 | /// [0, 1), a non-positive wavelength, octaves outside 1 to 12, an edge softness outside [0, 0.5], | ||
| 79 | /// a face roughness outside [0, 0.5], a negative weathering, or subdivisions outside 0 to 6, | ||
| 80 | /// or a library mesh that is null, invalid or flat along an axis. | ||
| 81 | void ValidateBoulderShapeOptions(const BoulderShapeOptions& options); | ||
| 82 | |||
| 83 | /// A boulder's shape in unit form, with the origin inside it. | ||
| 84 | struct BoulderShape { | ||
| 85 | TriangleMesh mesh; ///< Long axis along x, extent exactly 1. | ||
| 86 | std::size_t style = 0; ///< Index into the options' styles, or its library when it has one. | ||
| 87 | double intermediate = 1.0; ///< Extent along y, in long-axis lengths. | ||
| 88 | double shortAxis = 1.0; ///< Extent along z, in long-axis lengths. | ||
| 89 | MeshMaterial material{"rock", {0.5, 0.5, 0.5}}; ///< The library mesh's, or plain grey. | ||
| 90 | }; | ||
| 91 | |||
| 92 | /// `mesh` in the unit form of `BoulderShape`: centred on its box, turned about z so its longer | ||
| 93 | /// horizontal side lies along x, and scaled so that side is 1 long. | ||
| 94 | /// @throws std::invalid_argument for a mesh that is invalid or flat along an axis. | ||
| 95 | BoulderShape NormalizedBoulderShape(const SceneMesh& mesh); | ||
| 96 | |||
| 97 | /// The shape a seed gives, a pure function of the seed and options; every subdivision level | ||
| 98 | /// gives the same shape in finer detail. | ||
| 99 | /// @param shapeSeed E.g. from `BoulderShapeSeed`. | ||
| 100 | /// @throws std::invalid_argument as `ValidateBoulderShapeOptions`. | ||
| 101 | BoulderShape GenerateBoulderShape(std::uint64_t shapeSeed, const BoulderShapeOptions& options); | ||
| 102 | |||
| 103 | /// The shape seed of the boulder with ID `id`: `id` itself for 0 `prototypes`, otherwise `id` | ||
| 104 | /// hashed into [0, `prototypes`), so a field can be drawn with a small library of instanced meshes. | ||
| 105 | std::uint64_t BoulderShapeSeed(std::uint64_t id, std::uint32_t prototypes = 0); | ||
| 106 | |||
| 107 | } // namespace terraingen | ||
| 108 |