Getting Started
Basics

Basics

Planet

Your basic world is created through interacting with the Planet class. This will create a large cube, with each of its faces subdivided into a data structure called a QuadTree. Don't worry, it will be a sphere in the end, as each point will be bent into shape.

cube projected onto a sphere

from this gamedev.net post (opens in a new tab)

import { Planet } from "@hello-worlds/planet"
import worker from "./planet.worker"
 
// ...some threejs scene above
 
const kerbin = new Planet({
  radius: 100_000
  minCellSize: 32,
  minCellResolution: 48,
  workerProps: {
    numWorkers: 8,
    worker,
  },
  data: {
    seed: "My Cool Seed!",
  },
})
scene.add(kerbin)

By subdividing each plane into smaller pieces, we can delegate their construction to threads (in the browser called web-workers), which frees up our interactivity and makes the app feel snappier. This technique allows us to create vast earth-size (or larger!) worlds, because the user will only load a small fraction of the world at a given time in any significant detail.

Web Workers

The webworker requirement will likely be made optional and not a hard dependency in the future. Also, like.. I want this to work with node.js ¯\_(ツ)_/¯

We will use these web worker 'threads' to calculate the height positions on our planet, as well as the colors of each vertex.

Because we will make use web workers, you'll need a way to tell your bundler to build them.

components/planet/Planet.tsx

Generators

How do we add features to our planet? Generators!

Generators are simply functions that take an x,y,z position value as an input, and returns an output. You'll have access to other inputs in your webworker generator, but only by knowing the x,y,z in world coordinates of your vertex, you can already do many things!

Generators are higher-order functions. The first function allows you to initialize some expensive objects when the worker is first spawned. Use this as your "mount" effect to do things like create your noise objects. It's possible to pass initialData to your mounting function, and it has access to details like the planet's radius.

The second function which is returned by the first will be ran for each chunk update.

Generators are ran on the threads, and handle one chunk at a time. They're also static, which means once you bundle your app, you won't be able to change them. You should import all the functions you intend to use, as you won't be able to say... pass in a function in the generator input data (without shinanigans!)

Height Generator

The height generator will be ran inside the chunk web worker first. Usually you'll apply some noise, but you must return a number. We'll use this height number to push the vertex away from the sphere's origin, creating altitude (or lack thereof).

import {
  ChunkGenerator3Initializer,
  ColorArrayWithAlpha,
  createThreadedPlanetWorker,
  DEFAULT_NOISE_PARAMS,
  Noise,
  NOISE_TYPES,
} from "@hello-worlds/planets"
import { Color, MathUtils } from "three"
 
export type ThreadParams = {
  seed: string
}
 
const heightGenerator: ChunkGenerator3Initializer<ThreadParams, number> = ({
  data: { seed },
}) => {
  const terrainNoise = new Noise({
    ...DEFAULT_NOISE_PARAMS,
    seed,
    height: 2000,
    scale: 3000,
  })
 
  return ({ input }) => {
    const height = terrainNoise.get(input.x + w, input.y + w, input.z + w)
    return height
  }
}
 
// ... more below

Color Generator

The color generator will run after the height generator (and therefore will have access to the heigh details), and will return a THREE.Color. We'll use this to paint the vertex in our THREE.Material, and thus paint the world!

// ... continued from above
const colorGenerator: ChunkGenerator3Initializer<
  ThreadParams,
  Color | ColorArrayWithAlpha
> = () => {
  return ({ worldPosition }) => {
    const w = worldPosition.clone().normalize()
    return new Color().setRGB(w.x, w.y, w.z)
  }
}
 
// ... more below

Integrating into a worker

Simply call the createThreaded-WorldType-Worker() function and pass in your generators.

// ... continued from above
createThreadedPlanetWorker<ThreadParams>({
  heightGenerator,
  colorGenerator,
})