/
geo.go
215 lines (191 loc) · 6.85 KB
/
geo.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package geo
import (
"log"
"math"
"sort"
"time"
"github.com/Flokey82/genworldvoronoi/spheremesh"
"github.com/Flokey82/genworldvoronoi/various"
"github.com/Flokey82/go_gens/vectors"
)
type Geo struct {
*GeoConfig // Geo configuration
*Calendar
*BaseObject
*Resources // Natural resources.
PlateToVector []vectors.Vec3 // Plate tectonics / movement vectors
PlateIsOcean map[int]bool // Plate was chosen to be an ocean plate
PlateRegs []int // Plate seed points / regions
RegionToWindVec [][2]float64 // Point / region wind vector
RegionToWindVecLocal [][2]float64 // Point / region wind vector (local)
RegionToOceanVec [][2]float64 // Point / region ocean current vector
RegionToPlate []int // Point / region to plate mapping
Ocean_r []int // Ocean regions
Mountain_r []int // Mountain regions
Coastline_r []int // Coastline regions
AvgInsolation []float64 // Average daily insolation values
QuadGeom *QuadGeometry // Quad geometry generated from the mesh (?)
}
func NewGeo(seed int64, cfg *GeoConfig) (*Geo, error) {
if cfg == nil {
cfg = NewGeoConfig()
}
result, err := spheremesh.MakeSphere(seed, cfg.NumPoints, cfg.Jitter)
if err != nil {
return nil, err
}
return &Geo{
GeoConfig: cfg,
Calendar: NewCalendar(),
PlateIsOcean: make(map[int]bool),
BaseObject: newBaseObject(seed, result),
Resources: newResources(result.NumRegions),
RegionToWindVec: make([][2]float64, result.NumRegions),
RegionToWindVecLocal: make([][2]float64, result.NumRegions),
RegionToOceanVec: make([][2]float64, result.NumRegions),
QuadGeom: NewQuadGeometry(result.TriangleMesh),
}, nil
}
func (m *Geo) GenerateGeology() {
// Generate tectonic plates.
start := time.Now()
m.generatePlates()
m.assignOceanPlates()
log.Println("Done plates in ", time.Since(start).String())
// Calculate elevation.
start = time.Now()
m.assignRegionElevation()
log.Println("Done elevation in ", time.Since(start).String())
// Calculate wind vectors.
start = time.Now()
m.assignWindVectors()
log.Println("Done wind vectors in ", time.Since(start).String())
// Assign rainfall, moisture.
start = time.Now()
m.assignRainfallBasic()
// m.assignRainfall(1, moistTransferIndirect, moistOrderWind)
// m.assignFlux()
log.Println("Done rainfall in ", time.Since(start).String())
// Hydrology (based on regions) - EXPERIMENTAL
start = time.Now()
// m.assignHydrologyWithFlooding()
m.assignHydrology()
// m.getRivers(9000.1)
// m.r_elevation = m.rErode(0.05)
log.Println("Done hydrology in ", time.Since(start).String())
// Now that water is assigned, we can make note of waterbodies.
// NOTE: Lake sizes are assigned in assignHydrology etc.
start = time.Now()
m.assignWaterbodies()
log.Println("Done waterbodies in ", time.Since(start).String())
// Note waterfalls.
start = time.Now()
m.assignWaterfalls()
log.Println("Done waterfalls in ", time.Since(start).String())
// Place resources
start = time.Now()
m.placeResources()
log.Println("Done placing resources in ", time.Since(start).String())
// Hydrology (based on triangles)
// Amit's hydrology code.
start = time.Now()
m.assignTriValues()
// m.assignDownflow()
// m.assignFlow()
log.Println("Done triangles in ", time.Since(start).String())
// Quad geometry update.
// This is really only useful for rendering the map but we don't
// really use this right now.
start = time.Now()
m.QuadGeom.setMap(m.SphereMesh.TriangleMesh, m)
log.Println("Done quadgeom in ", time.Since(start).String())
// Identify continents / landmasses.
start = time.Now()
m.assignLandmasses()
log.Println("Done identify landmasses in ", time.Since(start).String())
// Update the biome regions.
// This will be interesting to determine place names, impact on
// pathfinding (navigating around difficult terrain), etc.
start = time.Now()
m.assignBiomeRegions()
log.Println("Done biome regions in ", time.Since(start).String())
// Assign ocean currents.
start = time.Now()
// m.assignOceanCurrents()
m.assignOceanCurrents3()
log.Println("Done ocean currents in ", time.Since(start).String())
// Hacky: Generate temperatures.
transportTemp := true
start = time.Now()
// TODO: Do iterative steps since the water temperature will influence
// the air temperature and vice versa.
m.initRegionWaterTemperature()
m.initRegionAirTemperature()
if transportTemp {
m.transportRegionWaterTemperature()
m.assignRegionAirTemperature()
}
log.Println("Done temperatures in ", time.Since(start).String())
// Average daily insolation. (currently with a set day of year)
start = time.Now()
m.AvgInsolation = m.GetAverageInsolation(90)
log.Println("Done insolation in ", time.Since(start).String())
}
func (m *Geo) Tick() {
// Advance the calendar.
m.Calendar.Tick()
}
// GetCustomContour returns a countour by tracing the region borders determined
// by the function. The result is a list of sequential triangle center points.
// Each sequence represents a continous border around regions of the same type.
//
// The function returns true if the region borders should be traced / if
// the regions do not belong to the same group of regions.
func (m *Geo) GetCustomContour(f func(idxA, idxB int) bool) [][]int {
var edges [][2]int
seen := make(map[[2]int]bool)
for i := 0; i < len(m.SphereMesh.Halfedges); i++ {
idxA := m.SphereMesh.S_begin_r(i)
idxB := m.SphereMesh.S_end_r(i)
var vx [2]int
if idxA > idxB {
vx[0] = m.SphereMesh.S_outer_t(i)
vx[1] = m.SphereMesh.S_inner_t(i)
} else {
vx[0] = m.SphereMesh.S_inner_t(i)
vx[1] = m.SphereMesh.S_outer_t(i)
}
if seen[vx] {
continue
}
seen[vx] = true
if f(idxA, idxB) {
edges = append(edges, vx)
}
}
return various.MergeIndexSegments(edges)
}
// GetVectorSortOrder returns a list of regions sorted by their vector order.
// This allows us to sort regions "up wind" or "down wind", for example.
func (m *Geo) GetVectorSortOrder(vecs [][2]float64, reverse bool) ([]float64, []int) {
orderedRegs := make([]int, m.SphereMesh.NumRegions) // sorted regions
regSort := make([]float64, m.SphereMesh.NumRegions) // numeric sort order
for r := 0; r < m.SphereMesh.NumRegions; r++ {
lat := (m.LatLon[r][0]) * vecs[r][1] / math.Abs(vecs[r][1])
lon := (m.LatLon[r][1]) * vecs[r][0] / math.Abs(vecs[r][0])
regSort[r] = (lat + lon)
orderedRegs[r] = r
}
// Sort the indices in vector-order so we can ensure that we push the moisture
// in their logical sequence across the globe.
if reverse {
sort.Slice(orderedRegs, func(a, b int) bool {
return regSort[orderedRegs[a]] > regSort[orderedRegs[b]]
})
} else {
sort.Slice(orderedRegs, func(a, b int) bool {
return regSort[orderedRegs[a]] < regSort[orderedRegs[b]]
})
}
return regSort, orderedRegs
}