4面体に分解して計算するのが単純明快な解決法。
Godotにはドロニー分割法を使って凸形形状を4面体に分割するGeometry3D.tetrahedralize_delaunay()
という関数が用意されているのでこれを使う。
コード
static func calc_convex_volume(points: PackedVector3Array) -> float:
# 4面体を構成する頂点の入力配列内でのインデックスが代入される。
var tetrahedrons := Geometry3D.tetrahedralize_delaunay(points)
if tetrahedrons.is_empty(): # 4面体化失敗
return 0 # お好みでエラーを出したり、NANを返しても良いでしょう。
var volume := 0.0
for i in tetrahedrons.size():
if i % 4 != 0:
continue
var a := points[tetrahedrons[i]]
var b := points[tetrahedrons[i + 1]]
var c := points[tetrahedrons[i + 2]]
var d := points[tetrahedrons[i + 3]]
# 頂点から4面体の体積を計算して加算。
volume += abs((a - d).dot((b - d).cross(c - d))) / 6
return volume
使用例
extends ConvexPolygonShape3D
func get_volume() -> float:
return calc_convex_volume(points)
頂点を与えるだけで計算できるので、物理系以外でも使えそう。
注意点
Geometry3D.tetrahedralize_delaunay()
は頂点数が増えると途端に遅くなるので、スタッターを発生させたくない場合はスレッドで計算してキャッシュするなどの工夫が必要になる。