Source file: /examples/mesh/terrain.js
import Stats from "stats.js"
import { GUI } from "dat.gui"
import {
BasicMaterial,
Camera,
CameraPlugin,
MeshMaterial3D,
MeshMaterialPlugin,
PerspectiveProjection,
OrbitCameraControls,
TerrainMeshBuilder,
TextureLoader,
WireframeBuilder,
WebGLRenderDevice,
WebGLRenderer,
CanvasTarget
} from "chorama"
/**
* @param {number} x
* @param {number} z
* @returns {number}
*/
function sampleTerrainHeight(x, z) {
return (
Math.sin(x * 2.5) * Math.cos(z * 2.5) * 0.7 +
Math.sin((x + z) * 1.5) * 0.25
)
}
/**
* @param {number} x
* @param {number} z
* @returns {number}
*/
function ridgeTerrainHeight(x, z) {
return Math.sin(x * 4) * 0.18 + Math.cos(z * 3) * 0.15
}
const terrainHeightModes = {
Hills: sampleTerrainHeight,
Flat: () => 0,
Ridges: ridgeTerrainHeight
}
const canvas = document.createElement("canvas")
const renderTarget = new CanvasTarget(canvas)
const renderDevice = new WebGLRenderDevice(canvas, {
depth: true
})
const renderer = new WebGLRenderer({
plugins: [
new CameraPlugin(),
new MeshMaterialPlugin(),
]
})
const camera = new Camera(renderTarget)
const cameraControls = new OrbitCameraControls(camera, canvas)
const textureLoader = new TextureLoader()
const texture = textureLoader.load({
paths: ["/images/uv.jpg"],
flipY: true
})
const material = new BasicMaterial({
mainTexture: texture
})
const meshBuilder = new TerrainMeshBuilder()
meshBuilder.width = 1.8
meshBuilder.depth = 1.8
meshBuilder.widthSegments = 24
meshBuilder.depthSegments = 24
meshBuilder.heightScale = 0.35
meshBuilder.sampleHeight = terrainHeightModes.Hills
const object = new MeshMaterial3D(meshBuilder.build(), material)
camera.transform.position.set(0, 1.2, 6.8)
if (camera.projection instanceof PerspectiveProjection) {
camera.projection.fov = Math.PI / 180 * 90
camera.projection.aspect = innerWidth / innerHeight
}
document.body.append(canvas)
addEventListener("resize", updateView)
updateView()
requestAnimationFrame(update)
const settings = {
sampleHeight: terrainHeightModes.Hills,
wireframe: false
}
// demo-only GUI controls
const controls = new GUI()
const buildOptionsFolder = controls.addFolder("Settings")
buildOptionsFolder
.add(meshBuilder, "width", 0.2, 6, 0.01)
.name("Width")
.onFinishChange(buildMesh)
buildOptionsFolder
.add(meshBuilder, "depth", 0.2, 6, 0.01)
.name("Depth")
.onFinishChange(buildMesh)
buildOptionsFolder
.add(meshBuilder, "widthSegments", 1, 100, 1)
.name("Width Segments")
.onFinishChange(buildMesh)
buildOptionsFolder
.add(meshBuilder, "depthSegments", 1, 100, 1)
.name("Depth Segments")
.onFinishChange(buildMesh)
buildOptionsFolder
.add(meshBuilder, "heightScale", 0, 2, 0.01)
.name("Height Scale")
.onFinishChange(buildMesh)
buildOptionsFolder
.add(settings, "sampleHeight", terrainHeightModes)
.name("Height Pattern")
.onChange((value) => {
meshBuilder.sampleHeight = value
buildMesh()
})
buildOptionsFolder
.add(settings, "wireframe")
.name("Wireframe")
.onChange(buildMesh)
buildOptionsFolder.open()
function update() {
stats.begin()
cameraControls.update()
renderer.render([object, camera], renderDevice)
stats.end()
requestAnimationFrame(update)
}
function updateView() {
canvas.style.width = innerWidth + "px"
canvas.style.height = innerHeight + "px"
canvas.width = innerWidth * devicePixelRatio
canvas.height = innerHeight * devicePixelRatio
if (camera.projection instanceof PerspectiveProjection) {
camera.projection.aspect = innerWidth / innerHeight
}
}
function buildMesh() {
const mesh = meshBuilder.build()
if (settings.wireframe) {
object.mesh = new WireframeBuilder(mesh).build()
} else {
object.mesh = mesh
}
}
// demo-only performance monitor
const stats = new Stats()
stats.showPanel(1)
document.body.append(stats.dom)
stats.dom.removeAttribute("style")
stats.dom.classList.add("performance-monitor")