From 7502888d0316c3e88cb3bf3fc2e9602f2b44484d Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Fri, 1 Mar 2019 12:37:30 +0200 Subject: [PATCH] Camera-based subdivision --- src/engine/components/planet/index.js | 48 ++++++++++++++++++++++----- src/engine/mesh/aabb.js | 37 +++++++++++++++++++++ src/index.js | 8 +++-- 3 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 src/engine/mesh/aabb.js diff --git a/src/engine/components/planet/index.js b/src/engine/components/planet/index.js index f149668..76d4bbf 100644 --- a/src/engine/components/planet/index.js +++ b/src/engine/components/planet/index.js @@ -1,5 +1,6 @@ import { Mesh } from '../../mesh' -import { mat4 } from 'gl-matrix' +import { BoundingBox } from '../../mesh/aabb' +import { mat4, vec3 } from 'gl-matrix' import { subv3, mulv3, addv3, normalv3, crossv3 } from '../../utility' const lodMax = 8 @@ -28,10 +29,6 @@ class CubeFace { this.position = subv3(this.position, mulv3(this.left, this.radius / 2)) this.position = subv3(this.position, mulv3(this.forward, this.radius / 2)) - if (this.parent) { - this.transform = this.parent.transform - } - this.generate() } @@ -88,6 +85,7 @@ class CubeFace { } this.mesh = Mesh.construct(window.gl, vertices, indices, textureCoords) + this.bounds = BoundingBox.fromMesh(this.mesh) this.generated = true } @@ -139,10 +137,6 @@ class CubeFace { return } - // Set model transform matrix uniform - const transformLocation = shader.getUniformLocation(gl, 'uModelMatrix') - gl.uniformMatrix4fv(transformLocation, false, this.transform) - this.mesh.prepare(gl, shader) this.mesh.draw(gl, shader) } @@ -174,7 +168,43 @@ class CubePlanet { this.faces[0].children[0].subdivide() } + getActiveQuadTreeFaces (face, active) { + if (!face.children.length) { + active.push(face) + } else { + for (let f = 0; f < face.children.length; f++) { + this.getActiveQuadTreeFaces(face.children[f], active) + } + } + } + + update (f, camera) { + let activeTree = [] + this.getActiveQuadTreeFaces(this.faces[f], activeTree) + + for (let a = 0; a < activeTree.length; a++) { + let dist = vec3.distance(camera.pos, activeTree[a].bounds.center) + + if (dist > activeTree[a].radius) { + if (dist <= this.radius * 2.0 || activeTree[a].parent === this) { + activeTree[a].merge() + activeTree[a].generate() + } else { + activeTree[a].merge() + activeTree[a].parent.merge() + activeTree[a].parent.generate() + } + } else if (activeTree[a].radius > this.resolution && activeTree[a].level < lodMax) { + activeTree[a].subdivide() + } + } + } + draw (gl, shader) { + // Set model transform matrix uniform + const transformLocation = shader.getUniformLocation(gl, 'uModelMatrix') + gl.uniformMatrix4fv(transformLocation, false, this.transform) + for (let i in this.faces) { this.faces[i].draw(gl, shader) } diff --git a/src/engine/mesh/aabb.js b/src/engine/mesh/aabb.js new file mode 100644 index 0000000..6f1694f --- /dev/null +++ b/src/engine/mesh/aabb.js @@ -0,0 +1,37 @@ + +class BoundingBox { + constructor (min, max) { + this.min = min + this.max = max + this.calculateSphere() + } + + static fromMesh (mesh) { + let min = [0, 0, 0] + let max = [0, 0, 0] + for (let v = 0; v < mesh.vertices.length; v += 3) { + let vertex = [mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]] + // X + if (vertex[0] > max[0]) max[0] = vertex[0] + if (vertex[0] < min[0]) min[0] = vertex[0] + // Y + if (vertex[1] > max[1]) max[1] = vertex[1] + if (vertex[1] < min[1]) min[1] = vertex[1] + // Z + if (vertex[2] > max[2]) max[2] = vertex[2] + if (vertex[2] < min[2]) min[2] = vertex[2] + } + return new BoundingBox(min, max) + } + + calculateSphere () { + this.center = [ + this.min[0] + (this.max[0] - this.min[0]) / 2, + this.min[1] + (this.max[1] - this.min[1]) / 2, + this.min[2] + (this.max[2] - this.min[2]) / 2 + ] + this.radius = Math.max((this.max[0] - this.min[0]) / 2, (this.max[1] - this.min[1]) / 2, (this.max[2] - this.min[2]) / 2) + } +} + +export { BoundingBox } diff --git a/src/index.js b/src/index.js index d5ebd28..f03dbef 100644 --- a/src/index.js +++ b/src/index.js @@ -38,9 +38,10 @@ async function pipeline () { cam.updateProjection(game.gl) // Planet test - let planet = new CubePlanet([0.0, 0.0, 0.0], 16, 128) + let planet = new CubePlanet([0.0, 0.0, 0.0], 16, 512) // Update function for camera + let face = 0 game.addUpdateFunction(function (dt) { if (game.input.isDown('w')) { cam.processKeyboard(0, dt) @@ -61,6 +62,8 @@ async function pipeline () { // TESTING: Move model forward // t = t + 0.1 // entity.setPosition([t, 0.0, 0.0]) + planet.update(face++, cam) + if (face > 5) face = 0 }) // Render function for the triangle @@ -69,7 +72,7 @@ async function pipeline () { cam.draw(gl, shader) entity.draw(gl, shader) planet.draw(gl, shader) - +/* // Use terrain shader terrainShader.use(gl) @@ -81,6 +84,7 @@ async function pipeline () { // Draw terrain terrain.draw(gl, terrainShader) +*/ }) game.startGameLoop()