spoint 0.1.602 → 0.1.603
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/terrain/SplineCarve.js +37 -11
package/package.json
CHANGED
|
@@ -6,6 +6,15 @@ function cellKey(cx, cz) {
|
|
|
6
6
|
return (cx + OFF) * BIG + (cz + OFF)
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
function carveHash(seed, i) {
|
|
10
|
+
let h = seed | 0
|
|
11
|
+
h = Math.imul(h ^ (i | 0), 0x27d4eb2d) >>> 0
|
|
12
|
+
h ^= h >>> 15; h = Math.imul(h, 0x2c1b3c6d) >>> 0
|
|
13
|
+
h ^= h >>> 12; h = Math.imul(h, 0x297a2d39) >>> 0
|
|
14
|
+
h ^= h >>> 15
|
|
15
|
+
return (h >>> 0) / 4294967296
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
function catmullRom(p0, p1, p2, p3, t) {
|
|
10
19
|
const getX = (p) => Array.isArray(p) ? p[0] : p.x
|
|
11
20
|
const getZ = (p) => Array.isArray(p) ? p[1] : p.z
|
|
@@ -134,30 +143,47 @@ export function createSplineCarveLayer() {
|
|
|
134
143
|
const cells = new Map()
|
|
135
144
|
const appliedSplines = []
|
|
136
145
|
|
|
137
|
-
function carve(controlPoints, width, depth, kind, baseHeightFn) {
|
|
146
|
+
function carve(controlPoints, width, depth, kind, baseHeightFn, opts = {}) {
|
|
138
147
|
if (!Array.isArray(controlPoints) || controlPoints.length < 2) return { touched: 0 }
|
|
139
148
|
if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(depth)) return { touched: 0 }
|
|
140
149
|
if (kind !== 'river' && kind !== 'road') return { touched: 0 }
|
|
150
|
+
const { seed = 0, widthVariance = 0, bankErosion = 0 } = opts
|
|
151
|
+
const isRiver = kind === 'river'
|
|
141
152
|
const stepSize = Math.max(1, width * 0.5)
|
|
142
153
|
const spinePoints = sampleSpline(controlPoints, stepSize)
|
|
143
154
|
if (spinePoints.length === 0) return { touched: 0 }
|
|
144
|
-
const
|
|
155
|
+
const maxHalfWidth = (width * 0.5) * (1 + (isRiver ? widthVariance : 0)) + (isRiver ? bankErosion : 0)
|
|
145
156
|
let touched = 0
|
|
146
|
-
|
|
157
|
+
let arcLen = 0
|
|
158
|
+
for (let si = 0; si < spinePoints.length; si++) {
|
|
159
|
+
const sp = spinePoints[si]
|
|
147
160
|
const sx = sp.x, sz = sp.z
|
|
148
|
-
const
|
|
149
|
-
|
|
161
|
+
if (si > 0) { const px = spinePoints[si - 1].x, pz = spinePoints[si - 1].z; arcLen += Math.hypot(sx - px, sz - pz) }
|
|
162
|
+
let halfWidth = width * 0.5
|
|
163
|
+
if (isRiver && widthVariance > 0) {
|
|
164
|
+
const t = carveHash(seed, Math.round(arcLen * 0.1))
|
|
165
|
+
halfWidth *= 1 + (t * 2 - 1) * widthVariance
|
|
166
|
+
}
|
|
167
|
+
const cx0 = Math.floor((sx - maxHalfWidth) / CELL_M), cx1 = Math.ceil((sx + maxHalfWidth) / CELL_M)
|
|
168
|
+
const cz0 = Math.floor((sz - maxHalfWidth) / CELL_M), cz1 = Math.ceil((sz + maxHalfWidth) / CELL_M)
|
|
150
169
|
for (let cz = cz0; cz <= cz1; cz++) {
|
|
151
170
|
for (let cx = cx0; cx <= cx1; cx++) {
|
|
152
171
|
const cMinX = cx * CELL_M, cMinZ = cz * CELL_M
|
|
172
|
+
const ccx = cMinX + CELL_M * 0.5, ccz = cMinZ + CELL_M * 0.5
|
|
173
|
+
let bankOffset = 0
|
|
174
|
+
if (isRiver && bankErosion > 0) {
|
|
175
|
+
const angle = carveHash(seed ^ 0x5bd1e995, cx * 92821 + cz) * Math.PI * 2
|
|
176
|
+
bankOffset = (carveHash(seed ^ 0x1b873593, cx * 15485863 + cz) * 2 - 1) * bankErosion * (0.5 + 0.5 * Math.cos(angle))
|
|
177
|
+
}
|
|
178
|
+
const cellHalfWidth = halfWidth + bankOffset
|
|
179
|
+
if (cellHalfWidth <= 0) continue
|
|
153
180
|
const nearestX = Math.max(cMinX, Math.min(sx, cMinX + CELL_M))
|
|
154
181
|
const nearestZ = Math.max(cMinZ, Math.min(sz, cMinZ + CELL_M))
|
|
155
182
|
const d = Math.hypot(nearestX - sx, nearestZ - sz)
|
|
156
|
-
if (d >
|
|
157
|
-
const ccx = cMinX + CELL_M * 0.5, ccz = cMinZ + CELL_M * 0.5
|
|
183
|
+
if (d > cellHalfWidth) continue
|
|
158
184
|
const dCenter = Math.hypot(ccx - sx, ccz - sz)
|
|
159
|
-
const falloffDist = Math.min(dCenter,
|
|
160
|
-
const falloff = 0.5 * (1 + Math.cos((falloffDist /
|
|
185
|
+
const falloffDist = Math.min(dCenter, cellHalfWidth)
|
|
186
|
+
const falloff = 0.5 * (1 + Math.cos((falloffDist / cellHalfWidth) * Math.PI))
|
|
161
187
|
const key = cellKey(cx, cz)
|
|
162
188
|
const base = typeof baseHeightFn === 'function' ? baseHeightFn(ccx, ccz) : null
|
|
163
189
|
const targetDelta = Number.isFinite(base) ? -depth * falloff : 0
|
|
@@ -169,7 +195,7 @@ export function createSplineCarveLayer() {
|
|
|
169
195
|
}
|
|
170
196
|
}
|
|
171
197
|
}
|
|
172
|
-
if (touched > 0) appliedSplines.push({ controlPoints: controlPoints.map(p => Array.isArray(p) ? [p[0], p[1]] : [p.x, p.z]), width, depth, kind })
|
|
198
|
+
if (touched > 0) appliedSplines.push({ controlPoints: controlPoints.map(p => Array.isArray(p) ? [p[0], p[1]] : [p.x, p.z]), width, depth, kind, seed, widthVariance, bankErosion })
|
|
173
199
|
return { touched }
|
|
174
200
|
}
|
|
175
201
|
|
|
@@ -224,7 +250,7 @@ export function loadSplineCarveLayer(json, baseHeightFn) {
|
|
|
224
250
|
if (json && Array.isArray(json.splines)) {
|
|
225
251
|
for (const s of json.splines) {
|
|
226
252
|
if (!s || !Array.isArray(s.controlPoints) || !Number.isFinite(s.width) || !Number.isFinite(s.depth)) continue
|
|
227
|
-
layer.carve(s.controlPoints, s.width, s.depth, s.kind, baseHeightFn)
|
|
253
|
+
layer.carve(s.controlPoints, s.width, s.depth, s.kind, baseHeightFn, { seed: s.seed ?? 0, widthVariance: s.widthVariance ?? 0, bankErosion: s.bankErosion ?? 0 })
|
|
228
254
|
}
|
|
229
255
|
}
|
|
230
256
|
return layer
|