資料室 ページ指定表示
資料室 ページ指定表示
 ■Report Liblary..._____________
| HOME | | 最新3ページ | | 最新100リスト | | 10回毎一覧 | | ANDOR  | | 辞書 |

Close

Only
000036.ブーリアン処理の精度アップに向けて 
  (2003年07月16日(水) 02:15)

Open
メタセコイア用ブーリアンプラグイン:boolean.lzh
解凍して、metasequoiaをインストールしたフォルダのPlugins\Object\の中に入れてください。
オブジェクトメニューに下記コマンドが追加されます。
・「ブーリアン」
・「形を維持して頂点を最適化」

●最適化機能を見なおし
以前、ブーリアンの副産物で「形を維持して最適化」の処理を作り、
そのままプラグインとしていたが、閉じたオブジェクト(ソリッド)で行なわないと
必要な面まで削除されてしまうことがあった。
まあ分かっていたことなので、ソリッドで実行することが前提と表記はしていたが。
不便なので、この際高速化と共に精度アップを行なった。

以前の方法だと、各点毎に、その点Aと辺を構成するもう1点Bを調べ、
この点Bを使う面の法線が、その辺ABを使う面の法線のいづれかに含まれていれば
点Bは点Aにマージ出来るとしていた。

ソリッドであれば上記条件で問題無いが、そうでない場合、辺が1面でしか使われていない部分で
マージを行なってしまい、面が消える原因となっていた。
また、マテリアルも考慮していないし、4頂点の面は無視していた。

今回は新しく下記条件を付けた。
・点Bを使う面の数が、辺ABを使う面の数より多い
・辺ABを使う面の法線と同じ法線を持つ面が、点Bを使う(Aは含まない)面に1つ以上存在する
・同一法線の面は同一マテリアルである
・点Bの角度(Bを使う面のBの角度の合計)が180度の整数倍になる、もしくは、
 辺ABと同じベクトルを持つ辺が、点Bを使う面に含まれる


●デローニー三角分割法のおさらい
点ABCの三角形があるとき、外接円の中心点をPとし、その求め方は、
3辺それぞれの中心から、各自の辺と直角に交わる線を引き、それらが交わった点になる。
3辺とも計算する必要はなく、いづれか2辺で中心点Pは求めることが出来る。
半径rは当然r=|P−A|である。
そして、実は難しい2辺の交点。
2次元なら分かるが、3次元は結局理解出来なかったので、下記方法で計算した。
まず、三角形ABCの法線Nを求め、そこから三角形A、B+N、B−Nの法線先Nabを求める。
同じく、三角形B、C+N、C−NからNbcを求める。
辺ABの中点Pab=(B−A)/2と、Nabで、
三角形Pab、Nab+N、Nab−Nを作りFabとする。
さらに辺BCの中点Pbc=(C−B)/2から、辺Pbc、Pbc+Nbcを作りLbcとする。
Fabを無限の面、Lbcを無限の直線とするとき、それらの交点がPとなる。
もうちょっと簡単に求める方法がありそうなものだが、よく分からないのでこれで。
下図は左が上面図、右が適度な視点からの図。



●4頂点面の構成条件
ブーリアン計算時、交点の存在する4頂点の面は全て3頂点の面にしている。
通常は、交点計算、分割、結線修正、最適化を行なっているが、
「4頂点」をチェックすることにより、上記処理の後に4頂点面の再構成を実行する。

3頂点の面を4頂点にする条件は以下の通り。
 @.1辺(2頂点)を共有している
 A.共有している辺と、共有していない辺が交差する
 B.法線方向が同じ
 C.マテリアルが同じ

