textured voxel terrain
This commit is contained in:
parent
ab35890f1b
commit
968c6ff3ad
@ -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
BIN
assets/textures/voxel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user