2011年12月

2011年12月27日

modo501 SDK いじってみた その74 modo501 SP4

さて、前回は歯車の各パラメータが何を表しているのかを調べてみた。今回はそれを使ってビューポートにどのように歯車の画像が生成されるかというところを調べてみたい。

で、前回に引き続きBuildToothPointList ( )から調べて行きたい。で、ざっくりこのファンクションの中を眺めると、歯車アイテムのチャンネル値→歯車形状を生成するためのデータ+歯車形状のポイントデータという変換をするもののようだ。結果はファンクションに参照渡しされている引数GearGeometry &gear、std::vector<Point> &points、unsigned &pointCount、unsigned &pointStrideに返される。

ファンクションの定義の中身を見ていくと、まずはポイントデータを格納するpointsをクリアして空のベクター配列にすることから始めている。

points.clear ();

続いて作業用のローカル変数の初期化

double rimInsetRatio = 0, wallInsetRatio = 0;
double axialShaftInsetRatio = 0, radialShaftInsetRatio = 0;
double innerSpokeInsetRatio = 0, outerSpokeInsetRatio = 0; // N/A for teeth

次にBuildGearConstruct ( ) ファンクションが呼び出される。このファンクションの役割は、GearGeometry構造体変数gearに引数から生成した値を代入することだ。

BuildGearConstruct (gear,
 teeth, coverage, shaftRad, innerRad, outerRad, thickness,
 rimInsetRatio, wallInsetRatio,
 axialShaftInsetRatio, radialShaftInsetRatio,
 innerSpokeInsetRatio, outerSpokeInsetRatio,
 contactAngle, teethFacing, bevelAngle, helicalAngle,
 toothFaceRatio, toothTiltAngle, toothGrowth);

GearGeometry構造体の定義は以下のようになっている。

struct GearGeometry
{
 // per gear
 unsigned teeth;
 unsigned coveredToothCount;
 double contactRatio;
 double bevelRadiusOffset;
 double scaledOuterRadius;
 double upperBeveledScaledOuterRadius;
 double lowerBeveledScaledOuterRadius;
 double beveledInnerToothRadius;
 double innerWallRadius;
 double toothTiltAngle;
 double outerSpokeRad;
 double innerSpokeRad;
 double scaledThickness;
 double rimThickness;
 double axialShaftRimThickness;
 double inner_spoke_thickness;
 double outer_spoke_thickness;

 // per tooth
 double apex_angle_0;
 double apex_angle_12;
 double inner_angle;
 double contact_angle_E;
 double contact_angle_N;
 double contact_angle_2;
 double contact_angle_4;
 double contact_angle_8;
 double contact_angle_10;
};

注釈を見るとわかる通りこれらのメンバー変数は歯車アイテム単位の部分と、それを構成する歯単位の部分に分かれている。そしてBuildGearConstruct ( ) は前半の歯車アイテムのメンバー変数の値を生成代入している。中身を辿って行けば各メンバー変数の導出過程が出ているわけだけど、ここで端から見て行ってもそれが何なのかを掴み辛いので、これらのパラメータが利用される段階でその導出部分をさかのぼって確認するようにしてみたい。そこでここはgearに値が入った事だけ認識してとばしておく。

次にfor文で繰り返されるブロックが始まる。見ると媒介変数 i を0〜gear.coveredToothCount−1まで繰り返すようだ。

