Landscapeに、動的生成したHeightmapを適用するための、長い闘いの記録。
-----
地形に起伏を付けたい。
まずはLandscapeの実装を思い出すためにソースを見返す。
少しリファクタリングが必要だなぁ・・・
ソース整理
方針としては
マテリアルのウエイトマップを書き込む時にやったように
ハイトマップデータをバッファで保持して、テクスチャを生成すればいいはず。
とりあえず適当な値を放り込んで、テクスチャを更新してみる。
RGBA8の内訳ってなに?
R = Heightの上位8bit
G = Heightの下位8bit
B = Normal.X
A = Normal.Y
Normal はこの計算式
FMath::RoundToInt(127.5f * (Normal.X + 1.0f))
出来た。
・・・
けれど近づくと、地形がにょきにょき生える。
LODかな?
コリジョンも見た目通りになっていない。
コリジョンは別に更新が必要なのか。
----
なるほど、LandscapeのLODはミップマップを利用しているようだ。
ミップマップの更新も必要。
LandscapeEdit.cpp の ULandscapeComponent::GenerateHeightmapMips() を参考に
ミップマップを生成する処理を書く。
めんどい。
WITH_EDITOR外してくれればいいのに。。。
・・・
出来たー。
・・・
けれど、今度は表示がパカパカとちらつくようになった。
カリングかな?
きっとバウンディングボックスの更新をすれば治るだろう。
・・・
LandscapeComponent の CachedLocalBox を更新して、UpdateComponentToWorld() を呼ぶ。
出来たーー。
あとはコリジョンか・・。
----
コリジョンの更新は ULandscapeComponent::UpdateCollisionHeightData()
こりゃまた長いソースだなぁ・・・。
心が折れる。
これも WITH_EDITOR。
この関数の要点はなんだろう?
・・・
UpdateCollisionHeightBuffer() かな。
移植してみる。
んー、移植してみたけど、これはエディタ用のバッファを作っているだけだ。
・・・
本体は、こっちだ。
UpdateHeightfieldRegion()
Physx用のデータを更新している。
・・・
出来たーーー。
草の高さが更新されなかったので、一旦草はオフにした。
草の高さはまた別に更新が必要なのか。
建物も現状コリジョンを見ずに配置してしまっているので、
宙に浮いたり、地面に埋まったりしてしまっているけれども、
ひとまず地形に起伏を付けることはできた!
----
・・・
んーー?
移動していると、ガクガク上下に震えたり、当たり抜けして下に落ちたりする場所がある。
viewmode CollisionVis や viewmode CollisionPawn で見ても、正常に当たりが存在するように見えるのだけれど・・・?
これを調べるのは難儀だなぁ。。
そして床ぬけするとなぜか Player が Destroy される。
誰の仕業?
落下死したら Destroy する仕組みとかがあるんだろうか??
コールスタックはこれ。
> UE4Editor-Engine.dll!APawn::Destroyed() Line 393 C++
UE4Editor-Engine.dll!UWorld::DestroyActor(AActor * ThisActor, bool bNetForce, bool bShouldModifyLevel) Line 622 C++
UE4Editor-Engine.dll!AActor::Destroy(bool bNetForce, bool bShouldModifyLevel) Line 3802 C++
UE4Editor-Engine.dll!AActor::CheckStillInWorld() Line 1308 C++
UE4Editor-Engine.dll!UCharacterMovementComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction * ThisTickFunction) Line 1269 C++
へぇ。
MovementComponent に落下し判定の機能があるようだ。
無効化する方法は・・・
WorldSettings にあった。
Kill Z の値は -1000.0 になっている。
-10m?
結構小さいな。
この値をもっと低くするか、
Enable World Bounds Checks のチェックを外せば無効化できそう。
----
当たり抜け問題。
挙動的にバウンディングボックス的なものがあるんじゃないだろうかという疑惑。
エンジンコードのコリジョン更新処理をもう少し見直してみる。
んーわからん。
当たり抜けを引き起こしている要因は何か?
・高さ?
・角度?
・属性的なもの?
まずすべて高さ 0.0 の平面にしてみる。
-> 問題ない。
-4m にしてみる。
-> 抜ける。
-1m にしてみる。
-> 抜ける
-0.5m にしてみる。
-> ガクガクする。
あー、やっぱり高さの問題か。
どこかにバウンディングボックス的な情報があるはずだ。どこだ?
CollisionComponent の ChachedLocalBox は更新しているはずなんだけれど。
まだどこかにあるのだろうか?
----
・・・
んーーわからん。
1つ気づいたこと。
1度目の VR Preview 実行では当たり抜けするけれども
2度目の VR Preview 実行では当たり抜けしない気がする。
これは一体?・・・
世界にコリジョンを反映させる処理みたいなものがあるのでは・・?
・・・
どうも一度エディタに戻った時に、ハイトマップがエディタ環境に反映された結果、
その次の VR Preview では正しく動くようになっているっぽい。
やはり何か処理が足りないようだ。
----
・・・
んー。
ブレイクポイントを張ってみたけれど、
VR Previewから戻るときに特にLandscape系の処理は走っていない。
何だろう・・・
・・・
----
解けたー!!!
原因は何気なくコードを移植した UpdateHeightfieldRegion() のこの部分。
PhysicsInterfaceTypes::FInlineShapeArray PShapes;
const int32 NumShapes = FillInlineShapeArray_AssumesLocked(PShapes, Actor);
if (NumShapes > 1) {
FPhysicsInterface::SetGeometry(PShapes[1], LandscapeComponentGeom);
}
LandscapeCollision.cpp の ULandscapeHeightfieldCollisionComponent::OnCreatePhysicsState() を見ると
どうやらShapeを2つattachしていて、 PShape[1] だとエディット用の Shape を示しているらしい。
PShapes[1] を PShapes[0] にしたら反映されるようになった!
----
草がずれる問題。
どうも Landscape を移動する前の状態から、草が更新されていないように見える。
草の再配置が行われていない?
草の再配置を明示するためにはどうしたらいいだろうか??
-> 結論。
ALandscapeProxy::FlushGrassComponents() を呼べば
前の草が削除されて新しい草に更新される。
けれども、この関数の後半にある #if WITH_EDITOR で囲まれた部分が、
GrassData のバッファ自体も解放してしまうので、ウエイト情報やハイト情報も消えてしまう。
-> この部分を除外したコードを別関数として移植。
----
やっと・・・終わった・・・。
動画
第0053回 / 地形に起伏を付ける / Making a procedural terrain. #UE4 #GameDev #VR https://t.co/KoKgUhsnE4 pic.twitter.com/jCyyufxT7F
— ue4-master (@ue4master80) December 16, 2019
以下宿題。
・建物が微妙に地面から浮いている。
・遠くに移動すると、地面のなくなった物理オブジェが落下していく・・・。物理止めないと。