Skip to content

Commit

Permalink
Updates to noise seed.
Browse files Browse the repository at this point in the history
  • Loading branch information
NorthernScott committed Sep 30, 2024
1 parent df60236 commit 5775040
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 65 deletions.
79 changes: 42 additions & 37 deletions lathe/lathe.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,24 @@
import random
import time
from io import TextIOWrapper
from typing import Generator

# import third-party modules
import numpy as np
import typer

# import internal modules
from mylogger import err_con, log, std_con
from numpy.typing import NDArray
from rich.logging import RichHandler
from rich.table import Table
from typing_extensions import Annotated

# import internal modules
from mylogger import log, std_con
from terrain import sample_octaves
from typing_extensions import Annotated
from util import Mesh, MeshArray, create_mesh, now, rescale, save_world
from viz import viz

# Define globals.

RADIUS: int = 6378100 # Radius of the world in meters. The approximate radius of Earth is 6378100 m.
FEATURE_SIZE: np.float64 = (
RADIUS * 0.25
) # Determines the "coherence" of the random noise; affects the size of discrete landmasses. A good size for Earth-like continents is 0.5× the radius.
ZMIN: int = round(
number=-(RADIUS * 0.0015)
) # The lowest elevation in the world, in meters. The deepest oceanic trench on Earth is approximately -10,300 m.
Expand Down Expand Up @@ -93,12 +89,6 @@ def main(
help="The radius of the world in meters. (Earth radius is approximately 6378100 m.)"
),
] = RADIUS,
feature_size: Annotated[
float,
typer.Option(
help="Determines the coherence of noise; affects the size of discrete landmasses. A good size for Earth-like continents is approximately 50% of the radius."
),
] = FEATURE_SIZE,
recursion: Annotated[
int,
typer.Option(
Expand Down Expand Up @@ -129,7 +119,7 @@ def main(
help="Sets the global sea level by defining a relative percent of the range from min to max altitude."
),
] = OCEAN_PERCENT,
zscale: Annotated[
scale: Annotated[
int,
typer.Option(
help="Sets a scaling factor for elevations to make them visible in the plot. Does not change the actual elevation values."
Expand Down Expand Up @@ -175,11 +165,28 @@ def main(

std_con.print("Setting world seed and parameters.\r\n")

if seed == 0:
seed_rng: Generator = np.random.default_rng()
world_seed: int = seed_rng.integers(low=0, high=999999)
else:
world_seed: int = seed
try:
if seed == 0:
world_seed = 0
elif seed < 1:
world_seed = 0
err_con.print(
"Seed must be an integer between 1 and 255. Setting to random seed. \r\n"
)
elif seed > 255:
world_seed = 0
err_con.print(
"Seed must be an integer between 1 and 255. Setting to random seed. \r\n"
)
else:
world_seed = seed
except ValueError as e:
err_con.print("Seed must be an integer between 1 and 256. \r\n", f"{e}")
finally:
if world_seed == 0:
world_seed_string = str("Random")
else:
world_seed_string = str(world_seed)

zrange: float = zmax - zmin

Expand All @@ -190,9 +197,8 @@ def main(
world_params = {
"name": name,
"timestamp": now(),
"seed": int(world_seed),
"seed": world_seed_string,
"radius": radius,
"feature size": feature_size,
"recursion": recursion,
"octaves": octaves,
"ztilt": ztilt,
Expand All @@ -209,18 +215,18 @@ def main(
params_table.title = "World Parameters"
params_table.title_style = "bold magenta"
params_table.add_row("Name", name)
params_table.add_row("World Seed", str(object=world_seed))
params_table.add_row("Radius", str(object=radius))
params_table.add_row("Feature Size", str(object=feature_size))
params_table.add_row("Recursion Factor", str(object=recursion))
params_table.add_row("Octaves (#)", str(object=octaves))
params_table.add_row("Axial Tilt", str(object=ztilt))
params_table.add_row("Lowest Elevation", str(object=zmin))
params_table.add_row("Highest Elevation", str(object=zmax))
params_table.add_row("Elevation Range", str(object=zrange))
params_table.add_row("Ocean Percent", str(object=ocean_percent))
params_table.add_row("Sea Level", str(object=ocean_point))
params_table.add_row("Log Display", str(object=loglevel))
params_table.add_row("Timestamp", str(now()))
params_table.add_row("World Seed", world_seed_string)
params_table.add_row("Radius", str(radius))
params_table.add_row("Recursion Factor", str(recursion))
params_table.add_row("Octaves (#)", str(octaves))
params_table.add_row("Axial Tilt", str(ztilt))
params_table.add_row("Lowest Elevation", str(zmin))
params_table.add_row("Highest Elevation", str(zmax))
params_table.add_row("Elevation Range", str(zrange))
params_table.add_row("Ocean Percent", str(ocean_percent))
params_table.add_row("Sea Level", str(ocean_point))
params_table.add_row("Log Display", str(loglevel))

std_con.print(params_table)

Expand Down Expand Up @@ -254,7 +260,6 @@ def main(
init_strength=INIT_STRENGTH,
roughness=ROUGHNESS,
persistence=PERSISTENCE,
feature_size=feature_size,
radius=radius,
seed=world_seed,
)
Expand Down Expand Up @@ -282,7 +287,7 @@ def main(
log.debug(msg="Generating elevation scalars.")
elevation_scalars: NDArray[np.float64] = (
((raw_elevations) + radius) / radius
) * zscale
) * scale
log.debug(msg="")

log.debug(msg="Elevation Scalars:")
Expand Down Expand Up @@ -326,7 +331,7 @@ def main(
world_mesh=world_mesh,
# scalars="Elevations",
radius=radius,
zscale=zscale,
zscale=scale,
zmin=zmin,
zmax=zmax,
)
Expand Down
71 changes: 43 additions & 28 deletions lathe/terrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,28 @@

import numpy as np
import opensimplex as osi
from mylogger import std_con
from mylogger import err_con, std_con
from numba import prange
from numpy.typing import NDArray


def sample_noise(
points, roughness, strength, feature_size, radius
points: NDArray[np.float64],
roughness: float,
strength: float,
radius: int,
) -> NDArray[np.float64]:
"""Samples 4-dimension simplex noise at each point in the input array.
Args:
points (NDArray[np.float64]): A 3d array of points.
roughness (float): The roughness (frequency) of the noise.
strength (float): The strength (persistence) of the noise.
radius (int): The radius of the world.
Returns:
elevations: A NDArray[np.float64] of elevations for each point in the input array.
"""
elevations = np.ones(len(points), dtype=np.float64)
rough_verts = points * roughness

Expand All @@ -19,58 +33,59 @@ def sample_noise(
x=rough_verts[v][0],
y=rough_verts[v][1],
z=rough_verts[v][2],
w=1 / feature_size,
w=1,
)

# ?: Adding +1 to elevation moves negative values in the 0-1 range. Multiplying by 0.5 drags any values > 1 back into the 0-1 range. I'm not sure if multiplying by the radius is the proper thing to do in my next implementation.

# return (elevations + 1) * 0.5 * strength * radius
return elevations * strength * radius


def sample_octaves(
points,
octaves,
init_roughness,
init_strength,
roughness,
persistence,
feature_size,
radius,
seed,
points: NDArray[np.float64],
octaves: int,
init_roughness: float,
init_strength: float,
roughness: float,
persistence: float,
radius: int,
seed: int,
) -> NDArray[np.float64]:
"""Samples multiple octaves of noise to generate elevations.
"""Iterates through the noise function multiple times to create a more complex noise pattern.
Arguments:
points -- _description_
octaves -- _description_
init_roughness -- _description_
init_strength -- _description_
roughness -- _description_
persistence -- _description_
radius -- _description_
Args:
points (NDArray[np.float64]): A 3d array of points.
octaves (int): The number of times to iterate through the noise function.
init_roughness (float): A starting value for the roughness.
init_strength (float): A starting value for the strength.
roughness (float): A multiplier for the roughness.
persistence (float): A multiplier for the strength.
radius (int): The radius of the world.
seed (int): The seed for the noise generator.
Returns:
Array of elevations.
elevations: A NDArray[np.float64] of elevations for each point in the input array.
"""

# Initialize noise generator seed.
osi.seed(seed)

if seed == 0:
osi.random_seed()
else:
osi.seed(seed)

# Initialize elevations array.
elevations = np.ones(shape=len(points), dtype=np.float64)

# NOTE: In my separate-sampling experiment, rough/strength pairs of (1.6, 0.4) (5, 0.2) and (24, 0.02) were good for 3 octaves. The final 3 results were added and then multiplied by 0.4

for i in prange(octaves):
std_con.print(f"Octave: {i+1} ", "\r\n")
elevations += sample_noise(
points=points,
roughness=init_roughness / radius,
strength=init_strength / radius,
feature_size=feature_size,
radius=radius,
)
init_roughness *= roughness
init_strength *= persistence
std_con.print(f"Octave: {i} ", "\r\n")

return elevations

0 comments on commit 5775040

Please sign in to comment.