-
Notifications
You must be signed in to change notification settings - Fork 18
/
sector.go
144 lines (101 loc) · 4.18 KB
/
sector.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
137
138
139
140
141
142
143
144
package tetra3d
type SectorDetectionType int
const (
SectorDetectionTypeVertices = iota // If sector neighbors are detected by sharing vertex positions
SectorDetectionTypeAABB // If sector neighbors are detected by AABB
)
// Sector represents an area in a game (a sector), and is used for sector-based rendering.
// When a camera renders a scene with sector-based rendering enabled, the Camera will render
// only the objects within the current sector and any neighboring sectors, up to a customizeable
// depth.
// A Sector is, spatially, an AABB, which sits next to or within other Sectors (AABBs).
// Logically, a Sector is determined to be a neighbor of another Sector if they either intersect,
// or share vertex positions. Which of these is the case depends on the Sectors' SectorDetectionType.
type Sector struct {
Model *Model // The owning Model that forms the Sector
AABB *BoundingAABB // The AABB used to search for neighbors if the SectorDetectionType is set to SectorDetectionTypeAABB
Neighbors Set[*Sector] // The Sector's neighbors
SectorDetectionType SectorDetectionType // How the Sector is detected
rendering bool // If the Sector was rendering in the last Camera.Render____() call.
}
// NewSector creates a new Sector for the provided Model.
func NewSector(model *Model) *Sector {
mesh := model.Mesh
margin := 0.01
sectorAABB := NewBoundingAABB("sector", mesh.Dimensions.Width()+margin, mesh.Dimensions.Height()+margin, mesh.Dimensions.Depth()+margin)
sectorAABB.SetLocalPositionVec(model.WorldPosition().Add(mesh.Dimensions.Center()))
return &Sector{
Model: model,
AABB: sectorAABB,
Neighbors: newSet[*Sector](),
}
}
// Clone clones a Sector.
func (sector *Sector) Clone() *Sector {
newSector := &Sector{
Model: sector.Model,
AABB: sector.AABB.Clone().(*BoundingAABB),
Neighbors: make(Set[*Sector], len(sector.Neighbors)),
}
for n := range sector.Neighbors {
newSector.Neighbors[n] = struct{}{}
}
return newSector
}
// UpdateNeighbors updates the Sector's neighbors to refresh it, in case it moves. The Neighbors set is updated, not replaced,
// by this process (so clear the Sector's NeighborSet first if you need to do so).
func (sector *Sector) UpdateNeighbors(otherModels ...*Model) {
sector.AABB.updateSize()
for _, otherModel := range otherModels {
if otherModel == sector.Model || otherModel.sector == nil || sector.Neighbors.Contains(otherModel.sector) {
continue
}
otherModel.sector.AABB.updateSize()
switch sector.SectorDetectionType {
case SectorDetectionTypeVertices:
modelMatrix := sector.Model.Transform()
otherMatrix := otherModel.Transform()
exit:
for _, v := range sector.Model.Mesh.VertexPositions {
transformedV := modelMatrix.MultVec(v)
for _, v2 := range otherModel.Mesh.VertexPositions {
transformedV2 := otherMatrix.MultVec(v2)
if transformedV.Equals(transformedV2) {
sector.Neighbors.Add(otherModel.sector)
otherModel.sector.Neighbors.Add(sector)
break exit
}
}
}
case SectorDetectionTypeAABB:
if sector.AABB.Colliding(otherModel.sector.AABB) {
sector.Neighbors.Add(otherModel.sector)
otherModel.sector.Neighbors.Add(sector)
}
}
}
}
// Rendering returns if the Sector was rendering as of the last time a Camera rendered Nodes in its Scene.
// To be rendered, a Camera would have needed to be in the Sector, or in a neighboring sector within the
// camera's neighbor rendering range.
func (sector *Sector) Rendering() bool {
return sector.rendering
}
// NeighborsWithinRange returns the neighboring Sectors within a certain search range. For example,
// given the following example:
//
// # A - B - C - D - E - F
//
// If you were to check NeighborsWithinRange(2) from E, it would return F, D, and C.
func (sector *Sector) NeighborsWithinRange(searchRange int) Set[*Sector] {
out := newSet[*Sector]()
if searchRange > 0 {
out.Combine(sector.Neighbors)
for next := range sector.Neighbors {
out.Combine(next.NeighborsWithinRange(searchRange - 1))
}
}
// The sector itself is not a neighbor of itself
out.Remove(sector)
return out
}