feat: basic chunk system

This commit is contained in:
Florian Sylvain
2024-06-10 13:35:21 +02:00
parent 02e60a4e03
commit 89f656f2f3
3 changed files with 136 additions and 106 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

+119
View File
@@ -0,0 +1,119 @@
import { InstancedMesh, Texture, Vector3 } from "three"
import Face, { Orientation } from "./face"
const FACES_OFFSETS: { [key: string]: Vector3 } = {
Up: new Vector3(0, 1, 0),
North: new Vector3(0, 0, 1),
South: new Vector3(0, 0, -1),
East: new Vector3(-1, 0, 0),
West: new Vector3(1, 0, 0),
Down: new Vector3(0, -1, 0)
}
interface BlockData {
position: Vector3
textureName: string
}
export default class Chunk {
chunk: BlockData[]
chunkSet: Set<string>
radius: number
textures: { [name: string]: Texture[] }
faces: { [key: string]: Face[] }
constructor(radius: number, textures: { [name: string]: Texture[] }) {
this.chunk = []
this.chunkSet = new Set<string>()
this.radius = radius
this.textures = textures
this.faces = {}
this.generateChunk()
this.initializeFaces()
}
generateChunk() {
for (let i = 0; i < this.radius; i++) {
for (let j = 0; j < this.radius; j++) {
const max = Math.ceil(Math.sin(i / 30) * 10 + Math.cos(j / 30) * 10)
for (let k = -16; k <= max; k++) {
const position = new Vector3(i, k, j)
let textureName = "stone"
if (k === max) textureName = "grass"
else if (k > max - 6) textureName = "dirt"
this.chunk.push({ position, textureName })
this.chunkSet.add(position.toArray().toString())
}
}
}
}
initializeFaces() {
for (const textureName in this.textures) {
const textureFaces = this.textures[textureName].map((texture, index) => {
const orientation = ["Up", "North", "South", "East", "West", "Down"][
index
] as Orientation
return new Face(orientation, 0, texture)
})
this.faces[textureName] = textureFaces
}
}
filterFaces(block: Vector3, textureName: string): Face[] {
return this.faces[textureName].filter((face) => {
const offset = FACES_OFFSETS[face.orientation]
const adjacentBlock = new Vector3(
block.x + offset.x,
block.y + offset.y,
block.z + offset.z
)
return !this.chunkSet.has(adjacentBlock.toArray().toString())
})
}
countVisibleFaces() {
const faceCounts: { [key: string]: { [key: string]: number } } = {}
this.chunk.forEach(({ position, textureName }) => {
if (!faceCounts[textureName]) {
faceCounts[textureName] = {
Up: 0,
North: 0,
South: 0,
East: 0,
West: 0,
Down: 0
}
}
Object.keys(FACES_OFFSETS).forEach((orientation) => {
const offset = FACES_OFFSETS[orientation]
const adjacentBlock = new Vector3(
position.x + offset.x,
position.y + offset.y,
position.z + offset.z
)
if (!this.chunkSet.has(adjacentBlock.toArray().toString())) {
faceCounts[textureName][orientation]++
}
})
})
for (const textureName in faceCounts) {
for (const orientation in faceCounts[textureName]) {
const face = this.faces[textureName].find(
(f) => f.orientation === orientation
)
if (!face?.instancedMesh) return
face.instancedMesh.count = faceCounts[textureName][orientation]
face.instancedMesh = new InstancedMesh(
face?.geometry,
face?.material,
faceCounts[textureName][orientation]
) // TODO dégueulasse
}
}
return faceCounts
}
}
+16 -105
View File
@@ -1,10 +1,9 @@
import "./style.css" import "./style.css"
import * as THREE from "three" import * as THREE from "three"
import { OrbitControls } from "three/addons/controls/OrbitControls.js" import { OrbitControls } from "three/addons/controls/OrbitControls.js"
import Face from "./face" import Chunk from "./chunk"
import Block from "./block" import Block from "./block"
import TextureLoader from "./textureLoader" import TextureLoader from "./textureLoader"
// import TextureLoader from "./textureLoader"
const scene = new THREE.Scene() const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera( const camera = new THREE.PerspectiveCamera(
@@ -21,121 +20,33 @@ const controls = new OrbitControls(camera, renderer.domElement)
controls.update() controls.update()
document.body.appendChild(renderer.domElement) document.body.appendChild(renderer.domElement)
let grassTextures = [] as THREE.Texture[] let textures: { [name: string]: THREE.Texture[] } = {}
let dirtTextures = [] as THREE.Texture[]
async function loadTextures() { async function loadTextures() {
const textureLoader = new TextureLoader(16, 16) const textureLoader = new TextureLoader(16, 16)
grassTextures = await textureLoader.load("/grass.png") textures["grass"] = await textureLoader.load("/grass.png")
dirtTextures = await textureLoader.load("/dirt.png") textures["dirt"] = await textureLoader.load("/dirt.png")
// const debugTextures = await textureLoader.load("/debug.png") textures["stone"] = await textureLoader.load("/stone.png")
onTextureLoaded() onTexturesLoaded()
} }
// const gridHelper = new THREE.GridHelper(1000, 1000) function onTexturesLoaded() {
// scene.add(gridHelper) const radius = 128
const chunk = new Chunk(radius, textures)
// const axesHelper = new THREE.AxesHelper(500) chunk.countVisibleFaces()
// scene.add(axesHelper)
const FACES_OFFSETS: { [key: string]: THREE.Vector3 } = {
Up: new THREE.Vector3(0, 1, 0),
North: new THREE.Vector3(0, 0, 1),
South: new THREE.Vector3(0, 0, -1),
East: new THREE.Vector3(-1, 0, 0),
West: new THREE.Vector3(1, 0, 0),
Down: new THREE.Vector3(0, -1, 0)
}
function filterFaces(
faces: Face[],
block: THREE.Vector3,
chunkSet: Set<string>
): Face[] {
return faces.filter((face) => {
const offset = FACES_OFFSETS[face.orientation]
const adjacentBlock = new THREE.Vector3(
block.x + offset.x,
block.y + offset.y,
block.z + offset.z
)
return !chunkSet.has(adjacentBlock.toArray().toString())
})
}
function countVisibleFaces(
chunkSet: Set<string>,
chunk: THREE.Vector3[]
): { [key: string]: number } {
const faceCounts: { [key: string]: number } = {
Up: 0,
North: 0,
South: 0,
East: 0,
West: 0,
Down: 0
}
chunk.forEach((block) => {
Object.keys(FACES_OFFSETS).forEach((orientation) => {
const offset = FACES_OFFSETS[orientation]
const adjacentBlock = new THREE.Vector3(
block.x + offset.x,
block.y + offset.y,
block.z + offset.z
)
if (!chunkSet.has(adjacentBlock.toArray().toString())) {
faceCounts[orientation]++
}
})
})
return faceCounts
}
function onTextureLoaded() {
const chunk = [] as THREE.Vector3[]
const chunkSet = new Set<string>()
const radius = 32
for (let i = 0; i < radius; i++) {
for (let j = 0; j < radius; j++) {
const max = Math.ceil(Math.sin(i / 30) * 10 + Math.cos(j / 30) * 10)
for (let k = -64; k < max; k++) {
const vec3 = new THREE.Vector3(i, k, j)
chunk.push(vec3)
chunkSet.add(vec3.toArray().toString())
}
}
}
const faceCounts = countVisibleFaces(chunkSet, chunk)
const grassFaces = [
new Face("Up", faceCounts.Up, grassTextures[0]),
new Face("North", faceCounts.North, grassTextures[1]),
new Face("South", faceCounts.South, grassTextures[2]),
new Face("East", faceCounts.East, grassTextures[3]),
new Face("West", faceCounts.West, grassTextures[4]),
new Face("Down", faceCounts.Down, grassTextures[5])
]
const dirtFaces = [
new Face("Up", faceCounts.Up, dirtTextures[0]),
new Face("North", faceCounts.North, dirtTextures[1]),
new Face("South", faceCounts.South, dirtTextures[2]),
new Face("East", faceCounts.East, dirtTextures[3]),
new Face("West", faceCounts.West, dirtTextures[4]),
new Face("Down", faceCounts.Down, dirtTextures[5])
]
const cubes = [] as Block[] const cubes = [] as Block[]
chunk.forEach((block) => { chunk.chunk.forEach(({ position, textureName }) => {
cubes.push(new Block(filterFaces(grassFaces, block, chunkSet), block)) const faces = chunk.filterFaces(position, textureName)
cubes.push(new Block(faces, position))
}) })
grassFaces.forEach((face) => { Object.values(chunk.faces).forEach((faceArray) => {
faceArray.forEach((face) => {
if (face.instancedMesh) scene.add(face.instancedMesh) if (face.instancedMesh) scene.add(face.instancedMesh)
}) })
})
} }
camera.position.x = 15 camera.position.x = 15