2

提案や批判にオープンにし、これを読む可能性のある Blender を使用する XNA プログラマーに希望を与えたいので、なぜ私がしていることをしているのかについて少し背景情報を提供する必要があるように感じます。この投稿の長さを考慮して、私の問題の核心を気にしない場合は、最後の段落まで飛ばしてください。

毎回 Blender から新しい .FBX または .OBJ モデルをエクスポートする必要がないように、.blend ファイル ( Blenderで作成) を読み取り、それらを XNA ゲームからロード可能なデータに変換する XNA コンテンツ パイプライン拡張プロジェクトに取り組んでいます。少し調整するだけでなく、(願わくば) Blender の素晴らしいパーティクルと物理演算機能の XNA 互換サポートを作成します。

Blender の内部構造に深く入り込むことなく、.blend ファイルがどのように機能するかについての私の理解を説明したいと思います。あなたがこの件についてより詳しい場合は、私を修正してください。

Blender はファイルをバイトの「ブロック」で保存します。これらのブロックのほとんどには、3D シーンのオブジェクトと設定を表すデータが含まれており、ファイル内の最後のブロック (SDNA ブロックと呼ばれる) には、非常に単純な C スタイルの構造と考えられるものが含まれており、それぞれに一意の識別子があります。 、およびさまざまなタイプのいくつかのフィールド。これらの構造体のフィールドは、 または などの単純な型のint場合floatもあれば、SDNA ブロックで定義された型の場合もあります。

たとえば、IDSDNA 構造の半疑似コード表現は次のとおりです。

structure IDPropertyData
{
    void *pointer;
    ListBase group;
    int val;
    int val2;
}

ご覧のとおり、フィールド*pointerval、およびは、実行時にまたはval2型の単純な値で表すことができます。ただし、このフィールドは typeであり、ファイルの SDNA ブロックの他の場所で定義されています。void*intgroupListBase

ここで、C# の新しい動的機能が登場しますBlenderObject。SDNA 構造体 (「SDNA 型」) とバイトのチャンクを指定すると、次のいずれかを格納することにより、その構造体のインスタンスとして動作するクラス (と呼ばれる) を作成しました。それ自体の単純型の値、またはBlenderObjectそれぞれが「フィールド」の 1 つを表す他のインスタンスのコレクション。これにより、私のライブラリのユーザーは次のようなコードを書くことができます:

//Get a BlendContent instance that contains the file's information.
BlendContent content = BlendContent.Read(filePath);
//Get the 0th (and only) block containing data for the scene (code "SC")
BlendFileBlock sceneBlock = content.FileBlocks["SC", 0];
//Get the BlenderObject that represents the scene
dynamic scene = sceneBlock.Object;
//Get the scene's "r" field, whose SDNA type is RenderData.
dynamic renderData = scene.r;
//Get the x and y resolution of the rendered scene
float
    xParts = renderData.xparts,
    yParts = renderData.yparts;

scenerenderDataはどちらも「複雑な」BlenderObjectインスタンス (それぞれが直接の値ではなくフィールドのコレクションを持つ) でありxparts、 とypartsは両方とも「単純な」BlenderObjectインスタンス (それぞれがフィールドのコレクションではなく直接の単純型の値を持つ) です。 )。BlenderObjectSDNA 型がアセンブリで具体的にコンパイルされた型である場合、それぞれが期待どおりに動作します。これは、ダイナミクスを使用して Blender オブジェクトを表現する私の目標です。

私のライブラリの使用を簡素化するために、DynamicObject「単純な」BlenderObjectインスタンスが直接の値として動作するように のメソッドをオーバーロードすることに取り組んでいます。たとえば、直接型の値を 4 としfooます。 で次のことができるようにしたいと考えています。BlenderObjectintfoo

string s = foo.ToString();
Console.WriteLine(s);

最初の行の意図は、fooの内部値のToStringメソッドを呼び出すfooことです。この反射的なメソッド呼び出しはうまく機能します (また、配列型の直接値のインデクサーに適用される同じ概念も同様です)。ただし、次のようなことを試した場合を除きます。fooTryInvokeMemberInt32.ToString()BlenderObject.ToString()

string s = foo.Bar();
Console.WriteLine(s);

Barは私のアセンブリで定義された拡張メソッドであるため、fooの値 4 を反映すると、明らかにそれを見つけることができず、もちろん例外がスローされます。最後に、私の質問は次のとおりです。

オブジェクトに適用できる拡張メソッドを見つけてキャッシュするにはどうすればよいですか? リフレクションで拡張メソッドを見つける方法は知ってBlenderObjectますが、動的インスタンスで拡張メソッドが呼び出されるたびにこれを行うと、非常に遅くなります。その質問で説明されている方法以外に、拡張メソッドを見つけるためのより高速な方法はありますか? そうでない場合、拡張メソッドをインターンする方法についてはどうすればよいのでしょうか? 長生きして申し訳ありません。役立つ回答/コメントを事前に感謝します。

編集:

@spender が答えたように、私の問題を解決する簡単な方法は辞書を使用することです (ただし、渡さDictionary<Type, Dictionary<CallInfo, MethodInfo>>れた によって提供される CallInfo を簡単に使用するために を使用しています)。ただし、私の実装により、別の質問が発生しました。InvokeMemberBinderDynamicObject.TryInvokeMember

呼び出し元のアセンブリによって参照されるアセンブリで定義された型を取得するにはどうすればよいですか? たとえば、次のコードを考えてみましょう。このコードが私の Blender ライブラリを参照するプロジェクトにあり、拡張メソッドBaz()がそのプロジェクトによって参照される別のアセンブリで定義されているが、私の Blender パイプラインによっては定義されていない場合、Blender パイプライン内から検索するにはどうすればよいでしょうBaz()か? これは可能ですか?

4

1 に答える 1