for (unsigned i = 0; i < gear.coveredToothCount; ++i) {

ここでcoveredToothCountについて BuildGearConstruct ( ) を確認してみると、

gear.coveredToothCount = static_cast<unsigned>(teeth * coverage);

となっている。teethが歯車1周ぶんの歯数で、coverageが被覆率(歯車の中心角をパーセンテージで表した数)だ。これらを掛け合わせてunsignedでキャストすれば歯車の実際の歯数が整数で得られる。だからこのfor文は歯車の歯の数だけ繰り返されるものだとわかる。

次にBuildToothConstruct ( ) ファンクションが呼び出される。

BuildToothConstruct (gear, i);

これはパラメータを見てもなんとなくわかるように、BuildGearConstruct ( )で埋められなかったGearGeometry構造体の後半の歯単位のメンバー変数の値を導出して代入する働きをする。これもそのパラメータが使われる時に中を確認したい。

次の部分からはいよいよ実際のポイントデータを生成するためのファンクション呼び出しが6個並ぶ。これらがfor文によって歯1枚ごとに呼び出されるわけだね。その時にBuildGearConstruct ( )によって生成する歯は元となるgearのメンバー変数の値が変更されているわけだね。

PushBeveledHelicalPointPair (・・・);

PushBeveledHelicalPointPair (・・・);

PushBeveledHelicalPointPair (・・・);

PushBeveledHelicalPointPair (・・・);

PushBeveledHelicalPointPair (・・・);

PushBeveledHelicalPointPair (・・・);

そして最後にiが0の時のpoint配列のサイズをpointStrideに格納して、for文のブロックは終了だ。

if (i == 0) {
 pointStride = static_cast<unsigned>(points.size ());
}

で、このpointStrideは何かと言えば、歯1枚をあらわすために必要なポイントの数だ。1枚分ポイントを生成した時に消費した配列の要素数を調べればわかるってわけだね。

そしてfor文を抜けて歯の生成が全部終わったら、生成したポイントの総数をpointCountに格納する。

pointCount = static_cast<unsigned>(points.size ());

そして最後にポイント数に含まれない2つのポイントを生成してpoints配列に追加してこのファンクションは終了だ。

Point point;
point.vec[0] = 0.0;
point.vec[1] = 0.0;
point.vec[2] = thickness;
points.push_back (point);

point.vec[2] = -thickness;
points.push_back (point);

これは歯車の中心軸の両端の座標値らしい。thicknessは歯車の幅チャンネルから読み込んだ時に値を半分にされているので

if (!ReadFloatChannel (m_item, chanRead, CHANs_GEAR_THICKNESS, thickness))
  return LXe_NOTFOUND;
thickness /= 2;

その値は歯車の幅の1/2の値で、2つの座標値は[0,0,thickness] , [0,0,-thickness]だから2点間の距離は歯車の幅になる。

これらのポイントはpointCountを代入したあとでpointsに追加されるので歯車を構成するポイントの数pointCountとしては現れない。

ざっと流れは掴んだところで今度はポイントを生成するファンクションを個別に見て行きたい。まずPushBeveledHelicalPointPair ( ) から。呼び出し側で与えている引数は以下のようになっている。

PushBeveledHelicalPointPair (points,
  gear.apex_angle_0 + helicalAngle,
  gear.apex_angle_0, doubleHelical,
  gear.upperBeveledScaledOuterRadius,
  gear.lowerBeveledScaledOuterRadius,
  gear.scaledThickness);

gear.apex_angle_0はBuildToothConstruct ( ) で生成されていて、

gear.apex_angle_0 = TWO_PI * toothIndex / gear.teeth;

となっている。以降断りが無ければ値の導出部分はBuildGearConstruct ( ) かBuildToothConstruct ( ) の中に記述されているものと解釈して欲しい。TWO_PIは2πの定数としてgearitem.cppに定義されている。toothIndexはfor文のブロックで媒介変数になっていたiの値で、何番目の歯を扱っているかを表している。gear.teethは

gear.teeth = teeth;

となっていて、歯車1周分の歯数だ。以上からapex_angle_0は2πに歯の番号÷歯数をかけて現在処理している歯の中心角をラジアンで出しているようだ。helicalAngleは歯車を捩れた形状にするために歯の両端で角度をずらすパラメータなので、2つの引数gear.apex_angle_0 + helicalAngleとgear.apex_angle_0は歯の両端の中心角を与えることになる。helicalAngleが0なら歯の両端は同じ中心角になるわけだね。

fig01

gear.upperBeveledScaledOuterRadiusとgear.lowerBeveledScaledOuterRadiusは、

gear.innerSpokeRad = shaftRad * (1.0 + radialShaftInsetRatio);

gear.innerSpokeRad = LXxMIN (gear.innerSpokeRad, innerToothRad);

double maxBevelRadiusOffset = innerToothRad - gear.innerSpokeRad;
double gearHeight = thickness * 2;

gear.bevelRadiusOffset
 = gearHeight / tan (HALF_PI - bevelAngle);
gear.bevelRadiusOffset
 = LXxMIN (gear.bevelRadiusOffset, maxBevelRadiusOffset);
if (teethFacing == TEETH_FACING_INSIDE) {
 gear.bevelRadiusOffset = -gear.bevelRadiusOffset;
}

gear.scaledOuterRadius
= innerToothRad + (outerToothRad - innerToothRad) *   toothGrowth;

gear.upperBeveledScaledOuterRadius
= gear.scaledOuterRadius - gear.bevelRadiusOffset;

gear.lowerBeveledScaledOuterRadius
= gear.upperBeveledScaledOuterRadius +
 (gear.scaledOuterRadius - gear.upperBeveledScaledOuterRadius) * toothFaceRatio;

として求められている。いろんなパラメータが絡んでくるのでちょっと導出までが長いけど順に見ていくと、

gear.innerSpokeRadはまず歯車のシャフトの半径に「100%+半径のシャフトインセット値(%)」をかけた値を求めてシャフトハブの外半径を求めている。ハブの厚みはシャフトの半径×半径のシャフトインセット値で求まるので、それにシャフトの半径を足したものがシャフトハブの外形になるわけだ。次にこの値とinnerToothRadとのどちらか小さい方をgear.innerSpokeRadにしている。innerToothRadは歯車の内半径のことで、外歯歯車の場合は歯の谷までの半径だ。これとシャフトハブのどちらか小さい方を半径として、そこから歯車のスポークが始まるようにLXxMIN ( ) で両者を比較して小さい方をgear.innerSpokeRadに代入している。

innerToothRadとouterToothRadはCGearItemInstance::vitm_Draw ( ) で呼び出されたCalcToothRadii ( ) ファンクションで算出され

if (teethFacing == TEETH_FACING_OUTSIDE) {
 outerToothRadius = toothSpacing * teeth / PI / 2;
 innerToothRadius = outerToothRadius - toothSpacing / 2;
}
else if (teethFacing == TEETH_FACING_INSIDE) {
 innerToothRadius = toothSpacing * teeth / PI / 2;
 outerToothRadius = innerToothRadius - toothSpacing / 2;
}

となっている。toothSpacingが歯の山から山までの距離で、それにteeth(歯数)がかけられると歯車の外周の長さが算出できる。それをπで割ると歯車外径の直径が求められ、それを2で割ると半径になる。そこからtoothSpacingの半分=歯の厚みぶんを引いた距離が歯車の内径の半径といるわけだ。CalcToothRadii ( ) ではteethFacingフラグによって外歯か内歯かを切り替えてこれらの値を計算している。

という事はouterToothRad - innerToothRadは歯車の外半径から内半径を引いた距離で、これに歯たけのパラメータtoothGrowthをかけた値が実際の歯の高さになる(導出式から見れば、2つの値の差はtoothSpacing / 2だよね)。そこに歯車の内半径を足せば、実際の歯車の外半径が出る。それがgear.scaledOuterRadiusだ。

maxBevelRadiusOffsetは歯車の内半径innerToothRadからスポークの開始位置 gear.innerSpokeRadを引いた値。先に出てきた導出過程から、innerSpokeRadはinnerToothRadを超えることは無いからこの値はマイナスになることはない。通常、maxBevelRadiusOffsetの値は歯車の内径位置からシャフトハブの外側までの距離になり、それを超えてベベルがすぼむと歯がシャフトハブを削ることになっちゃうからそれの防止用のリミッタだろうね。

gearHeightはthicknessが歯車の幅の半分の値なのでその2倍は歯車の厚みになる。

gear.bevelRadiusOffsetはベベル角度から計算される歯のすぼまる量と先に算出したすぼまる事ができる限界の距離maxBevelRadiusOffsetとを比較して、小さい方をその値に採用している。角度から計算する方は歯車の軸方向の厚みを90度からベベル角度を引いた値のタンジェントの値で割っている。tan90°って無限に振り切れちゃうからこの式はどうなんだろうね。

gear.bevelRadiusOffset
 = gearHeight / tan (HALF_PI - bevelAngle);

bevelAngleが0という一番よくある場合にタンジェントの値が無限になって割り算の値がほぼ0になるんだろうけどあんまりいい感じがしない。これってこう書いても同じだよね?

gear.bevelRadiusOffset
 = gearHeight * tan (bevelAngle);

要するに直角三角形の直角を除く2つの角のどっちの角を選ぶか(互いに90度の余角の関係になっている)でタンジェントの垂辺と底辺が入れ替わるから、分母分子が入れ替わるわけだね。これで歯車の端からgearHeightぶん進んだ時にbevelAngleだけテーパー角度を取った時の斜面の下がる量が求まるわけだ。これがシャフトハブをえぐらないような数値をgear.bevelRadiusOffsetに採用しているわけだね。

gear.upperBeveledScaledOuterRadiusは歯たけを考慮に入れた実際の歯車の外半径gear.scaledOuterRadiusからベベルによって歯がすぼまる量gear.bevelRadiusOffsetを引いた値で、ベベル歯車のすぼまった方の外半径だ。

gear.lowerBeveledScaledOuterRadiusは式をよく整理してみると、

gear.lowerBeveledScaledOuterRadius
= gear.upperBeveledScaledOuterRadius +
(gear.scaledOuterRadius - gear.upperBeveledScaledOuterRadius) * toothFaceRatio
= gear.upperBeveledScaledOuterRadius +
(gear.scaledOuterRadius - gear.scaledOuterRadius + gear.bevelRadiusOffset) * toothFaceRatio
= gear.upperBeveledScaledOuterRadius +gear.bevelRadiusOffset * toothFaceRatio

となる。toothFaceRatioは歯の歯元に対する歯末の幅だ。デフォルトの値は100%でその場合の上式は以下のようになる。この時歯末と歯元の幅は一緒だ。

gear.lowerBeveledScaledOuterRadius
= gear.upperBeveledScaledOuterRadius +gear.bevelRadiusOffset

そして

gear.upperBeveledScaledOuterRadius
= gear.scaledOuterRadius - gear.bevelRadiusOffset;

なので、結局gear.lowerBeveledScaledOuterRadiusはgear.bevelRadiusOffsetが相殺されてgear.scaledOuterRadiusと等しくなる。逆にtoothFaceRatioが0%になった時はgear.upperBeveledScaledOuterRadiusと等しくなる。この値がどう使われるのかは使われている部分で見ないとわからないけどtoothFaceRatioの値によってgear.upperBeveledScaledOuterRadiusからgear.scaledOuterRadiusまで変化する値のようだ。

gear.scaledThicknessは歯車の厚みの半分の値thicknessに歯の歯元に対する刃先の厚みtoothFaceRatioをかけたもので、歯先の厚みの半分の値だ。

gear.scaledThickness = thickness * toothFaceRatio;

fig02


これらを引数として受け取ってポイント座標を算出するのが

PushBeveledHelicalPointPair (points,
 gear.apex_angle_0 + helicalAngle,
 gear.apex_angle_0, doubleHelical,
 gear.upperBeveledScaledOuterRadius,
 gear.lowerBeveledScaledOuterRadius,
 gear.scaledThickness);

なわけだ。ようやく引数だけ解釈が付いたけど長くなったので続きは次回に回すことにして、今年はここまでにしておくね。新年は1月10日から定期更新する予定。それまでは不定期更新になるよ。

それではみなさんよいお年を。

カテゴリー別ページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)modo | CG

