通常、特定の軸(XYZ)平面上への投影は、単に頂点の値を揃えるだけで出来ます。
例えば、全ての頂点をYZ平面上に投影したければ、X座標を0.0に揃えるだけで整列できます。
しかし、これが単純な軸平面上ではない場合、もしくは任意の移動方向に制限して投影する場合、一気に話が難しくなります。
以下は頂点座標を、任意の平面上に任意の方向から投影する関数です。
PROJ_THRESHOLD = 0.0001 -- 1
fn projToPlane plnNrm plnOfs vert vec =
(
local vecAng = dot plnNrm vec -- 2
if (abs vecAng) > PROJ_THRESHOLD then -- 3
(
local closestDist = dot plnNrm (plnOfs - vert) -- 4
local dist = closestDist / vecAng -- 5
vec * dist + vert -- 6
)
else
vert -- 7
)
引数説明
- plnNrm
- 平面の法線です。予め正規化しておく必要があります。
法線というのは面から垂直に伸びている単位ベクトルの事で、例えばYZ平面であればZ軸の方向を向いているので[1,0,0]になります。
正規化するにはnormalize関数を使用します。 - plnOfs
- 平面のオフセット値です。平面上にある一点の座標を指定します。
この一点は平面上に有りさえすれば、何処を指定していても問題ありません。 - vert
- 投影前の頂点座標です。
- vec
- 投影を行う方向ベクトルです。
このベクトルも正規化されている必要があります。
またこのベクトルが面と完全に平行だった場合、処理はキャンセルされます。(後述)
処理内容説明
- 1. PROJ_THRESHOLD = 0.0001
- 面とvecが平行だった時、どの程度までなら処理するかの許容値を設定しています。
1.0を完全に垂直、0.0を完全に平行とし、0.0以外の値を設定します。 イメージしてみると分かりますが、面と移動ベクトルが完全に平行だと、頂点は面にぶつからず処理不能となります。 - 2. local vecAng = dot plnNrm vec
- plnNrmとvecがどの程度同じ向きを向いているかを計算しています。
全く同じ向きなら1.0、正反対を向いていれば-1.0、直角なら0.0を返します。
いわゆる内積演算です。 - 3. if (abs vecAng) > PROJ_THRESHOLD then
- 2の計算結果の絶対値がPROJ_THRESHOLDより小さければ、処理をキャンセルしています。
- 4. local closestDist = dot plnNrm (plnOfs - vert)
- 頂点と平面との再接近距離を計算しています。
具体的にはplnOfsからvertへ向かうベクトルを計算し、plnNrmと内積を取っています。
こうすることで面と頂点との距離を計算しています。不思議ですね。 - 5. local dist = closestDist / vecAng
- 再接近距離をvecAngで割る事で、vec方向に移動した時の衝突距離を計算しています。
そもそもvecAngは移動ベクトルと面法線の一致度である為、この2つが違えば違う程、小さな値(0に近い値)になっていきます。 結果、distはより大きな値になります。
ただし0.0の時(完全に平行な時)は結果が無限大になってしまう為、処理不可です。 - 6. vec * dist + vert
- 最終的な移動先座標を計算しています。
単位ベクトルであるvecと移動距離distを掛ける事によって、移動オフセットを計算する事が出来ます。 - 7. vert
- vecが面と完全に平行だった時、引数vertをそのまま返しています。
ここは、必要に応じて例外処理等に変更しても良いと思います。
実際に使ってみます。
thePoly = selection[1]
plnNrm = normalize [0.5, 0, 1]
plnOfs = [0, 0, 10]
vec = z_axis
in coordsys #world
(
for vi = 1 to (polyop.getNumVerts thePoly) do
(
local v = polyop.getVert thePoly vi
polyop.setVert thePoly vi (projToPlane plnNrm plnOfs v vec)
)
)
面の向きはXに傾いたZ方向、面のオフセットはZ+に10ずらしています。また、移動ベクトルはZ軸に指定しています。
どうでしょうか?
傾いた面上に整列しているのが分かるかと思います。
また面は原点を通っておらず、Z方向にオフセットされている事も確認出来ます。
本来であればもっと画像を多用して解説すべき記事ですが、管理人の体力が限界なのでこの辺で。