terrain height maps using noise, meshes with child meshes

This commit is contained in:
Evert Prants 2018-11-26 17:37:25 +02:00
parent 55b5565580
commit c941a65145
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
4 changed files with 144 additions and 21 deletions

View File

@ -27,17 +27,17 @@
{ {
"name": "Circle" "name": "Circle"
,"transformation": [ ,"transformation": [
1 1.29986
,0 ,0
,0 ,0
,0 ,0
,0 ,0
,1 ,1.29986
,0 ,0
,0 ,0
,0 ,0
,0 ,0
,1 ,1.29986
,0 ,0
,0 ,0
,0 ,0

View File

@ -43,29 +43,48 @@ class HeightMap {
} }
class SimplexHeightMap extends HeightMap { class SimplexHeightMap extends HeightMap {
constructor (offsetX, offsetY, size, seed) { // amplitude - Controls the amount the height changes. The higher, the taller the hills.
// persistence - Controls details, value in [0,1]. Higher increases grain, lower increases smoothness.
// octaves - Number of noise layers
// period - Distance above which we start to see similarities. The higher, the longer "hills" will be on a terrain.
// lacunarity - Controls period change across octaves. 2 is usually a good value to address all detail levels.
constructor (offsetX, offsetY, size, seed, amplitude = 15, persistence = 0.4, octaves = 5, period = 80, lacunarity = 2) {
super(size) super(size)
this.ix = offsetX this.ix = offsetX
this.iy = offsetY this.iy = offsetY
this.seed = seed this.seed = seed
this.osn = new OpenSimplexNoise(seed) this.osn = new OpenSimplexNoise(seed)
this.amplitude = amplitude
this.period = period
this.lacunarity = lacunarity
this.octaves = octaves
this.persistence = persistence
} }
getNoise (relX, relY) { getNoise (zx, zy) {
let x = ((this.ix * this.size) + relX) / this.size - 0.5 let x = ((this.size * this.ix) + zx) / this.period
let y = ((this.iy * this.size) + relY) / this.size - 0.5 let y = ((this.size * this.iy) + zy) / this.period
let total = this.osn.noise2D(2 * x, 2 * y) + let amp = 1.0
0.5 * this.osn.noise2D(4 * x, 4 * y) + let max = 1.0
0.25 * this.osn.noise2D(2 * x, 2 * y) let sum = this.osn.noise2D(x, y)
total *= 10 let i = 0
return total while (++i < this.octaves) {
x *= this.lacunarity
y *= this.lacunarity
amp *= this.persistence
max += amp
sum += this.osn.noise2D(x, y) * amp
}
return sum / max
} }
getHeight (x, y) { getHeight (x, y) {
return this.getNoise(x, y) return this.getNoise(x, y) * this.amplitude
} }
} }

View File

@ -1,13 +1,53 @@
import Resource from '../resource' import Resource from '../resource'
import { Texture, Material } from './material' import { Texture, Material } from './material'
import { mat4 } from 'gl-matrix' import { mat4, quat, vec3 } from 'gl-matrix'
let meshCache = {} let meshCache = {}
/**
* Returns an euler angle representation of a quaternion
* @param {vec3} out Euler angles, pitch-yaw-roll
* @param {quat} mat Quaternion
* @return {vec3} out
*/
quat.getEuler = function (out, quat) {
let x = quat[0]
let y = quat[1]
let z = quat[2]
let w = quat[3]
let x2 = x * x
let y2 = y * y
let z2 = z * z
let w2 = w * w
let unit = x2 + y2 + z2 + w2
let test = x * w - y * z
if (test > 0.499995 * unit) {
// singularity at the north pole
out[0] = Math.PI / 2
out[1] = 2 * Math.atan2(y, x)
out[2] = 0
} else if (test < -0.499995 * unit) {
// singularity at the south pole
out[0] = -Math.PI / 2
out[1] = 2 * Math.atan2(y, x)
out[2] = 0
} else {
out[0] = Math.asin(2 * (x * z - w * y))
out[1] = Math.atan2(2 * (x * w + y * z), 1 - 2 * (z2 + w2))
out[2] = Math.atan2(2 * (x * y + z * w), 1 - 2 * (y2 + z2))
}
return out
}
class Node { class Node {
constructor (pos, scale, rotation) { constructor (pos, scale, rotation) {
// Translation
this.pos = pos || [0.0, 0.0, 0.0] this.pos = pos || [0.0, 0.0, 0.0]
// Scaling
this.scale = scale || [1.0, 1.0, 1.0] this.scale = scale || [1.0, 1.0, 1.0]
// Rotation in Euler angles (yaw, pitch, roll) in radians
this.rotation = rotation || [0.0, 0.0, 0.0] this.rotation = rotation || [0.0, 0.0, 0.0]
this.transform = mat4.create() this.transform = mat4.create()
@ -55,12 +95,35 @@ class Node {
} }
} }
setTransformation (transform) {
let quaternion = quat.create()
let translation = vec3.create()
let rotation = vec3.create()
let scale = vec3.create()
mat4.getTranslation(translation, transform)
mat4.getScaling(scale, transform)
mat4.getRotation(quaternion, transform)
quat.getEuler(rotation, quaternion)
this.rotation = rotation
this.pos = translation
this.scale = scale
this.updateTransform()
}
// Setters // Setters
setPosition (newPos) { setPosition (newPos) {
this.pos = newPos this.pos = newPos
this.updateTransform() this.updateTransform()
} }
setTranslation (newTrans) {
this.setPosition(newTrans)
}
setScale (newScale) { setScale (newScale) {
this.scale = newScale this.scale = newScale
this.updateTransform() this.updateTransform()
@ -99,6 +162,10 @@ class Node {
return this.pos return this.pos
} }
get translation () {
return this.pos
}
// Draw base // Draw base
draw (gl, shader) { draw (gl, shader) {
// Set model transform matrix uniform // Set model transform matrix uniform
@ -132,7 +199,7 @@ class Mesh extends Node {
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW)
let mesh = new Mesh() let mesh = new Mesh()
mesh.pos = pos mesh.posBuffer = pos
mesh.ebo = ebo mesh.ebo = ebo
mesh.indices = indices.length mesh.indices = indices.length
@ -219,7 +286,8 @@ class Mesh extends Node {
}) })
} }
let finished = [] // Load everything
let loadComplete = []
for (let i in cleaned) { for (let i in cleaned) {
let meshdata = cleaned[i] let meshdata = cleaned[i]
let mesh = Mesh.construct(gl, meshdata.vertices, meshdata.indices, let mesh = Mesh.construct(gl, meshdata.vertices, meshdata.indices,
@ -235,7 +303,37 @@ class Mesh extends Node {
} }
} }
finished.push(mesh) loadComplete.push(mesh)
}
// Now, we need to give the meshes the appropriate parents and transforms.
let finished = []
function setChildren (parent, chMeshes) {
let meshIndex = chMeshes.meshes[0]
let mesh = loadComplete[meshIndex]
if (chMeshes.children) {
for (let i in chMeshes.children) {
if (!chMeshes.children[i].meshes) continue
setChildren(mesh, chMeshes.children[i])
}
}
mesh.name = chMeshes.name
if (parent == null) {
finished.push(mesh)
} else {
parent.children.push(mesh)
mesh.parent = parent
mesh.setTransformation(chMeshes.transformation)
}
}
let rootchildren = dat.rootnode.children
for (let j in rootchildren) {
if (!rootchildren[j].meshes) continue
setChildren(null, rootchildren[j])
} }
// Cache the mesh // Cache the mesh
@ -245,7 +343,7 @@ class Mesh extends Node {
} }
bindBuffers (gl, shader) { bindBuffers (gl, shader) {
gl.bindBuffer(gl.ARRAY_BUFFER, this.pos) gl.bindBuffer(gl.ARRAY_BUFFER, this.posBuffer)
shader.setAttribute(gl, 'aVertexPosition', 3, false, 3 * Float32Array.BYTES_PER_ELEMENT, 0) shader.setAttribute(gl, 'aVertexPosition', 3, false, 3 * Float32Array.BYTES_PER_ELEMENT, 0)
if (this.nms && shader.hasAttribute(gl, 'aNormal')) { if (this.nms && shader.hasAttribute(gl, 'aNormal')) {

View File

@ -1,6 +1,6 @@
import Engine from './engine' import Engine from './engine'
import Camera from './engine/camera' import Camera from './engine/camera'
// import Entity from './engine/mesh/entity' import Entity from './engine/mesh/entity'
import { Environment } from './engine/environment' import { Environment } from './engine/environment'
import { Terrain } from './engine/components/terrain' import { Terrain } from './engine/components/terrain'
@ -11,10 +11,12 @@ let game = new Engine()
let env = new Environment() let env = new Environment()
async function pipeline () { async function pipeline () {
// let entity = await Entity.createEntity(game.gl, 'test', [0.0, 0.0, -6.0]) let entity = await Entity.createEntity(game.gl, 'test', [0.0, 0.0, -6.0])
// let shader = await game.shaders.createShaderFromFiles(game.gl, 'basic', false) let shader = await game.shaders.createShaderFromFiles(game.gl, 'basic', false)
let terrainShader = await game.shaders.createShaderFromFiles(game.gl, 'terrain', false) let terrainShader = await game.shaders.createShaderFromFiles(game.gl, 'terrain', false)
console.log(entity.mesh)
// Create a height map based on OpenSimplex noise // Create a height map based on OpenSimplex noise
let hmap = new SimplexHeightMap(1, 1, 256, 50) let hmap = new SimplexHeightMap(1, 1, 256, 50)
@ -53,6 +55,10 @@ async function pipeline () {
// Render function for the triangle // Render function for the triangle
game.addRenderFunction(function (gl) { game.addRenderFunction(function (gl) {
shader.use(gl)
cam.draw(gl, shader)
entity.draw(gl, shader)
// Use terrain shader // Use terrain shader
terrainShader.use(gl) terrainShader.use(gl)