2011年12月26日

max script 勉強してみた その47 3dsmax 2012

今年も残すところあと僅か。遣り残した事だらけで終わりそうだなぁ。

さて、コンテキスト式の続きだ。今回調べてみたのは

with defaultAction <action> <expression>

というコンテキスト式で、<expression>実行時の3ds Max イベント(missingExtFiles、missingDLLs、missingXRefs、missingUVW、unsupportedRendereffect)のアクションを#logmsg 、 #logToFile 、#abort のいずれかに設定することが出来、<expression>実行が終了すれば元の設定に戻る。

例えばレンダリングしようとしたけどマッピングで指定したチャンネルにUVWマップが無かった場合、「missingUVW」というイベントが発生して、それに対して結び付けられたアクション例えば#logToFileが実行され、エラーメッセージがログファイルに書き出されるという事らしい。

そこでそのログファイルなんだけど、マニュアルを見てもはっきりしたことがわからない。基本設定パネルの「一般」タグの「メッセージ」のところで設定すれば書き出せるのかと思ったんだけど、どうもここで設定しても何も起こらない。スクリプトのマニュアルを見るとログファイルを設定するコマンドが見つかって、

logsystem.logName "ファイル名"

としてやると、今度はそのファイルにエラーが出力出来るようになった。これと同等の事をメニューやダイアログから行うにはどうしたらいいんだろうね。ログについては基本設定パネルのマニュアルに一言「max.log」という言葉が出てくるけど、ファイル検索してもそんなファイルが生成されている様子は無い。どっかにログ書き出しをONにする設定が他にあるのかな?

