gl01/internal/world/world.go

168 lines
3.7 KiB
Go
Raw Permalink Normal View History

2022-01-20 21:58:50 +08:00
package world
import (
"fmt"
"io"
"edgaru089.ink/go/gl01/internal/util/itype"
2022-01-20 21:58:50 +08:00
)
// World holds a number of Chunks and Entities.
type World struct {
Chunks map[itype.Vec2i]*Chunk
Seed int32
}
// NewWorld creates a new, empty, world with no chunks.
func NewWorld() *World {
var w World
w.Chunks = make(map[itype.Vec2i]*Chunk)
return &w
}
// The default World.
var DefaultWorld World
func init() {
DefaultWorld.Chunks = make(map[itype.Vec2i]*Chunk)
}
// Chunk returns the chunk at [x, z], or nil if nonexistent.
func (w *World) Chunk(x, z int) *Chunk {
return w.Chunks[itype.Vec2i{x, z}]
}
// SetChunk sets the chunk [x, z] to c.
func (w *World) SetChunk(x, z int, c *Chunk) {
if old, ok := w.Chunks[itype.Vec2i{x, z}]; ok {
old.FreeRender()
}
c.X = x
c.Z = z
c.renderChanged = true
2022-01-28 15:24:03 +08:00
c.world = w
2022-01-20 21:58:50 +08:00
w.Chunks[itype.Vec2i{x, z}] = c
c.InitRender()
}
func BlockPosToChunk(blockpos itype.Vec3i) (x, z int) {
if blockpos[0] >= 0 {
x = blockpos[0] / ChunkSizeX
} else {
x = (blockpos[0]+1)/ChunkSizeX - 1
}
if blockpos[2] >= 0 {
z = blockpos[2] / ChunkSizeZ
} else {
z = (blockpos[2]+1)/ChunkSizeZ - 1
}
return
}
func BlockPosToInChunk(blockpos itype.Vec3i) (x, y, z int) {
if blockpos[0] >= 0 {
x = blockpos[0] % ChunkSizeX
} else {
x = ChunkSizeX + (blockpos[0] % ChunkSizeX)
if x == ChunkSizeX {
x = 0
}
}
y = blockpos[1]
if blockpos[2] >= 0 {
z = blockpos[2] % ChunkSizeZ
} else {
z = ChunkSizeZ + (blockpos[2] % ChunkSizeZ)
if z == ChunkSizeZ {
z = 0
}
}
return
}
2022-02-24 20:20:33 +08:00
// Break breaks the block.
func (w *World) Break(pos itype.Vec3i) {
cx, cz := BlockPosToChunk(pos)
cix, ciy, ciz := BlockPosToInChunk(pos)
c, ok := w.Chunks[itype.Vec2i{cx, cz}]
if !ok || ciy < 0 || ciy >= ChunkSizeY {
return
}
c.SetBlock(cix, ciy, ciz, 0, 0)
}
2022-01-20 21:58:50 +08:00
// Block returns the block at the position [pos], or an empty Block{} if it does not exist.
func (w *World) Block(pos itype.Vec3i) Block {
cx, cz := BlockPosToChunk(pos)
cix, ciy, ciz := BlockPosToInChunk(pos)
c, ok := w.Chunks[itype.Vec2i{cx, cz}]
if !ok || ciy < 0 || ciy >= ChunkSizeY {
return Block{}
}
return Block{
Id: int(c.Id[cix][ciy][ciz]),
Aux: int(c.Aux[cix][ciy][ciz]),
Dataset: nil,
Behaviour: GetBlockBehaviour(int(c.Id[cix][ciy][ciz])),
}
}
2022-01-28 15:24:03 +08:00
// SetBlock sets the block at the given position, returning its Block.
//
// It fails if the chunk is not loaded, and an empty Block{} is returned.
func (w *World) SetBlock(pos itype.Vec3i, id, aux int, dataset itype.Dataset) Block {
cx, cz := BlockPosToChunk(pos)
cix, ciy, ciz := BlockPosToInChunk(pos)
c, ok := w.Chunks[itype.Vec2i{cx, cz}]
if !ok || ciy < 0 || ciy >= ChunkSizeY {
return Block{}
}
c.SetBlock(cix, ciy, ciz, id, aux)
return Block{
Id: int(c.Id[cix][ciy][ciz]),
Aux: int(c.Aux[cix][ciy][ciz]),
Dataset: nil,
Behaviour: GetBlockBehaviour(int(c.Id[cix][ciy][ciz])),
}
}
2022-01-20 21:58:50 +08:00
// LoadChunkFromGob loads (or overwrites) chunk [id.x, id.z] from the Gob-encoding file.
func (w *World) LoadChunkFromGob(id itype.Vec2i, file io.Reader) (err error) {
c := &Chunk{}
if err != nil {
return fmt.Errorf("World: load chunk (%d,%d) from gob file: %s", id[0], id[1], err)
}
if old, ok := w.Chunks[id]; ok {
old.FreeRender()
}
c.LoadFromGobIndexed(file, id[0], id[1])
w.Chunks[id] = c
c.InitRender()
return nil
}
// Render should be called from WorldRenderer, setting shader and texture context
// for the Render function, which merely calls BindVertexArray and DrawArrays.
func (w *World) Render() {
for _, c := range w.Chunks {
c.Render()
}
}
// RenderWater calls DrawArrays drawing the semi-transparant layer of the world.
func (w *World) RenderWater() {
for _, c := range w.Chunks {
c.RenderWater()
}
}