-
Notifications
You must be signed in to change notification settings - Fork 5
/
world.go
136 lines (119 loc) · 2.87 KB
/
world.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package chunk
import (
"fmt"
"log"
"sync"
"github.com/johanhenriksson/goworld/core/camera"
"github.com/johanhenriksson/goworld/core/object"
"github.com/johanhenriksson/goworld/math"
"github.com/johanhenriksson/goworld/math/vec3"
"github.com/johanhenriksson/goworld/physics"
)
type World struct {
object.Object
size int
distance float32
generator Generator
lock *sync.Mutex
active map[string]object.Component
ready chan chunkSpawn
}
type chunkSpawn struct {
Chunk *T
Key string
Position vec3.T
}
// Builds a world of chunks around the active camera as it moves around
func NewWorld(size int, generator Generator, distance float32) *World {
return object.New("World", &World{
size: size,
generator: generator,
distance: distance,
active: make(map[string]object.Component, 100),
ready: make(chan chunkSpawn, 100),
lock: &sync.Mutex{},
})
}
func (c *World) Update(scene object.Component, dt float32) {
c.lock.Lock()
defer c.lock.Unlock()
// update chunks
c.Object.Update(scene, dt)
// insert any new chunks
select {
case spawn := <-c.ready:
chonk := object.Builder(object.Empty(spawn.Key)).
Attach(NewMesh(spawn.Chunk)).
Attach(physics.NewRigidBody(0)).
Attach(physics.NewMesh()).
Position(spawn.Position).
Parent(c).
Create()
c.active[spawn.Key] = chonk
default:
}
// find the active camera
root := object.Root(scene)
cam := object.GetInChildren[*camera.Camera](root)
if cam == nil {
log.Println("chunk world: no active camera")
return
}
pos := cam.Transform().WorldPosition()
pos.Y = 0
// destroy chunks that are too far away
for key, chunk := range c.active {
if chunk == nil {
// being loaded
continue
}
dist := vec3.Distance(pos, chunk.Transform().Position())
if dist > c.distance*1.1 {
log.Println("destroy chunk", key)
chunk.Destroy()
delete(c.active, key)
}
}
// create chunks close to us
chunkPos := pos.Scaled(1 / float32(c.size)).Floor()
cx, cz := int(chunkPos.X), int(chunkPos.Z)
steps := int(c.distance / float32(c.size))
minDist := math.InfPos
var spawn func()
for x := cx - steps; x < cx+steps; x++ {
for z := cz - steps; z < cz+steps; z++ {
// check if the chunk would have been in range
p := vec3.NewI(x*c.size, 0, z*c.size)
dist := vec3.Distance(pos, p)
if dist > c.distance {
continue
}
// check if its already active
key := fmt.Sprintf("Chunk:%d,%d", x, z)
_, active := c.active[key]
if active {
continue
}
// spawn it
if dist < minDist {
minDist = dist
ix, iz := x, z
spawn = func() {
log.Println("spawn chunk", key)
c.lock.Lock()
c.active[key] = nil
c.lock.Unlock()
chunkData := Generate(c.generator, c.size, ix, iz)
c.ready <- chunkSpawn{
Chunk: chunkData,
Key: key,
Position: p,
}
}
}
}
}
if spawn != nil {
go spawn()
}
}