でもまあとりあえずエラーメッセージは上記のコマンドで書き出せるようになったので、BOXを1つ作って、それにマテリアルをセットして、テクスチャマッピングでマッピングチャンネルを2にしてマップの無いマッピングを施して

fig02 

レンダリング時にエラーが出るようにしてみた。下がレンダリングした時に出るエラーダイアログボックス。

fig01

そして先にコマンドで指定したログファイルを開いてみると、タイムスタンプと共に以下のメッセージが書き込まれていた。

ERR: オブジェクト (UVW 2): Box001 はテクスチャ座標が必要で、これがないと正しくレンダリングされないことがあります

ここでUVマップが見つからなかったというエラーイベントに対するアクションを変えてみる。

defaultActions.setAction #missingUVW #(#abort)

この関数は指定したイベントに指定したアクションのリストを結びつけるもので、イベントにアクションが何も設定されていなければfalseを返し、先に何か設定されていればtrueを返す。上の例では#missingUVWに#abortを割り当てた。

これで改めてレンダリングしてからログを見てみると確かにUVWについてのエラーログが書き込まれなくなった。次に

with defaultAction #logToFile (
 render()
)

としてみた。すると今度はエラーメッセージが書き込まれた。どうやら<expression>の中でdefaultActionを設定出来た感じだ。

