回答:
アレイとしてのツリーは、私にとっては勝利のように聞こえます。階層の深さ優先トラバーサルを実行し、配列に入力するだけです。再帰を巻き戻すときに、子への絶対インデックスで親を更新するか、delta-from-meだけで更新することができ、子はどちらの方法でも親インデックスを保存できます。実際、相対オフセットを使用する場合は、ルートアドレスを持ち歩く必要はありません。構造はおそらく次のようになると思います
struct Transform
{
Matrix m; // whatever you like
int parent; // index or offset, you choose!
int sibling;
int firstchild;
};
...したがって、(簡単に)可変サイズの構造を持つことができないので、兄弟に到達する方法を知るためのノードも必要です。変換オフセットの代わりにバイトオフセットを使用したと思いますが、変換ごとに子の数を可変にすることができます。
struct Transform
{
Matrix m; // whatever you like
int parent; // negative byte offest
int numchildren;
int child[0]; // can't remember if you put a 0 there or leave it empty;
// but it's an array of positive byte offsets
};
...その後、適切な場所に連続する変換を配置することを確認する必要があります。
ここに、子「ポインタ」が埋め込まれた完全に自己完結型のツリーを構築する方法を示します。
int BuildTransforms(Entity* e, OutputStream& os, int parentLocation)
{
int currentLocation = os.Tell();
os.Write(e->localMatrix);
os.Write(parentLocation);
int numChildren = e->GetNumChildren();
os.Write(numChildren);
int childArray = os.Tell();
os.Skip(numChildren * sizeof(int));
os.AlignAsNecessary(); // if you need to align transforms
childLocation = os.Tell();
for (int i = 0; i < numChildren; ++i) {
os.Seek(childArray + (i * sizeof(int)));
os.Write(childLocation);
os.Seek(childLocation);
childLocation = BuildTransforms(e->GetChild(i), os, currentLocation);
}
return os.Tell();
}
void BuildTransforms(Entity* root)
{
OutputStream os;
BuildTransforms(root, os, -1, 0);
}
(相対位置を保存する場合は- currentLocation
、2つの「位置」書き込みに追加するだけです。)
行列の配列へのインデックス付けは、おそらく最も単純でメモリ効率の良いアプローチでしょう。
一連の変換は、マトリックス配列にインデックスを付ける一連のポインターまたは整数またはその他の小さな構造体としてLIFOに保持できます。これは、冗長な行列の格納を防ぐのに役立ち、データストレージコードをデータアクセサーコードから分離します。
最終的には、LIFOからインデックス値をプッシュしてポップし、変換チェーンを保存または再生します。
また、マトリックス構造に変換タイプ...回転、変換などを含めることができる場合は、メモリを少し節約することもできます。それ以外の場合は、タイプをインデックスとともに保存する必要があるため、重複が発生する可能性があります。