diff --git a/src/engine/components/planet/index.js b/src/engine/components/planet/index.js index 9b1ea0f..6b4847a 100644 --- a/src/engine/components/planet/index.js +++ b/src/engine/components/planet/index.js @@ -14,88 +14,25 @@ class PlanetGenerator { if (resolution <= 1 || resolution % 2 === 0) { throw new Error('Resolution must be higher than 1 and an odd number.') } + + PlanetIndexBuffers.generate(resolution) } } -class CubeFace { - constructor (index, parent, level, pos, normal, generator) { - this.index = index - this.parent = parent - this.children = [] - - this.normal = normal - - this.level = level - - this.generated = false - this.generator = generator - - // Calculate left (x) and forward (z) vectors from the normal (y) - this.left = [normal[1], normal[2], normal[0]] - this.forward = crossv3(normal, this.left) - - this.position = pos - - // Center the face - if (level === 0) { - this.position = subv3(this.position, mulv3(this.left, generator.radius / 2)) - this.position = subv3(this.position, mulv3(this.forward, generator.radius / 2)) - } - - this.generate() - } - - generate () { - if (this.generated) return - - const sideResolution = this.generator.resolution - const vertices = [] - const normals = [] - const textureCoords = [] +const PlanetIndexBuffers = { + generate (sideResolution) { + PlanetIndexBuffers.base = PlanetIndexBuffers.generateBuffer(sideResolution) + PlanetIndexBuffers.fixT = PlanetIndexBuffers.generateBuffer(sideResolution, true, false, false, false) + PlanetIndexBuffers.fixTR = PlanetIndexBuffers.generateBuffer(sideResolution, true, true, false, false) + PlanetIndexBuffers.fixTL = PlanetIndexBuffers.generateBuffer(sideResolution, true, false, true, false) + PlanetIndexBuffers.fixB = PlanetIndexBuffers.generateBuffer(sideResolution, false, false, false, true) + PlanetIndexBuffers.fixBR = PlanetIndexBuffers.generateBuffer(sideResolution, false, true, false, true) + PlanetIndexBuffers.fixBL = PlanetIndexBuffers.generateBuffer(sideResolution, false, false, true, true) + PlanetIndexBuffers.fixR = PlanetIndexBuffers.generateBuffer(sideResolution, false, true, false, false) + PlanetIndexBuffers.fixL = PlanetIndexBuffers.generateBuffer(sideResolution, false, false, true, false) + }, + generateBuffer (sideResolution, fanTop = false, fanRight = false, fanLeft = false, fanBottom = false) { const indices = [] - - const radius = this.generator.radius - const divisionLevel = Math.pow(2, this.level) - - for (let i = 0, vertexPointer = 0; i < sideResolution; i++) { - for (let j = 0; j < sideResolution; j++, vertexPointer++) { - // Vertex index (0 - 1) - const iindex = i / (sideResolution - 1) - const jindex = j / (sideResolution - 1) - - // From the left and forward vectors, we can calculate an oriented vertex - const iv = divv3(mulv3(mulv3(this.left, iindex), radius), divisionLevel) - const jv = divv3(mulv3(mulv3(this.forward, jindex), radius), divisionLevel) - - // Add the scaled left and forward to the centered origin - const vertex = addv3(this.position, addv3(iv, jv)) - - // Normalize and multiply by radius to create a spherical mesh - const normal = normalv3(vertex) - const pointHeight = this.generator.noise.getNoise3D(this.level + 1, normal[0], normal[1], normal[2]) - const pos = mulv3(normal, (pointHeight - 1) + radius) - - vertices[vertexPointer * 3] = pos[0] - vertices[vertexPointer * 3 + 1] = pos[1] - vertices[vertexPointer * 3 + 2] = pos[2] - normals[vertexPointer * 3] = normal[0] - normals[vertexPointer * 3 + 1] = normal[1] - normals[vertexPointer * 3 + 2] = normal[2] - textureCoords[vertexPointer * 2] = j * (1 / sideResolution) - textureCoords[vertexPointer * 2 + 1] = i * (1 / sideResolution) - - if (i === Math.floor(sideResolution / 2) && j === Math.floor(sideResolution / 2)) { - this.center = pos - } - } - } - - // TODO: neighbor detection - let fanTop = false - let fanBottom = false - let fanLeft = false - let fanRight = false - for (let y = 0; y < sideResolution - 1; y++) { let slantLeft = (y % 2) === 0 for (let x = 0; x < sideResolution - 1; x++) { @@ -147,9 +84,97 @@ class CubeFace { } } - Mesh.generateNormals(normals, indices, vertices) + return { buffer: Mesh.loadToBuffer(Screen.gl, Screen.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices)), indices } + }, + setBuffer (mesh, buffer) { + mesh.ebo = buffer.buffer + mesh.indices = { length: buffer.indices.length } + } +} + +class CubeFace { + constructor (index, parent, level, pos, normal, generator) { + this.index = index + this.parent = parent + this.children = [] + + this.normal = normal + + this.level = level + + this.generated = false + this.generator = generator + + // Calculate left (x) and forward (z) vectors from the normal (y) + this.left = [normal[1], normal[2], normal[0]] + this.forward = crossv3(normal, this.left) + + this.position = pos + + // Center the face + if (level === 0) { + this.position = subv3(this.position, mulv3(this.left, generator.radius / 2)) + this.position = subv3(this.position, mulv3(this.forward, generator.radius / 2)) + } + + this.generate() + } + + generate () { + if (this.generated) return + + const sideResolution = this.generator.resolution + const vertices = [] + const normals = [] + const textureCoords = [] + + const radius = this.generator.radius + const divisionLevel = Math.pow(2, this.level) + + for (let i = 0, vertexPointer = 0; i < sideResolution; i++) { + for (let j = 0; j < sideResolution; j++, vertexPointer++) { + // Vertex index (0 - 1) + const iindex = i / (sideResolution - 1) + const jindex = j / (sideResolution - 1) + + // From the left and forward vectors, we can calculate an oriented vertex + const iv = divv3(mulv3(mulv3(this.left, iindex), radius), divisionLevel) + const jv = divv3(mulv3(mulv3(this.forward, jindex), radius), divisionLevel) + + // Add the scaled left and forward to the centered origin + const vertex = addv3(this.position, addv3(iv, jv)) + + // Normalize and multiply by radius to create a spherical mesh + const normal = normalv3(vertex) + const pointHeight = this.generator.noise.getNoise3D(this.level + 1, normal[0], normal[1], normal[2]) + const pos = mulv3(normal, pointHeight * 2 + radius) + + vertices[vertexPointer * 3] = pos[0] + vertices[vertexPointer * 3 + 1] = pos[1] + vertices[vertexPointer * 3 + 2] = pos[2] + normals[vertexPointer * 3] = normal[0] + normals[vertexPointer * 3 + 1] = normal[1] + normals[vertexPointer * 3 + 2] = normal[2] + textureCoords[vertexPointer * 2] = j * (1 / sideResolution) + textureCoords[vertexPointer * 2 + 1] = i * (1 / sideResolution) + + if (i === Math.floor(sideResolution / 2) && j === Math.floor(sideResolution / 2)) { + this.center = pos + } + } + } + + // TODO: neighbor detection + const indexBuffer = PlanetIndexBuffers.base + + // Generate normals for this mesh + Mesh.generateNormals(normals, PlanetIndexBuffers.base.indices, vertices) + + this.mesh = Mesh.construct(Screen.gl, vertices, null, textureCoords, normals) + + // Set the index buffer for this mesh to use + PlanetIndexBuffers.setBuffer(this.mesh, indexBuffer) - this.mesh = Mesh.construct(Screen.gl, vertices, indices, textureCoords, normals) this.generated = true } @@ -208,6 +233,8 @@ class CubeFace { return } + CubeFace.determineIndexBuffer(this) + this.mesh.prepare(gl, shader) this.mesh.draw(gl, shader) } @@ -231,6 +258,107 @@ class CubeFace { this.children[i].update(camera, dt) } } + + this.updateNeighbors() + } + + updateNeighbors () { + // TODO: planet face traversal + if (this.level === 0) return + switch (this.index) { + // Top left corner + case 0: + if (this.parent.neighborTop != null) { + this.neighborTop = this.parent.neighborTop.children[3] + } + this.neighborRight = this.parent.children[1] + this.neighborBottom = this.parent.children[3] + if (this.parent.neighborLeft != null) { + this.neighborLeft = this.parent.neighborLeft.children[1] + } + break + // Top right corner + case 1: + if (this.parent.neighborTop != null) { + this.neighborTop = this.parent.neighborTop.children[2] + } + if (this.parent.neighborRight != null) { + this.neighborRight = this.parent.neighborRight.children[0] + } + this.neighborBottom = this.parent.children[2] + this.neighborLeft = this.parent.children[0] + break + // Bottom right corner + case 2: + this.neighborTop = this.parent.children[1] + if (this.parent.neighborRight != null) { + this.neighborRight = this.parent.neighborRight.children[3] + } + if (this.parent.neighborBottom != null) { + this.neighborBottom = this.parent.neighborBottom.children[1] + } + this.neighborLeft = this.parent.children[3] + break + // Bottom left corner + case 3: + this.neighborTop = this.parent.children[0] + this.neighborRight = this.parent.children[2] + if (this.parent.neighborBottom != null) { + this.neighborBottom = this.parent.neighborBottom.children[0] + } + if (this.parent.neighborLeft != null) { + this.neighborLeft = this.parent.neighborLeft.children[2] + } + break + } + } + + static determineIndexBuffer (face) { + let buffer = PlanetIndexBuffers.base + if (face.level === 0) return PlanetIndexBuffers.setBuffer(face.mesh, buffer) + switch (face.index) { + // Top left corner + case 0: + if (face.neighborTop == null && face.neighborLeft == null) { + buffer = PlanetIndexBuffers.fixTL + } else if (face.neighborTop == null) { + buffer = PlanetIndexBuffers.fixT + } else if (face.neighborLeft == null) { + buffer = PlanetIndexBuffers.fixL + } + break + // Top right corner + case 1: + if (face.neighborTop == null && face.neighborRight == null) { + buffer = PlanetIndexBuffers.fixTR + } else if (face.neighborTop == null) { + buffer = PlanetIndexBuffers.fixT + } else if (face.neighborRight == null) { + buffer = PlanetIndexBuffers.fixR + } + break + // Bottom right corner + case 2: + if (face.neighborBottom == null && face.neighborRight == null) { + buffer = PlanetIndexBuffers.fixBR + } else if (face.neighborBottom == null) { + buffer = PlanetIndexBuffers.fixB + } else if (face.neighborRight == null) { + buffer = PlanetIndexBuffers.fixR + } + break + // Bottom left corner + case 3: + if (face.neighborBottom == null && face.neighborLeft == null) { + buffer = PlanetIndexBuffers.fixBL + } else if (face.neighborBottom == null) { + buffer = PlanetIndexBuffers.fixB + } else if (face.neighborLeft == null) { + buffer = PlanetIndexBuffers.fixL + } + break + } + PlanetIndexBuffers.setBuffer(face.mesh, buffer) } }