textured voxel terrain

This commit is contained in:
Evert Prants 2020-04-09 19:37:35 +03:00
parent ab35890f1b
commit 968c6ff3ad
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
9 changed files with 105 additions and 61 deletions

View File

@ -35,7 +35,7 @@ void main() {
}
totalDiffuse = max(totalDiffuse,0.2);
vec4 textureColor = texture2D(texture0, uv * 40.0);
vec4 textureColor = texture2D(texture0, uv);
if(textureColor.a<0.5){
discard;
}

BIN
assets/textures/voxel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -143,6 +143,8 @@ class Camera extends Node {
vec3.multiply(vec, this.right, velocity)
vec3.add(this.pos, this.pos, vec)
}
this.frustum.construct(mat4.multiply([], this.projection, this.view))
}
processMouseMove (offset, constrain = true) {

View File

@ -17,8 +17,7 @@ class PlanetGenerator {
return this.noise.getNoise3D(detail, normal[0], normal[1], normal[2])
}
getBiome (detail, normal) {
const heightAtPoint = this.getHeight(detail, normal)
getBiome (height, normal) {
// const moistureAtPoint = this.moisture.getNoise3D(5, normal)
// 0 - hot, 1 - cold
@ -27,12 +26,10 @@ class PlanetGenerator {
const poleTemp = 0.4
// TODO: pole settings for planet
const distanceFromPoles = Math.abs(vec3.dot(normal, [0.0, 1.0, 0.0]))
let e = heightAtPoint
// Calculate temperature for the poles
e = (e * e + poleTemp + (equatorTemp - poleTemp) * distanceFromPoles)
const e = (height * height + poleTemp + (equatorTemp - poleTemp) * distanceFromPoles)
if (heightAtPoint < 0.1) return [1 / 255, 56 / 255, 104 / 255] // Water
if (height < 0.1) return [1 / 255, 56 / 255, 104 / 255] // Water
else if (e <= 0.8) return [44 / 255, 50 / 255, 29 / 255] // Grass
else if (e < 0.9) return [50 / 255, 50 / 255, 50 / 255] // Stone
else return [1, 1, 1] // Snow
@ -176,7 +173,7 @@ class CubeFace {
const pointHeight = this.generator.getHeight(this.level + 1, normal)
const pos = mulv3(normal, pointHeight * 10 + radius)
const pointBiome = this.generator.getBiome(this.level + 1, normal)
const pointBiome = this.generator.getBiome(pointHeight, normal)
vertices[vertexPointer * 3] = pos[0]
vertices[vertexPointer * 3 + 1] = pos[1]
@ -255,6 +252,7 @@ class CubeFace {
}
draw (gl, shader) {
if (!this.visible) return
if (!this.mesh) {
for (const i in this.children) {
this.children[i].draw(gl, shader)
@ -262,16 +260,16 @@ class CubeFace {
return
}
if (!this.visible) return
CubeFace.determineIndexBuffer(this)
this.mesh.prepare(gl, shader)
if (this.mesh.cbuffer && shader.hasAttribute(gl, 'aColor')) {
gl.bindBuffer(gl.ARRAY_BUFFER, this.mesh.cbuffer)
shader.setAttribute(gl, 'aColor', 3, false, 3 * Float32Array.BYTES_PER_ELEMENT, 0)
this.mesh._bufferCount++
}
this.mesh.draw(gl, shader)
}

View File

@ -35,14 +35,14 @@ class DirectionalLight extends SpotLight {
}
class Environment {
constructor (ambient) {
constructor (ambient, sun) {
this.ambient = ambient
this.fogStart = 0
this.fogEnd = 0
this.fogColor = [0.8, 0.8, 0.8]
this.sun = new Light([0.0, 10000.0, -20000.0], [1.0, 1.0, 1.0])
this.sun = sun
this.lights = [this.sun]
this.maxEnvironmentLights = ENV_MAX_LIGHTS

View File

@ -84,6 +84,7 @@ class Material {
const tex = this.textures[i]
if (tex && tex instanceof Texture) {
gl.activeTexture(gl.TEXTURE0 + parseInt(i))
gl.bindTexture(tex.type, tex.id)
}
}

View File

@ -1,9 +1,9 @@
import { Mesh } from '../mesh'
import { Node, MeshInstance } from '../components'
import { MeshInstance } from '../components'
import { vec3 } from 'gl-matrix'
import { addv3, subv3, mulv3, dim3to1, dim1to3 } from '../utility'
import { BoundingBox } from '../mesh/aabb'
import { addv3, subv3, dim3to1 } from '../utility'
import VoxelData from './voxeldata'
import VoxelTexture from './voxeltexture'
const FACE_VERTEX = [
// Bottom
@ -24,34 +24,34 @@ const FACE_VERTEX = [
],
// Left
[
[0.0, 0.0, 1.0],
[0.0, 1.0, 1.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 1.0],
[0.0, 1.0, 1.0],
[-1.0, 0.0, 0.0]
],
// Right
[
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[1.0, 1.0, 1.0],
[1.0, 0.0, 1.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[1.0, 0.0, 0.0]
],
// Front
[
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 1.0, 1.0],
[0.0, 0.0, 1.0],
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 0.0, -1.0]
],
// Back
[
[0.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[1.0, 1.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]
]
]
@ -63,8 +63,10 @@ const FACE_RIGHT = 3
const FACE_FRONT = 4
const FACE_BACK = 5
const AIR_VOXEL = VoxelData.register('air', { solid: false })
const GROUND_VOXEL = VoxelData.register('ground', { solid: true })
const AIR = VoxelData.register('air', { solid: false })
const GRASS = VoxelData.register('grass', { solid: true, tiles: [0, 2, 1, 1, 1, 1] })
const DIRT = VoxelData.register('dirt', { solid: true, tiles: [0] })
const STONE = VoxelData.register('stone', { solid: true, tiles: [3] })
class VoxelChunk extends MeshInstance {
constructor (world, pos, size = 16) {
@ -85,6 +87,11 @@ class VoxelChunk extends MeshInstance {
// If the chunk is outside of the view, make it inactive
this.active = true
this.bounds = [
vec3.transformMat4([], [0.0, 0.0, 0.0], this.transform),
vec3.transformMat4([], [this.size, this.size, this.size], this.transform)
]
}
getVoxel (x, y, z) {
@ -95,41 +102,41 @@ class VoxelChunk extends MeshInstance {
if (neighbor) {
return neighbor.getVoxel(this.size + x, y, z)
}
return AIR_VOXEL
return AIR
} else if (x >= this.size) {
neighbor = this.world.getChunk(this.relativePos[0] + 1, this.relativePos[1], this.relativePos[2])
if (neighbor) {
return neighbor.getVoxel(x - this.size, y, z)
}
return AIR_VOXEL
return AIR
} else if (y < 0) {
neighbor = this.world.getChunk(this.relativePos[0], this.relativePos[1] - 1, this.relativePos[2])
if (neighbor) {
return neighbor.getVoxel(x, this.size + y, z)
}
return AIR_VOXEL
return AIR
} else if (y >= this.size) {
neighbor = this.world.getChunk(this.relativePos[0], this.relativePos[1] + 1, this.relativePos[2])
if (neighbor) {
return neighbor.getVoxel(x, y - this.size, z)
}
return AIR_VOXEL
return AIR
} else if (z < 0) {
neighbor = this.world.getChunk(this.relativePos[0], this.relativePos[1], this.relativePos[2] - 1)
if (neighbor) {
return neighbor.getVoxel(x, y, this.size + z)
}
return AIR_VOXEL
return AIR
} else if (z >= this.size) {
neighbor = this.world.getChunk(this.relativePos[0], this.relativePos[1], this.relativePos[2] + 1)
if (neighbor) {
return neighbor.getVoxel(x, y, z - this.size)
}
return AIR_VOXEL
return AIR
}
}
return this.data[dim3to1(x, y, z, this.size)] || AIR_VOXEL
return this.data[dim3to1(x, y, z, this.size)] || AIR
}
getVoxelv (v) {
@ -137,13 +144,18 @@ class VoxelChunk extends MeshInstance {
}
generate (generator) {
this.material = generator.material
this.material = VoxelTexture.material
for (let x = 0; x < this.size; x++) {
for (let z = 0; z < this.size; z++) {
const columnHeight = generator.getHeight(x + this.pos[0], z + this.pos[2])
const columnHeight = Math.floor(generator.getHeight(x + this.pos[0], z + this.pos[2]))
for (let y = 0; y < this.size; y++) {
const solid = this.pos[1] + y < columnHeight
this.data[dim3to1(x, y, z, this.size)] = solid ? GROUND_VOXEL : AIR_VOXEL
let voxel = AIR
if (this.pos[1] + y === columnHeight) voxel = GRASS
else if (this.pos[1] + y < columnHeight - 3) voxel = STONE
else if (this.pos[1] + y < columnHeight) voxel = DIRT
this.data[dim3to1(x, y, z, this.size)] = voxel
}
}
}
@ -154,7 +166,7 @@ class VoxelChunk extends MeshInstance {
// Programmatically generate a voxel face
// Returns the position, normal and texture coordinates for each vertex in this face
createFace (indices, points, pos, face) {
createFace (indices, points, pos, vId, face) {
// Add the corresponding offsets for this face to the position
const corners = [
addv3(pos, FACE_VERTEX[face][0]), addv3(pos, FACE_VERTEX[face][1]),
@ -163,14 +175,15 @@ class VoxelChunk extends MeshInstance {
// Select the normal for this face
const normal = FACE_VERTEX[face][4]
const uvs = VoxelData.textureIndex(vId, face)
// Create the 4 vertices that make up this face
// They're named points because this function returns not only vertices,
// but corresponding texture coordinates and normals at the same time for convenience
points.push([corners[0], normal, [0.0, 1.0]])
points.push([corners[1], normal, [0.0, 0.0]])
points.push([corners[2], normal, [1.0, 0.0]])
points.push([corners[3], normal, [1.0, 1.0]])
points.push([corners[0], normal, uvs[1]])
points.push([corners[1], normal, uvs[0]])
points.push([corners[2], normal, uvs[2]])
points.push([corners[3], normal, uvs[3]])
// Create the face
const inx = points.length - 4
@ -203,30 +216,31 @@ class VoxelChunk extends MeshInstance {
for (let y = 0; y < this.size; y++) {
for (let z = 0; z < this.size; z++) {
const cellPos = [x, y, z]
if (!VoxelData.isSolid(this.getVoxel(x, y, z))) continue
const vid = this.getVoxel(x, y, z)
if (!VoxelData.isSolid(vid)) continue
if (!VoxelData.isSolid(this.getVoxel(x, y - 1, z))) {
this.createFace(indices, points, cellPos, FACE_BOTTOM)
this.createFace(indices, points, cellPos, vid, FACE_BOTTOM)
}
if (!VoxelData.isSolid(this.getVoxel(x, y + 1, z))) {
this.createFace(indices, points, cellPos, FACE_TOP)
this.createFace(indices, points, cellPos, vid, FACE_TOP)
}
if (!VoxelData.isSolid(this.getVoxel(x - 1, y, z))) {
this.createFace(indices, points, cellPos, FACE_LEFT)
this.createFace(indices, points, cellPos, vid, FACE_LEFT)
}
if (!VoxelData.isSolid(this.getVoxel(x + 1, y, z))) {
this.createFace(indices, points, cellPos, FACE_RIGHT)
this.createFace(indices, points, cellPos, vid, FACE_RIGHT)
}
if (!VoxelData.isSolid(this.getVoxel(x, y, z + 1))) {
this.createFace(indices, points, cellPos, FACE_FRONT)
this.createFace(indices, points, cellPos, vid, FACE_FRONT)
}
if (!VoxelData.isSolid(this.getVoxel(x, y, z - 1))) {
this.createFace(indices, points, cellPos, FACE_BACK)
this.createFace(indices, points, cellPos, vid, FACE_BACK)
}
}
}
@ -262,10 +276,7 @@ class VoxelChunk extends MeshInstance {
}
update (gl, dt, camera) {
this.active = camera.frustum.containsBox(
vec3.transformMat4([], [0.0, 0.0, 0.0], this.transform),
vec3.transformMat4([], [this.size, this.size, this.size], this.transform)
)
if (this.bounds.length) this.active = camera.frustum.containsBox(this.bounds[0], this.bounds[1])
if (!this.active) return false
if (!this.generated) return this.generate(this.world.generator)
if (this.dirty) return this.createMesh(gl)
@ -352,8 +363,7 @@ class VoxelWorld {
}
class VoxelGenerator {
constructor (noise, material, groundHeight = 64) {
this.material = material
constructor (noise, groundHeight = 64) {
this.noise = noise
this.groundHeight = groundHeight
}

View File

@ -1,3 +1,6 @@
import VoxelTexture from './voxeltexture'
const MISSING_UV = [[0.0, 1.0], [0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]
const VoxelData = {
index: 0,
@ -12,15 +15,22 @@ const VoxelData = {
},
get: (name) => {
return VoxelData.registeredVoxels[name]
},
afterRegistration: () => {
},
isSolid: (id) => {
return VoxelData.indexCache[id] ? VoxelData.indexCache[id].solid : false
},
textureIndex: (id, face) => {
const icache = VoxelData.indexCache[id]
if (!icache.tiles) return MISSING_UV
if (icache.tiles.length === 1) return VoxelTexture.faceUVs(icache.tiles[0])
if (icache.tiles.length >= 2 && icache.tiles.length < 6) {
// top face
if (face === 1) {
return VoxelTexture.faceUVs(icache.tiles[0])
}
return VoxelTexture.faceUVs(icache.tiles[1])
}
return VoxelTexture.faceUVs(icache.tiles[face]) || MISSING_UV
}
}

View File

@ -1,9 +1,32 @@
import Screen from '../screen'
import { Texture, Material } from '../mesh/material'
function generateTextureMap (registeredVoxels) {
// body...
}
const clip = 1 / 100
const VoxelTexture = {
size: 0,
voxelSize: 0,
texture: null,
material: null,
loadFromFile: async (fileName, size) => {
VoxelTexture.texture = await Texture.fromFile(Screen.gl, fileName, true, Screen.gl.NEAREST, Screen.gl.CLAMP_TO_EDGE)
VoxelTexture.size = size
VoxelTexture.voxelSize = 1 / size
VoxelTexture.material = new Material([VoxelTexture.texture])
},
faceUVs: (textureIndex) => {
let x = textureIndex / VoxelTexture.size
let y = textureIndex - (x * VoxelTexture.size)
y = 1 - y - VoxelTexture.voxelSize
return [
[x + clip, y + clip],
[x + clip, y + VoxelTexture.voxelSize - clip],
[x - clip + VoxelTexture.voxelSize, y + clip],
[x - clip + VoxelTexture.voxelSize, y - clip + VoxelTexture.voxelSize]
]
}
}
export default VoxelTexture