ところで3つのアクションだけど、これも詳しい説明は付いていない。で、いろいろやってみた結果、

  • #abort:ログを残さない
  • #logmsg:defaultActionsにメッセージログとして記録
  • #logToFile:ログをファイルに書き出す

という感じみたいだ。だから#abortにすれば何も残らないし、#logToFileでは上の例のようにファイルに出力される。そして#logmsgの場合は、

<integer>defaultActions.getMsgLogCount <enum>eventID

のようにすれば、イベントIDに対してメッセージがいくつ格納されているかを確認できて、

<TSTR by value>defaultActions.getMsgLogMsg <enum>eventID <index>index

としてやればindexで示すエラーログが取得できるようだ。例えば

defaultActions.setAction #missingUVW #(#logmsg)

としてからレンダリングをしてエラーを出せば、

defaultActions.getMsgLogCount #missingUVW
1
defaultActions.getMsgLogMsg #missingUVW 1
"(UVW 2): Box001"

とログが取得できる。

それではまた次回。

maxまとめページ



take_z_ultima at 12:40|この記事のURLComments(0)TrackBack(0)3ds Max | CG

2011年12月24日

modo用年賀素材をちょっとだけアップしました modo 501

パソコン落ちまくりで調子悪い・・・。今年もかなりのやっつけになったけど、どうにか年賀状も完成したのでそこで使った素材をちょっとだけアップしといたよ。

fig01

fig02

modo501用なのでそれ以外の人は使えないけどね。

ダウンロードはこちら。



take_z_ultima at 15:38|この記事のURLComments(0)TrackBack(0)modo | CG

2011年12月22日

