75 lines
2.3 KiB
Go
75 lines
2.3 KiB
Go
// Package raycast 体素射线拾取:DDA 步进(场景/方块交互与动画.md §2)。
|
||
//
|
||
// 返回三元组:hitBlock(被击中方块)、faceNormal(击中面法线)、prevBlock(放置位)。
|
||
// 客户端与服务器共用同一实现,保证双端判定一致。
|
||
package raycast
|
||
|
||
import "math"
|
||
|
||
// Hit 射线命中结果。
|
||
type Hit struct {
|
||
X, Y, Z int32 // 被击中的方块坐标
|
||
Face [3]int32 // 击中面法线(±1 单位向量)
|
||
Prev [3]int32 // 放置位 = 被击中方块 - 面法线
|
||
Dist float64 // 命中距离
|
||
}
|
||
|
||
// Solid 方块可交互判定回调(用注册表 IsSolid 等)。
|
||
type Solid func(x, y, z int32) bool
|
||
|
||
// Cast 从 origin 沿 dir 做 DDA 体素步进,最大距离 maxDist,返回首个 solid 命中。
|
||
// dir 应为单位向量;分量接近 0 时对应轴 tDelta 取 +Inf(不会在该轴步进)。
|
||
func Cast(origin, dir [3]float64, maxDist float64, solid Solid) (Hit, bool) {
|
||
x, y, z := int32(math.Floor(origin[0])), int32(math.Floor(origin[1])), int32(math.Floor(origin[2]))
|
||
// 基准取整值(负数安全:不能用 int32() 截断,-0.5 会被截成 0)
|
||
fx, fy, fz := math.Floor(origin[0]), math.Floor(origin[1]), math.Floor(origin[2])
|
||
|
||
var step [3]int32
|
||
var tDelta, tMax [3]float64
|
||
for i := 0; i < 3; i++ {
|
||
base := [3]float64{fx, fy, fz}[i]
|
||
switch {
|
||
case dir[i] > 0:
|
||
step[i] = 1
|
||
tDelta[i] = 1 / dir[i]
|
||
tMax[i] = (base + 1 - origin[i]) / dir[i]
|
||
case dir[i] < 0:
|
||
step[i] = -1
|
||
tDelta[i] = -1 / dir[i]
|
||
tMax[i] = (origin[i] - base) / (-dir[i])
|
||
default:
|
||
step[i] = 0
|
||
tDelta[i] = math.Inf(1)
|
||
tMax[i] = math.Inf(1)
|
||
}
|
||
}
|
||
|
||
var face [3]int32
|
||
dist := 0.0
|
||
for dist <= maxDist {
|
||
if solid(x, y, z) {
|
||
// 当前格命中:面法线指向射线来向(背离方块),放置位 = 命中格 + 面法线
|
||
return Hit{X: x, Y: y, Z: z, Face: face, Prev: [3]int32{x + face[0], y + face[1], z + face[2]}, Dist: dist}, true
|
||
}
|
||
// 选择 tMax 最小的轴步进
|
||
switch {
|
||
case tMax[0] < tMax[1] && tMax[0] < tMax[2]:
|
||
dist = tMax[0]
|
||
x += step[0]
|
||
tMax[0] += tDelta[0]
|
||
face = [3]int32{-step[0], 0, 0}
|
||
case tMax[1] < tMax[2]:
|
||
dist = tMax[1]
|
||
y += step[1]
|
||
tMax[1] += tDelta[1]
|
||
face = [3]int32{0, -step[1], 0}
|
||
default:
|
||
dist = tMax[2]
|
||
z += step[2]
|
||
tMax[2] += tDelta[2]
|
||
face = [3]int32{0, 0, -step[2]}
|
||
}
|
||
}
|
||
return Hit{}, false
|
||
}
|