Files
ngzz-mc/internal/raycast/raycast.go

75 lines
2.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
}