戻る トップへ

ここにある内容は、Shadeのプラグインを設計しようという人用の資料です。その気のない人にはまったく無意味な内容です。
また内容は、あくまで私個人が特定の Shade のバージョンで確認した内容で、全ての Shade に当てはまるかの検証は行ってません。また、私が保証するものでもありません。 ましてや、e-frontier が保証するものでもありませんので、e-frontier にこの内容を前提にした質問、依頼を出さないように願います。
  1. コールバック関数
    このタイプの関数は、Shade 側からプラグインを呼び出すエントリーです。常駐型で Shade の 状態変化に応じて動くプラグインを製作するためには必須のエントリーです。

    1.1. virtual void plugin_interface::active_scene_changed ( bool &, scene_interface *, void * = 0 )
    この関数は、編集対象のシーンが切り替わる度に呼ばれる関数です。しかし、使用上で要注意なことが幾つかあります。
    まず最初は、Shade の起動時に引数の scene_interface * が不正な値で呼ばれます。 さらに、この時点では shade_interface のメンバー関数さえもうまく動作しない可能性があります。
    これへの対策としては、
    virtualvoid active_scene_chaged () { try { ... 本体 ... } catch (...) {}
    のように無条件で try{ } catch(...){ } で囲むようにコーディングします。 このとき、不用意に catch(...){} の{}内に何らかのエラー処理で shade_interface のメンバー関数を呼ばないようにする必要があります。それが逆に Shade を異常にする可能性があります。 せいぜい自分のプログラム内の変数設定程度に抑えることです。 また、得られた scene_interface * は、同じシーンで同一である保証はなく(同一ではありそうです) さらに、別のシーンであっても異なる保証さえもありません(どうも、等しいようです。) ですから、この scene_interface を保存しておいて別途使うとか、以前のシーンとの比較に使うとかは出来ません。

    1.2. virtual void plugin_interface::idle_task ( bool &, scene_interface *, void * = 0 )
    この関数は、Shade で待ち状態の処理がないときに定期的に呼ばれる関数です。本音は使いたくない 関数ですが、常駐型のプラグインでちょっと細かい同期処理をしたいときは、結局使わざるを得ない 関数になります。この関数でちょっとした処理を行う範囲は、それほどオーバーヘッドが増えない ようで、その点はあまり気にすることはないようです。
    この関数で注意することは、引数の scene_interface * が必ずしも現在アクティブなシーンではないことです。つまり、マルチシーンの場合、全ての scene_interface * を交互に伴って呼ばれるようです。ですから、アクティブな scene_interface から情報を得て動作を制御したいような処理の場合引数で得られた scene_interface をそのまま使うと困ったことになります。
    例えば、現在のモードが編集モードか非編集モードかによってどこかの表示を切り替える等の処理などの場合、シーンの一方と別の方の編集モードが異なると表示が目茶目茶ちらつき、 Shade の反応も遅くなるなどの現象がでます。 その scene_interface がアクティブなシーンのものかどうかをチェックするのは意外と面倒なので、このエントリー内で
    compointer active_scene ( scene->get_scene_interface() ) ;
    で勝手に アクティブシーンオブジェクトを取得してそれを使うようにします。

    1.3. virtual void make_wireframe(shape_class &, const mat4 &, int v, int f, void * =0 )
    この関数に参照を付けてないのは理由があります。同じ名前でも、各プラグインインターフェース毎に動作が異なるからです。この関数は、特定の形状にアトリビュートストリームを生成し、第2引数でプラグインID を指定することで、そのプラグインIDの make_wireframe が、図形ウインドウの更新毎にその形状のクラス参照を引数に呼ばれる機能と、
    全ての形状で呼ばれる二本立ての機能があります。
    最初の用途は、特定の形状に属性を付加して、それを図形ウインドウで表示するために利用します。後者の用途は、全ての形状に漏れなく一定の内容を図形ウインドウで表示する用途に使います。どちらの場合でも図形ウインドウ内で形状をデコレーションするためのものです。
    そのため、この関数内で形状を編集することは、基本的に保証されないと考えられます。 今のところ、形状要素の削除、追加は動作異常(無限ループ)になるようです。この関数内での処理は
    shape_class::make_line_wireframe()
    shape_class::make_sphere_wireframe()
    や、OpenGL 関数で図形ウインドウに図形を表示するに留めるのが無難なようです。(私は掟破りをして何とか動いてますが、考えなおす時期かな?)
    もしそのタイミングでどうしても、形状を操作したい場合は、そこでフラグのみ立て idle_task() で、そのフラグを検出して処理を行うのが正しい対処法のようです。
    OpenGL関数に渡す位置情報は、ローカル座標系での値を直接渡します。ワールド座標系への変換は不要です。
    次に、2番目の引数の mat4 は、今では何の意味もありません。中は単なる単位マトリックスです。
    3番目の view のリファレンスの説明は間違っているようです。正確には、
    0:側面図
    1:上面図
    2:正面図
    3:透視図
    です。
    もし使う場合は、各自チェックした方がいいでしょう。
    最後に、特定の形状にアトリビュートIDを設定する場合は、これが可能なプラグインのインターフェースは、割と少なく、attribute_interface, creator_interface, modifier_interface 位のようです。window_interfaceplugin_interface では不可能です。

    1.4.virtual void plugin_interface::shapes_modified( bool &, scene_interface *, int, shape_class *const * shapes, void * = 0 )
    SDK8 で加わったこのコールバック関数は、形状の要素の変更があった場合に呼ばれるようですが、取り消しで要素が削除されたり、追加されても呼ばれないようです。これは、ちょっと不便かも。
    また、あくまで形状要素の変更があったときにのみ呼ばれるので、要素の選択状態が変化しても呼ばれません。

    1.5.virtual bool plugin_inteface undoable (void * =0)
    取り消し(アンドゥ)機能を可能にするためのコールバック関数で、この関数で true を返すようにすると、別途用意されているアンドゥ用関数を使えば、個々に取り消しを可能に出来ます。しかし、この指定は、そのプラグインインターフェースの基本エントリから呼び出す場合しか有効ではありません。つまり、その他のコールバック関数の中でアンドゥ用関数を呼び出しても有効にはなりません。
    但し、window_interface 内のコントロール(ボタンなど)を押したときに処理の取り消しを可能にはできます。方法は、そのコントロールクラス内で undoable をオーバーライトすると可能になります。(確かにコントロールクラスは、plugin_interface から派生してますね...)

  2. window_interface
    window_interface は、他のプラグインに比べて、ボタン一発で実行出来るという点で、通常のプラグインのようにメニューから起動して、ダイアログで条件を設定して OKを押して単一の処理を実行のような面倒な手順を踏まなくていいメリットがあります。同時に複数の機能を1つのプラグインに集約できるのでプラグインIDの節約になります。(この件はもう意味がないか)
    他のプラグインに比べて、最初の取っ付きが面倒なようですが、一度作ってみると以外に簡単です。出来たプラグインは、いかにも Shade に密着した感を醸し出し自己満足にもなります。取り消し機能の有効化の方法も上記のように可能です。

    2.1. 起動シークエンス
    これは、Shadeのバージョンにより多少異なる場合が有り得ますが、最初のプラグイン作成で一番戸惑った点なので披露します。(以降の話は、バージョンによって異なるかもしれませんが、対応策はどのバージョンでも有効と考えられます。)

    1.通常のプラグインの起動シークエンスで共通のグローバル関数が Shade から読み込まれ、プラグインオブジェクトが生成される。

    2.initilize()関数が呼ばれる。
    このコールバック関数は、全てのプラグインが一通り読み込まれた後に呼ばれます。
    しかし、この時点では、まだ Shade の全構成の構築が完了してません。ですから、ここでシーンの情報や形状の情報を取得できませんし、した場合おかしくなる可能性があります。
    ここでは、自分のプラグイン内部の初期化(ウインドウのデザインや、使うコントロールの生成、変数の初期化)に専念し、シーン情報の取得などは行わないようにします。
    さらに、shade::message() のような関数も Shade のバージョンによっては、異常になります。異常にならないまでも、メッセージウインドウには、その結果は表示されません。

    3.active_scene_changed()
    このコールバック関数は、initilize() 後、無条件に何度か呼ばれます。
    困ったことは、最初の呼び出し時には、不正な scene_interface() で呼ばれるようです。そのため、この関数を使って単純にシーンの変化を得ようとすると、Shadeの起動時点で「予期せぬエラー」または、Shadeが異常になり、途方に暮れることになります。
    対処は、1.1.で書いたこと以上はありません。
    次に、もし、シーン指定無しで起動すると、名前無しシーンで呼ばれ、
    シーン有りで起動すると、そのシーン名で呼ばれるようです。
    くれぐれも、この関数内の catch(...){} 内に余計なものを書かないように!
    この対策にカウンターや初期化フラグを使うことは、将来の Shade のシークエンスが変わる可能性を考慮するとやめた方がいいと思います。

    2.2. VC++6の制限事項
    もうこの制限は、無視していい時期と思いますが、VC++6を利用している人用です。
    window_interface 内にコントロールを配置するためにそのクラスを配置するとき
    struct window_component : window_interface {
    コントロールの定義
    ・・・
    メンバー関数の定義
    ・・・
    変数の定義
    ・・・
    }

    (順不同、Public,privateの区別なし)
    でコントロールを単純に記述するとコンパイルエラーになります。
    コントロールをメンバー関数の中に書くとエラーが無くなります。しかし、これではいかにも、不便です。この場合は、
    struct a_class :button_class のように記述せずに
    struct a_class :window_component::button_class のように参照クラスを明記すればエラーは回避できます。
    .net の場合は、問題ないです。

    2.3. マウス移動とコントロール
    マウスのウインドウ内での移動を知りたいとコールバック関数のmouse_move() をオーバーライドした瞬間にそのウインドウ内でのコントロールは、機能不全になります。
    つまり、マウス制御関係とコントロールは共存できないということです。ですので、ウインドウのデザインは、コントロール配置型かマウス操作型かはっきり決めて設計する必要があります。両方混在させたい場合は、夫々をサブウインドウでデザインする必要があります。
    但し、全く手が無いわけではありません。方法は、コントロールは配置だけにし、その動作を全てマウスの移動とクリックでチェックしてコントロールを遠隔操作する方法です。この方法を使うと、スライダのドラッグ中のイベント検出も可能になります。
    NEW今回、この機能を提供する汎用クラスを作りました。幾つかのプラグインで一応動作しているようです。もし、興味があれば、draggable_sliderがそれですので、利用してみてください。

    2.3. コントロールの特性
    コントロールのベースになる handler_interfacecontrol_base_class のメンバー関数は、継承されているとは言っても全関数が全コントロールで利用できる訳ではありません。また、挙動も少しづつ異なります。幾つか典型例を示しますが、全ては網羅できません。一般にスタティック型とダイナミック型で異なるようです。
    static_text_class
    このクラスは、初期化後変化しないことを前提としていますので、clicked() は効かないようです。(少し不満)set_title() やその他のそのクラスの設定値を変える関数は、利用すること自体は可能ですが、それだけで表示には反映しないようです。直ぐに
    invalidate() を呼び出して再初期化させる必要があります。

    number_class
    これは、結構利用することが多いコントロールですが、表示もいまいち、編集もいまいちでもう少し使いやすくならないでしょうかね。編集しない用途には逆にコントロールキーで数値入力を促す能力が邪魔をして、結局 static_text_class で代用する方が便利な場合もあります。

    popup_menu_class
    これは、Shade 本体でも多用されて結構便利な機能ですが、どうもメニューリストを set_items() で自由に変更できないような感じです。(間違いかも知れません。)各自確認して利用してください。
    私は、アイテムを変更する場合、コントロールを削除してから新たに作ってます。
    このタイプのコントロールの場合の注意事項は、表示位置やサイズを指定した場合、その位置やサイズは、ポップアップ自体の位置とサイズであって、表題は、その左に付きます。よく間違えますので表題分ずらせる必要があります。

    slider_class
    割と使いやすいコントロールですが、数値部は number_class と比較して制限が多いので数値部は割り切って使います。スライダのドラッグ後に clicked() が呼ばれます。focused() は数値部がクリックされた時(入力完了時ではありません。)に呼ばれるようですが、あまり利用価値は感じません。数値部の値を編集し終わると、スライダと同じようにclicked() が呼ばれます。
    このクラスの位置もスライダ部の左端で、タイトル部と数値部はその右に出っ張ります。
    NEW今回、同期スライダの作成のためにスライダのデザインを少し詳細にチェックした結果、幾つか面白い結果が分かったので報告します。
    まず、コンストラクタの引数で渡す size は、スライダの大きさではなく、スライダの可動範囲であることが分かりました。size が n の場合、0~nの範囲で動くスライダが生成されます。しかも上記スライダ部の大きさはその前後4ピクセル分はみ出ます。
    次に、メンバー関数 get_size() で得られる値は、スライダ部の大きさで、コンストラクタで指定した size ではありません。(+9)になってます。ところが、メンバー関数 set_size() で与える大きさの水平部は、スライダの可動範囲になります。(つまり、コンストラクタの size と同じ。)
    ですから、set_size( get_size() ) ; のような記述をすると、+9分スライダが膨らむことになります。
    get_position() は、上記のスライダ部の左上になります。

    tab_class
    これは、ウインドウデザイン上でとても役に立つコントロールです。使う上で通常の利用上で大きな問題はありません。但し、メニューに日本語を使った場合、Shadeで文字化けのバグがありました。最新バージョンでは不明ですが、このクラス特有でしたので、利用時は意識して使ってください。
    プラグインの文字コードを utf8 で統一するとうまくいくかも知れません。

    quaternion_class vec2_control_class vec3_contorl_class vec4_control_class matrix_control_class
    これらは、Shade8で新たに加わったクラスで、どんな感じか興味津々でしたが拍子抜けでした。
    quaterinon_class は、ボールジョイントのスライダ制御部のデザインそのものを公開しただけで、作ろうと思えば自作できそうです。なんせ、回転を選ぶとスライダのつまみ部分が消えてしまうShade 自体のバグをそのまま継承してます。
    その他のクラスは、その数分の number_class を並べて一括処理出来るコントロールです。これらのコントロールを公開した理由は、このエントリーで設計すると、このコントロールを Shade 側でリファインすれば、プラグインの方も再設計せずにリファインされるという風に考えれば、良心的と解釈も出来ます。

    2.4. コントロールの破棄
    cont * x = new cont() ; で作ったコントロールは、通常の C++ のように
    delete x ;
    で削除しないで
    x->delete_self() ;
    で削除する必要があります。これは、リファレンスにもありますが、つい使ってしまわないように。

    2.5. コントロールのID
    コントロールを生成するときにコンストラクタの引数には必ず ID を渡す必要がありますが、この必要性がいまいちはっきりしません。実際のところ常に 0 を渡しても何の問題も起こらないようです。

    2.6. 必須の基本クラス
    window_interface 及び control_class のメンバー関数を使う上で、覚えてしまえばどうと言うこともなく、また source( 8SDK の場合は、include) ディレクトリ内の shade_types.hの中身を読めば分かる構造体(リファレンスには説明なし)に以下の構造体があります。
    これらは、ウインドウやクラスの幾何学的情報を纏めて扱うものです。最低その要素名が判れば使えます。
    rectangle_class
    4つの整数要素で四角形を表現する構造体です。
    要素は 配置順に left,top,right,bottom です。前2つで画面の右上、後ろ2つで対角点の位置を表現してます。大きさは right-left+1, bottom-top+1 になると思ってますが、まだ詳細は確認してません。
    point_class
    2つの整数要素で、画面の位置を表現する構造体です。 要素は、h,vh が水平位置、 v が垂直位置を表しています。

    size_class
    2つの整数要素で window や control の大きさを表現する構造体です。
    ソフト的には、point_class と等価です。単に使うときの意味をソース上で判りやすくするためだけにある構造体です。

    上記のクラスは、要素名を意識せずに、幾つかのオペレータ(演算子)操作が出来ます。詳細は、shade_types.h を真面目に読めば判りますが、私はまだ横着して要素を直接操作してます。

  3. shape_class 及びその派生クラス
    多分一番利用場面が多いクラスでしょう。また、このクラスから形状の要素一つ一つをアクセスする要素系クラスも多用されると思います。これらに付いては、理解しやすい点やリファレンスがそれなりにあるので特徴的なものだけを列記します。

    3.1. get_name()
    いろんな用途やデバッグ用には必須のメンバーです。使い方も別に何の工夫も要りません。但し、一点のみはまる点があります。それは、get_name() で得た、文字列エントリーの有効期限は、次の get_name() の呼び出しまでだということです。つまり、この関数は、メモリ内に保存してある形状名文字列のエントリを渡すのではなく、それを別途確保したメモリ領域にコピーしてそのエントリーを渡しているようです。ですので、呼び出すと前の値がクリアされます。
    具体的に、以下のソースの場合
    part_class & dad = shape.get_dad() ;
    sprintf(buff," %s %s"shape.get_name(),dad.get_name());
    shade->message( buff ) ;

    のような書き方を行うとどんな風に表示されるでしょうか?多分両方とも同じ名前になってしまうはずです。どちらの名前になるかは、sprintf の実装によると思います。
    デバッグのテスト用にソースに追加して面食らい、原因判明まで丸1日使ったことがあります。

    3.2. 要素クラス
    Shade7 から、線形状、自由曲面、ポリゴン形状のような、数が不定のポイントや線や面で構成される形状に その各要素を操作するクラスが追加されました。以前は、shape_classpart_classpolygon_mesh_class で直接引数に要素番号を付けて位置情報の取得や設定を行っていた者にとって、コーディング上余分な操作が必要になり不満でした。しかし、実際に利用してみると古いアクセス方法と要素クラスでアクセスする方法では、実行時間に格段の違いがあることがわかりました。特にポリゴン形状では、顕著です。
    面倒ですが、
    vertex_class edge_class face_class control_point_class
    は諦めて大いに使いましょう。

    3.3. polygon_mesh_class 内 begin... -> end... 系関数
    begin_set_point() end_set_point()
    begin_selecting_control_points() end_selecting_control_points()
    begin_removing_control_points() end_removing_control_points()
    begin_removing_faces() end_removing_faces()
    begin_removing_edges() end_removing_edges()

    は、複数の要素を一気に処理する場合、最初と最後にこれらをペアにして呼び出すと処理の高速化が可能になります。最初と2番目の関数ペアは、要素のモディファイにかんする処理で、それ以外は、要素の削除です。要素の削除は、ある意味、各要素の削除中要素番号を変化させないことで、高速化すると考えられます。本当かどうかは確認してません。意図的に削除する場合は、どのような場合でも大きい方から削除してますので。
    以上の関数はそういう意味でプラグインの性能向上上必須の関数ですが、注意しなければいけないことは、begin のネスティングで「予期せぬエラー」となります。幾つかの関数を階層的に書いて一番適切と思えるところにこのペアを書いたつもりが、次の改造でエラーで落ちることになったことが何度かありました。これらのペアは、面倒でも、プラグイン実行の最上位でのみ利用し、関数内では利用しないようにすることが安全です。

    3.4. polygon_mesh_class 内 remove_edge() 関数
    この関数は、appned_edge() と異なり、稜線だけを消してそれを構成要素としている面は削除しません。ですから、安易に使うと稜線がない面が残るだけで稜線削除の意図が達成できません。このような場合に面も削除する必要がある場合は、特殊な関数

    fccwev( e, p0, true )
    fcwev( e, p0, true )

    を活用して、面番号を得てそれを別途削除する必要があります。
    NEW但し、上記の関数を呼び出す前に呼び出す必要がある、setup_winged_edge() 関数が、SDK8 になって妙に時間が掛かるようになりました。少し多量の面を持つポリゴンの場合、秒の単位が掛かります。これは、是非改善して欲しい内容です。

  4. 位置情報の取得
    Shade では、形状の位置などの位置情報は、形状固有の座標系であるローカル座標と、シーンでの実際の位置のワールド座標があります。プラグインで取得する位置は、仕様上全てローカル座標値で、それを変換するマトリックスを別途得て、そのマトリックスを掛けることてワールド座標値を得ます。一見簡単なようで実は結構面倒です。面倒である理由は、各形状毎にその変換マトリックスを得る方法が同じでないからです。色々なコーディング上のテクニックはありますが、これが最上という方法がいまだに思いつかずにいます。
    そこで、ここでは各形状毎にマトリックスを得る方法を単に列記します。

    4.1.線形状の場合
    shape_class または line_class からポイント番号を指定して control_point_class を得る。
    control_point_class から skin_class を得る。
    skin_class でスキンバインド数を得る。
    skinバインド数が0なら、shape_class から get_local_to_world_matrix() で変換マトリックスを得る。
    0でないなら、skinクラスの get_skin_world_matrix() で変換マトリックスを得る。

    4.2.自由曲面パートの場合
    shape_class または part_class からポイント番号を指定してcontrol_point_class を得る。
    control_point_class から skin_class を得る。
    skin_class でスキンバインド数を得る。
    skinバインド数が0なら、part_class(shape_class でもいい)で get_transformation() * get_local_to_world_matrix() で変換マトリックスを得る。
    0でないなら、skinクラスの get_skin_workd_matrix() で変換マトリックスを得る。

    4.2.ポリゴン形状の場合
    shape_class から polygon_mesh_class を得る。
    polygon_mesh_class から 頂点番号で vertex_class を得る。
    vertex_class から skin_class を得る。
    skin_class でスキンバインド数を得る。
    skinバインド数が0なら、polygon_mesh_class(shape_class でもいい) から get_local_to_world_matrix() で変換マトリックスを得る。
    0でないなら、skinクラスの get_skin_world_matrix() で変換マトリックスを得る。

    4.3.その他の形状の場合
    shape_class から get_skin(void) を使い skin_class を得る。
    skin_class でスキンバインド数を得る。
    skinバインド数が0なら、shape_class から get_local_to_world_matrix() で変換マトリックスを得る。
    0でないなら、skinクラスの get_skin_world_matrix() で変換マトリックスを得る。

    以上でマトリックスを得ることが出来ます。ローカル位置は、別途確保します。

    4.4.local_to_world_matrix() の意味
    このマトリックスは、その形状の親パートからルートパートまでの全変換マトリックスの積と考えられます。パートでない通常の形状自体は変換マトリックスは持ちませんから、このマトリックスでワールド座標系に変換できます。
    しかし、パートの場合は、そのパートが持つ変換マトリックスの分は、このマトリックスには含まれません。これは、当たり前といえば当たり前で、ジョイントパートの場合、ジョイントの回転の中心点や起点、終点は、自分のパートの変換マトリックスの影響下にあるはずはないからです。(その情報とジョイントの変化値から自分の変換マトリックスを作る訳ですから)ジョイントの制御点の位置のワールド座標値を得るためには、この仕様が正しいといえます。
    唯一問題(というより誤解しやすい)対象は、自由曲面パートです。このパートは、パートでありながら、その子の線形状の要素を直接番号でアクセスできます。そこで、線形状と同じ発想で計算すると、自由曲面パートの自体が持つ変換マトリックス分計算漏れになります。このことは、いつも念頭にあるのですが、よくその分の処理を見落としてまいます。

    4.5. get_skin_world_matrix() 後記
    通常、形状の位置はローカルで得られますので、後はワールド座標値を得ればほとんどのニーズに耐えられます。しかし、別の形状のポイントのワールド位置にある形状のポイントを合わせたい場合、与えられたワールド座標値から、その形状のローカル座標値に変換する必要があります。そのときは、get_skin_local_matrix() というようなマトリックスが必要になります。このマトリックスは逆マトリックスですので、計算はできますが、それなりの計算量が必要になります。将来は、このメンバー関数を追加して欲しいです。
    もうひとつ不満なことは、スキンのバインド数が0の場合、get_skin_world_matrix() は、単位マトリックスを返します。このため、上記のような面倒なフローになります。
    もし、その場合、get_skin_world_matrix() が、形状の get_local_to_world_matrix() と同じ値を返せば、一々バインド数を調べなくても、常に get_skin_world_matrix() を得れば済むのですが。

  5. ブラウザ内移動
    ブラウザ内で形状を移動させる関数は、scene_interfaceshape_class の両方に place 系の名前で用意されてます。しかし、真面目な話、とても使いにくいです。特に形状を追加する場合、パートを選択して追加した場合、そのパートが空ならそのパート内に形状が追加され、空でなければ、その下の位置に追加されます。そのため現在の形状挿入位置のチェックをして、その形状がパートで子を持つかどうかのチェックを行い、その有無で形状挿入後の後処理をすることになります。このとき、これらの place 系メンバー関数が必要になります。
    無条件に弟位置に形状を挿入する方法としては、無条件に part を生成し、その part_class を取得しておき、その後そのまま形状を挿入し、最後に無条件に、上記 part を削除する手もありますね。

    5.1.shape_class か scene_interface 系か?
    shape_class の場合、どの形状を動かすかは、オブジェクトで指定するため、個々の形状を指定して操作出来るメリットがあります。それに比べ scene_interface の場合は、選択されている形状を移動するので、前段階で形状を選択する必要があります。 しかし、shape_class のメンバー関数には、致命的欠陥があります。それは、対象の形状がパートの場合エラーで落ちることです。また、select() も動作しません。そこで、私は、今のところ常に scene_interfaceplace 系のメンバー関数を使うことにしてます。

    5.2.scene_interface の set_insertion_point(*shape_interface)
    この関数は、対象がパートの場合エラーになります。あまり深く考えずに select() を使う方がうまくいくようです。

    5.3.設計法
    以上の不具合を知らなかったとき、ちゃんと動くか調べるのに、各呼び出し毎に show_message_box() を呼び出してデバッグしました。デバッグは、.net のデバッグ機能を使うことが出来ますが、ブレイク中は、Shade側の表示をチェックできません。(デュアルモニタで夫々にデバッガとShade画面を出すような贅沢が出来れば別ですが。)そこで、place 系処理のデバッグはこの方法でやってます。幾つかの場合で意図したとおりに移動してくれれば、後はほぼ問題なく設計完了できます。

  6. vec2,vec2,mat4,quaternion_class
    これらの基本クラスを理解しておくのは、プラグインを設計する時点で必須のことです。しかし、これらのリファレンスは全くありません。vector.hvector.cpp を眺めれば全てを理解できるので作る者は、その程度の苦労を厭うなという訳でしょうか?でも随分不親切です。
    それらの全てのリファレンスを書くのは到底無理なので、判った範囲を徒然に書いていこうと思います。

    6.1. mat4とvec3の演算
    vec3 * mat4 は、vec3( vec4(vec3,1.0) * mat4 ) と同じ計算がされる。

    vec3( vec4( vec3,0.0) *mat4 ) をすると、オフセット分が無視される。(当たり前ですが二つの vec4 の差に変換マトリックスを掛けるとその差の部分のワールド座標での差になるのはいかにも数学的ですね。

    6.2. コンストラクタ
    mat3 mat(qoternion_class a) ;
    と記述すれば、 クォータニオン a の変換マトリックスを mat に得ることが出来る。

    6.3. mat4::unmatrix(scale, shear, rotate, translate)
    引数に確保した変数を与えると、このメンバーで、変換マトリックスを 拡大縮小、回転、せん断、移動の各要素に分解できる。

    6.4. vec3 同士の掛け算は、外積
    vec3 v = a * b ; // a,bvec3
    と書くと、vにはその二つのベクトルの外積が得られます。

    6.5. mat4 では割れません。
    当たり前ですが vec3/mat4 の演算は未定義です。(欲しいな)

    6.6. 8SDK
    8SDK では、vectors.cpp のかなりの部分が vectors.h に移っています。vectors.cpp には初期設定系の記述のみになっているので、これを参考に定義時の値を数値を使わず、簡単に記述出来ます。
    一番典型は mat4quaternion_classidentity で、無変換の値が入ってます。

    6.7. その他の演算
    vec3vec4mat4quaternion_class との積は、全て予想通りの結果を与えてくれると思います。

  7. ジョイント系に付いて
    ジョイントの操作は、motion_inteface 系と 各ジョイント個別クラスで設定できるようになりつつありますが、何故か、ball_joint のみ仲間はずれにされてます。何故でしょうかね?
    今のところここに大きく取り上げるテーマは無いのですが、1つだけあります。

    7.1.ball joint に与える quaternion値
    この値に絶対値 1.0 以外の quaternion を与えても Shade 内部で正規化するようです。密かに拡大縮小するのではないかと期待しましたが、見事に外れました。そのためか、正規化時に 0 割を起こすと、ボールジョイントの影響下の形状は、どっかにぶっ飛びます。余分に正規化する位なら、0 チェックもしてくれ!

  8. 文字エンコード
    現在、Shade では、一部のプラグインと Shade 本体との文字コード変換処理にバグがあり文字化けが起こります。Shade8 でさらにバグが増えました。8.1.1 でかなり改善したと表現されてますが、詳細は未チェックです。
    基本的に問題を回避する一番根本の解決策は、ソースを全て UTF8 で書いて、.net のコンパイルを、windows のコントロールパネルの地域と言語オプションを英語に指定してから行うことです。
    このとき、プラグインのグローバル関数の以下のエントリで 0 を返すようにします。
    extern_c int STDCALL get_text_encoding (shade_interface *shade, void *) {return 0;}
    もし、地域を変更したくない場合は、ソース中の全ての "" で表現する文字列の最後のキャラクタにブランクを追加すると何とか使えるようです。
    Shade8からは、さらに UTF8 化を進め、ディフォルトは、utf8 ということになったようです。
    今後のプラグインは、全てソースを utf8 で書くようにした方がいいと感じます。その方が、文字列変換のオーバーヘッドも考慮しても正解でしょう。但し、エキスポータやインポータはやはり問題ですね。

以下は、プラグイン設計中よくやったコーディング上のミスです。

 
恥ずかしながら公開します。
a.if( zzz == yuu ) と書くところを if( zzz = yuu ) と書いてしまう、
急いでいる時によくやるミス。
何か、制御が全然利いてないと?感じたらチェックすること。
また、予期せぬエラーも招く可能性あり

b. if( (a=func()) && flag ) のような記述では、cの言語仕様上 どちらが先に参照されるか分からない。
もしflag が flase の場合、 a=func()は実行されず、aの値が不定になる可能性がある。
このような記述は止めること。
a=func();
if ( a && flag ) {..}
のように記述すること

c.参照型クラスを取得する関数の記述で
shape_class & shape = scene->get_shape() ;
のように書くべきところで
shape_class shape = scene->get_shape() ;
と書いてしまってもコンパイルエラーにはならない。リンクエラーとなる。
この場合は、リンク時のエラーのため、ソースの何処のミスか見つけ難い。
もし、リンクエラーの場合、どの関数がエラーになっているかを調べ、
そのクラス名を確認後、そのクラス名でソース全体をサーチし、そこに
..._class & abc でなく
..._class abc になっているところを全て見つけるしかない。

.net の場合は、リンクエラーのメッセージからある程度読み取れるが、
vc6 の場合は判明しずらい。 リンクエラーの場合は、とにかくこのミスがないかを調べること

d.同様な記述で、参照型で定義した変数を別のところで上書きすることはできない。
具体的には、
shape_class & shape = scene->get_shape() ;
と書いた後で
shape = scene->get_...() ;
のように上書きしても、その変数 shape には、期待された形状クラスへのポインタ
は得られない。(困ったことにコンパイラでエラーにならない。これは、正確にはどうなるか不明。但しこのような記述自体するべきでない。

e. char *str = "abc" ; のような記述では、実体"abc" は、暗黙の const となる。
そのため、後で
*str = 'x' ;
のような表現で、最初の一文字だけ変更するような使い方は出来ない。
このような使い方をしたければ、
char str[sizeof("abc")] ;
strcpy( str, "abc" ) ;

のように ::strcopy を使う必要がある。

デバッグに付いて
.net ではプラグインのデバッグも可能です。
プラグインのプロジェクトは、普通 Shade から提供された plugin を使いまわししてます。というのもプロジェクト項目全てを自分で設定できれば申し分ないのですが、結構設定項目が多くてミスが入る可能性があるからです。(特にインクルード指定やリンク指定など)
例えば、OpenGL を使う場合、simplerendeler.cpp のプロジェクトから派生させれば、何の気苦労も無く利用できるようになります。
SDK一式をコピーし、その中に自分のプラグイン用のディレクトリを作り、sample ディレクトリの中身は全部消して、.netplugins.vcpprj をクリックして起動し、自分が作りたいプラグインのタイプに最も近い構成から構成をコピーし名前を付け、その他の構成は、全て削除します。そして、利用するソースを指定して、その他のサンプルソースはプロジェクトから全て削除します。これで一応の環境は完成です。私はこの時点の構成は公開用として release と名前を付けてます。
私は、さらにもう1つ構成を作り、それを debug と名前を付けてデバッグ用にします。 この debug 用構成のコンパイラスイッチ部に

/D "DEBUG"
を追加し、
利用するプログラムに shade の実行形式のフルパスを指定します。
ソースには、
デバッグ用したい部分に
#ifdef "DEBUG"
......
#endif

として設計し、デバッグ中は、デバッグ構成で作ったdllで実行させデバッグします。 デバッグメニューの開始を選ぶと、「shadeのデバッグ用ソースがないからその部分のデバッグが出来ない」みたいなワーニングが出ますが、無視して構いません。割と気楽にブレイクポイントを設定して、F5:F10:F11を使ったステップ実行やブレイクポイントの再設定で再実行など、特に問題なく実行できます。期待通りの動作が出来たら、公開用の release 構成でメイクすれば、デバッグ用のソース部分を取り除かないでリリース用のdllが完成します。