terrain height maps using noise, meshes with child meshes
This commit is contained in:
parent
55b5565580
commit
c941a65145
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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')) {
|
||||||
|
12
src/index.js
12
src/index.js
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user