modo501 SDK いじってみた その73 modo501 SP4

前回に引き続き CGearItemInstance::vitm_Draw ( ) だ。

前回パラメータを読み込むところまで終わった。今回はその続きから。

if (selectionFlags & LXiSELECTION_SELECTED ||
  selectionFlags & LXiSELECTION_ROLLOVER) {
  lineWidth = 2.0;
}
else {
  lineWidth = 1.0;
}

このコードは前のorbの時も出てきたけど、アイテムにマウスが乗っかるロールオーバーの時とアイテムが選択されている時に線の太さを2にして、選択もロールオーバーもしていないときは1にするための処理だ。selectionFlagsはvitm_Draw ( ) に渡される引数で現在のアイテムの選択状態を渡してくれるんだったね。結果として設定されるlineWidthは後で線を引くときに参照される。

次にgear、pointStride、pointCountが宣言されて、それらとpointsを参照渡しするBuildToothPointList ( ) 関数が呼び出される。パラメータとチャンネルの関係はこんな感じだ。

GearGeometry gear;
unsigned pointStride, pointCount;
BuildToothPointList (
  gear, 
  points,
  teeth, // 歯数
  coverage, //被覆率
  shaftRad, //シャフト
  innerToothRadius,
  outerToothRadius,
  thickness, //厚み 
  contactAngle, //接触角度
  teethFacing, //歯の向き
  bevelAngle, //べベル角度
  helicalAngle, //傾斜角度
  doubleHelical, //やま歯
  toothFaceRatio, //面の比率
  toothTiltAngle, //回転の傾斜角度
  toothGrowth, //歯たけ
  pointCount,
  pointStride
);

ここでチャンネルと歯車形状の関係を調べておこう。

被覆率」は1周360度を100%とした時の歯車の中心角だ。100%より小さい値にすると歯車の一部が欠けて下のような扇形になる。

fig01

歯の間隔」は歯と歯の距離。
接触角」は歯の側面の傾き(歯の形状がインボリュート曲線にはなっていない)。
シャフト」はシャフト穴の半径。スポークはスポークの数。

fig02

スポーク有り」と「リム有り」は、それぞれON/OFFできる。

スポーク有り:ON リム有り:OFF

fig03

スポーク有り:OFF リム有り:ON

fig04

スポーク有り:ON リム有り:ON

fig05

もちろん両方ともOFFにできて、ハブから歯車が浮いた状態になる。

fig06

これはウォールインセント比を小さくして歯車の内側の円をハブの面まで持ってきて連結する場合に使う感じかな。

fig07

厚み」は歯の軸方向の幅。
リムインセット比」は歯の厚さに対するリム面の位置。1%が最小値でリム面がほぼ歯の中央に集まり、リムの厚さがほぼ0になる。100%で歯の幅と同じになってリムと歯の面の段差が無くなり、100%を超えるとリム面が歯より突出する。
ウォ−ルインセット比」はリム部分の半径と歯車の底面の半径の比。5%〜99%まで変えられる。大きくなるほどリム部分が大きくなる。歯の向きが内側になった場合は100%で内歯の底面の径と一致し、0%で歯車の外径が内歯の底面の直径の倍になる。
軸に対するシャフトインセット比」は歯幅に対するシャフトハブの長さの比。100%より大きく取るとハブが歯幅より突出し、100%より小さければ歯幅よりくぼむ。リムインセット比とこの比の大小によってリムとハブの段差が変わる。
半径のシャフトインセット比」はシャフト径に対するハブの厚みの比。100%にするとハブの径がシャフトの径の倍になる。

fig08

内側のスポークインセット比」はスポークの内側の厚みと歯幅の比。ただしシャフトハブの高さは越えないので「軸に対するシャフトインセット比」が100%より小さい場合はこの値が100%でも歯幅にはならない。
外側のスポークインセット比」はスポーク外側の幅と歯幅との比。
「スポークスイープ比」はスポークの扇型の中心角。隣り合うスポークどうしの成す角の半分を100%とした時の中心角の割合で表される。200%(には出来ないけど、)で隣のスポークと接続する。
「スポーク捩れ角」はスポークの外側の位置の角度のオフセット。隣り合うスポークの中心角を100%とした時の中心角のオフセットの割合で表される。100%で隣のスポークの位置になる。