Aについてはメタセコ自体が対応してないようなので、条件とする必要がある。
↓右上から左下への線が外に出てしまうが、可能な方向もある。↓
↓左上から右下への線が外に出てしまい、おかしな面が表示される。↓
共有されている辺の検索には、SDK付属のMQ3DLibにある、class MQObjEdgeを利用する。 先述した、辺が交差しているかの処理は依存コードを含んでしまい、纏めきれないので省略します。
//---------------------------------------------------------------------------
// 3頂点から4頂点面構成
//---------------------------------------------------------------------------
void RefreshQuad( MQObject Obj ) {                    //4頂点化
    int face_count;                                    //面数
    int Aint[4], Bint[4];                            //頂点インデックス
    MQPoint Pa[4], Pb[4];                            //頂点
    MQPoint Nml;                                    //法線
    int Alp, Blp, lp;                                //カウンタ
    int Fno, Lno, Mtr;                                //面、辺のインデックス、マテリアル
    bool *Fbl, Crs;                                    //面の使用/未使用、交差フラグ
    MQObjEdge *edge = new MQObjEdge( Obj );            //共有エッジ取得

    face_count = Obj->GetFaceCount();
    Fbl = new bool[face_count];
    for( Alp = 0; Alp < face_count; Alp ++ ) {        //面の数だけ処理を行う
        Fbl[Alp] = false;
    }
    for( Alp = 0; Alp < face_count; Alp ++ ) {        //面の数だけ処理を行う
        if( Obj->GetFacePointCount( Alp ) != 3 )  continue;
        if( Fbl[Alp] )  continue;
        Obj->GetFacePointArray( Alp, Aint );
        Mtr = Obj->GetFaceMaterial( Alp );
        for( lp = 0; lp < 3; lp ++ ) {
            Pa[lp] = Obj->GetVertex( Aint[lp] );
        }
        Nml = GetNormalB( Pa[0], Pa[1], Pa[2] );
        for( Blp = 0; Blp < 3; Blp ++ ) {
            if( edge->getPair( Alp, Blp, Fno, Lno ) ) {    //面、辺取得
                if( Obj->GetFacePointCount( Fno ) != 3 )  continue;
                if( Fbl[Fno] )  continue;
                Obj->GetFacePointArray( Fno, Bint );
                for( lp = 0; lp < 3; lp ++ ) {
                    Pb[lp] = Obj->GetVertex( Bint[lp] );
                }
                //***!注意!***依存コード利用のため、下記処理を省略しています。
                //辺(Pa[Blp], Pa[(Blp+1)%3])と、辺(Pa[(Blp+2)%3], Pb[(Lno+2)%3])が交差しているならばcontinue;
                //**********
                if( (Nml-GetNormalB( Pb[0], Pb[1], Pb[2] )).abs() > Limit )  continue;    //法線が同一でない
                if( Mtr != Obj->GetFaceMaterial( Fno ) )  continue;                        //マテリアルが同一でない
                switch( Blp ) {
                case 0:
                    Aint[3] = Aint[2];
                    Aint[2] = Aint[1];
                    Aint[1] = Bint[(Lno+2)%3];
                    break;
                case 1:
                    Aint[3] = Aint[2];
                    Aint[2] = Bint[(Lno+2)%3];
                    break;
                case 2:
                    Aint[3] = Bint[(Lno+2)%3];
                    break;
                }
                Obj->SetFaceMaterial( Obj->AddFace( 4, Aint ), Mtr );
                Fbl[Alp] = true;
                Fbl[Fno] = true;
                break;
            }
        }
    }
    for( Alp = 0; Alp < face_count; Alp ++ ) {        //面の数だけ処理を行う
        if( Fbl[Alp] )  Obj->DeleteFace( Alp );
    }
    Obj->Compact();
    delete Fbl;
    delete edge;
}
●同一法線の重なった面のマージ 互いに同一無限平面状に存在し、一部の領域が重なっている面の計算をしなければならない事がある。 これまで通り、各線がどこかの面と交差していないか調べ、その交点で分割するという方法では 交点座標が複数あったり、float型の限界で求める事が出来なかったりしておかしな形になっていた。 (2003/07/15現在のboolean.dllで形が崩れるのは、重なった面の処理か、float落ちのどちらかと思われます) 重なった面だけは別途処理する必要がある。 ぴったり重なっているならメタセコ標準機能でも対処出来る。 booleanの中では下記のように処理している。
//---------------------------------------------------------------------------
// 重なった面を削除
//---------------------------------------------------------------------------
void EqualNormalFaceDelete( MQObject Obj ) {
    int pf, pl, lp, no, xx, yy, face_index = Obj->GetFaceCount(), line_index;
    int Fint[2][4];
    bool *Del;
    MQObjEdge *edge = new MQObjEdge( Obj );
    Del = new bool[ face_index ];
    for( lp = 0; lp < face_index; lp ++ )  Del[lp] = false;

    for( lp = 0; lp < face_index; lp ++ ) {
        line_index = Obj->GetFacePointCount( lp );
        if( line_index == 2 )  line_index = 1;            //エッジは1本なので1にする
        for( no = 0; no < line_index; no ++ ) {
            if( edge->getPair( lp, no, pf, pl ) == false )  continue;
            if( Del[pf] )  continue;
            Obj->GetFacePointArray( lp, Fint[0] );        //インデックス取得
            Obj->GetFacePointArray( pf, Fint[1] );
            pl = 0;
            if( line_index == 1 )  line_index = 2;        //頂点数は2に戻す
            for( xx = 0; xx < line_index; xx ++ ) {
                for( yy = 0; yy < line_index; yy ++ ) {
                    if( Fint[0][xx] == Fint[1][yy] )  pl ++;    //同じものがあった
                }
            }
            if( pl >= line_index ) {                    //重なった面
                Del[pf] = true;
            }
        }
    }
    //重なった面を削除
    for( lp = 0; lp < face_index; lp ++ ) {
        if( Del[lp] )  Obj->DeleteFace( lp );
    }
    delete[] Del;
    delete edge;
}
但し、完全に重なった面のみで、一部重なった面には対応が現状では行えていない。 今後の課題としたい。 ●ブーリアンプラグインの未サポートおまけ機能 いづれも未完成のため、面がおかしくなるなど不具合が多数出ると思われます。 ドロップダウンの選択肢には出てきませんが、それぞれ対応する「」内の文字を入れると処理が行えます。 ・頂点複製「:」   座標0を中心としたAオブジェクトを、Bオブジェクトの頂点に加算配置します。   Aの大きさに対して、Bの頂点と頂点の間が密集してると、場合によってはエラーで落ちます。 four3.lzh ・積算「*」   座標0を中心としたAオブジェクトを、Bオブジェクトで積算します。   Aの大きさに対して、Bの頂点と頂点の間が密集してると、場合によってはエラーで落ちます。   Bを立体にして積算すると、場合によってはエラーで落ちます。   補間部分のマテリアルが割り当たっていないのは未完成だからです。お待ちください。 four4.lzh 関連リンク: メタセコイア用ブーリアンプラグイン:boolean.lzh 資料室:デローニー三角分割 資料室:ブーリアン計算の覚え書き 資料室:Metasequoiaプラグイン「ブーリアン」β版

| HOME | | 最新3ページ | | 最新100リスト | | 10回毎一覧 |
■0x112 ■0x227 ■0x55F ■0xDDF ■0xF36 ■0xF8B ■0xEBC ■0x114 ■0x33F ■0x77F ■0xAAF ■0xEEF
____________Report Library Ver2.00β 2003/08/27_bomber@xps.jp_http://bomber.xps.jp/_