Cross Country

A small tank in open country: rolling hills, woods, lakes and the whole sky. Its tracks are forty links a side, carried round by the model's own animation at exactly the pace of the ground, so they grip on the spot when it turns in place; its wheels turn with them, the turret traverses and the gun elevates. Head for the hills.

Demo

import {
  b3d, b3dSun, b3dSkybox, b3dLight, b3dFog, b3dTerrain, b3dCloudDeck, b3dDecorator, b3dWater,
  b3dTank, inputFocus, pad, slider3d, assetUrl, effectiveTier, headsetDevice,
} from 'tosijs-3d'
import { tosi } from 'tosijs'

const { crossCountry } = tosi({ crossCountry: { time: 9, speed: 30, coverage: 0.4 } })

// A LIGHTER WORLD on a headset or a low-powered device: a shorter view, fewer
// trees, no cloud deck.
const light = headsetDevice() || effectiveTier() === 'low'

// A level patch to start on, eased into the hills round it.
const level = 30
const start = pad({ x: 0, z: 0, radius: 12, level, blend: 80 })

const terrain = b3dTerrain({
  seed: 7,
  biome: 'on',
  surfaceType: 'cylinder',
  radius: 1000,
  cylinderHeight: 1000,
  tileSize: 128,
  lodLevels: light ? 2 : 3,
  splitFactor: 2,
  reach: light ? 1500 : 4000,
  grossScale: 0.015,
  detailScale: 0.09,
  horizScale: 8,
  grossAmplitude: 160,
  detailAmplitude: 8,
  // Mostly land, with lakes and sea in the low ground.
  baseHeight: -40,
  // Green country: moist, and cooling slowly enough with height that the
  // hills are not snow-capped.
  biomeMoisture: 0.9,
  biomeLapseRate: 0.0015,
})
terrain.landform = start
terrain.regenerate()

const woods = b3dDecorator(light ? { budget: 1200, radius: 400 } : { budget: 5000, radius: 900 })
// Nothing on the starting patch.
woods.clear = (x, z) => Math.max(0, Math.min(1, (Math.hypot(x, z) - 14) / 20))

preview.append(
  b3d(
    {
      style: 'width: 100%; height: 100%',
      gamepad: 'left_stick,right_stick,right_stick_click,left_trigger,right_trigger,right_bumper,A',
      panelTitle: 'Cross Country',
      scenePanel: () => [
        slider3d({ label: 'time of day', value: crossCountry.time, min: 0, max: 24, step: 0.1 }),
        slider3d({ label: 'time speed', value: crossCountry.speed, min: 0, max: 600, step: 10 }),
        ...(light ? [] : [slider3d({ label: 'cloud cover', value: crossCountry.coverage, min: 0, max: 1.5, step: 0.05 })]),
      ],
    },
    b3dSun({ shadowTextureSize: 2048 }),
    b3dSkybox({
      timeOfDay: crossCountry.time,
      realtimeScale: crossCountry.speed,
      starfieldCube: '/sky/nebula',
      starfieldData: '/sky/stars',
      starfieldTilt: '12,25,58',
    }),
    b3dLight({ intensity: 0.5, nightIntensity: 0.06, groundColor: '#4b4d52' }),
    b3dFog({ syncSkybox: true, start: light ? 400 : 800, end: light ? 1400 : 3500 }),
    terrain,
    woods,
    ...(light ? [] : [b3dCloudDeck({ altitude: 600, coverage: crossCountry.coverage, orographic: 0.6, wind: 8 })]),
    b3dWater({ y: 0, waterSize: 8000, follow: true, twoSided: true }),
    inputFocus(
      b3dTank({ url: assetUrl('tonio/tank/tank2.glb'), player: true, x: 0, z: 0, y: level + 0.5 })
    )
  )
)
.preview { height: 100%; }

On a headset or a low-powered device the world is lighter: a shorter view, fewer trees and no cloud deck.

The right trigger or W drives, the left trigger, the right bumper or S brakes and reverses, and the left stick or A and D steer: a tank turns on the spot. The right stick or the arrow keys traverse the turret, and pulling back raises the gun; clicking the right stick, V, or the glass gamepad's button under the right stick swaps up and down between the gun and the camera (in a headset a buzz: one for the gun, two for the camera); the view follows the turret, and the turret holds its aim while the hull turns under it. The owl's panel sets the hour, the speed and the cloud.

How it is put together