fig09

「歯の向き」は内側と外側が選択できる。

fig10

べベル角度」は歯面を円筒の側面から円錐の側面に傾ける。
傾斜角度」は歯を厚み方向でずらす。

fig11

やま歯」をONにすると「ベベル角度」「傾斜角度」の効果が歯の中央で反転して対称形状になる。

fig12

面の比率」は歯幅に対する歯末の歯幅。100%より小さくすれば先細りになる。
回転の傾斜角度」は歯を回転方向に倒す。
歯たけ」は歯の間隔に対する歯の高さの比率。0%で歯がなくなる。

fig13

解像度」は曲面部分の分割数を設定する値だ。大きくなるほどきれいな曲面になるけどメモリの消費量も増える。

fig14

ふぅ・・・。パラメータだけで物凄い数だな。でも実際こんな歯車があってもかみ合わせて回転させたら互いにガリガリ削れて凄い振動が発生するだろうね。

それではまた来週。

カテゴリー別ページ



take_z_ultima at 11:46|この記事のURLComments(0)TrackBack(0)modo | CG

2011年12月21日

max script 勉強してみた その46 3dsmax 2012

今年は本当にいろんな人達が逝ってしまったなぁ・・・
森田芳光監督のご冥福をお祈りいたします。

前回のcoordsysを使ったプログラムはmoveを使うものからposパラメータに直接座標値を代入するものに書き換えたよ(それからcoordsysの部分もひとくくりにまとめた)。その方がわかりやすいもんね。

さて、今回はaboutコンテキスト式。

fig01これはメインツールバーの[回転/スケール]の変換中心リストに相当する機能だ。これによってどこを中心に回転やスケールをするのかを指定することが出来る。

aboutコンテキスト式で指定できる中心は以下の通り。

  • about selection
  • about pivot
  • about coordsys
  • about <node>
  • about <matrix3>
  • about <point3>

[回転/スケール]の変換中心のボタンで言うと、

fig02基点中心を使用が「pivot」で、

fig03選択部分の中心を使用が「selection」で、

fig04変換座標の中心を使用が残り全部(coordsys,<node>, <matrix3>,<point3>)

って事かな。

例えば下のように3つのBOXを用意して、3つとも選択状態にしておいてそれぞれの方法で回転させてみた。

fig08

pivotは基点中心だから下のコードを実行すると、BOXそれぞれの基点(デフォルトで底面の中心にある)を中心に回転した。

about pivot rotate $ 30 x_axis

これがその結果。

fig05

selectionは選択の中心だから選択されているBOX3つのオブジェクトの平均位置が中心点になった。BOXの基点はBOXの底面にあるけど、選択の中心はオブジェクトを囲むバウンディングボックスの中心位置が基準になるので、中心点のZ座標はグリッド面から浮いた位置になった。

about selection rotate $ 30 x_axis

これがその結果。

fig06

<node><matrix3><point3>はそれらが示す座標値を中心に回転やスケールをする。<node>、<Matrix3>であればpositionプロパティであり、<point3>はそのままだ。下のコードはBox003の基点を中心に回転させてみたものだ。

about $box003 rotate $ 30 x_axis

これがその結果。

fig07

coordsysは現在の参照座標系を使うという事なので、何も指定しなければワールド座標系が基点になる。coordsysコンテキスト式と一緒に使えばいいんだろうね。例えば参照座標系をペアレントの親座標にして中心座標をその座標系に従うようにして回転させるには以下のようにすればいい。

in coordsys parent about coordsys rotate $ 20 x_axis

下のシーンは矢印のようにペアレントして上の2つのBOXを選択してある。

fig09

下はこの状態で上の式を実行したところ。各BOXの基点は底面の中心にあり、選択した2つのBOXはその基点がペアレントした親の基点を中心にそれぞれ20度回転しているのがわかる。

fig10

それではまた次回。

maxまとめページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)3ds Max | CG
Archives