チュートリアル > ビットマップ ペイント ツールの開発 - UV 座標のアンラップ |
このビットマップ ペイント ツール開発の手順では、ペイント ツール内にあるオブジェクトの UV 座標のグラフィック表示を生成するオプションを追加し、それを拡散反射光マップとしてシーン オブジェクトに自動的に割り当てます。
--Code in italic has no changes since the previous version. macroScript MicroPaint category:"HowTo" ( global MicroPaint_CanvasRollout try(destroyDialog MicroPaint_CanvasRollout)catch() local isErasing = isDrawing = false local bitmapX = bitmapY = 512 local bitmapx_1 = bitmapx-1 local bitmapy_1 = bitmapy-1
ビットマップ内の UV 座標の位置を計算するには、ピクセルをビットマップのサイズ - 1 としてカウントする必要があります (たとえば 1 ~ 512 ではなく、0 ~ 511)。簡単にするために、これらの正しい値を格納する変数を 2 つ定義します。
local temp_bitmap_filename = (getDir #preview +"/microPaint_temp.tga") local theCanvasBitmap = bitmap bitmapX bitmapY color:white filename:temp_bitmap_filename
現在のペイントをディスク上の一時ファイルに保存できるようにするには、ファイル名を定義してビットマップに割り当てる必要があります。ファイル名はローカル変数に格納され、スクリプトの他の箇所でそのファイルにアクセスする場合にはそれが使用されます。BMP よりは TGA で保存した方が速くできるようですが、保存はどの形式で行ってもかまいません。
local theBackgroundBitmap = bitmap bitmapX bitmapY color:white local currentPos = lastPos = [0,0] localtheChannel = 1
読み込み対象のマップ チャネルです。この値をコントロールするための、ユーザ インタフェース スピナーを追加することができます。この時点ではチャンネル 1 のみを扱います。
この変数は、UV 座標にアクセスするメッシュ オブジェクトを格納します。
rcMenu CanvasMenu ( subMenu "File" ( menuItem new_menu "New" menuItem open_menu "Open..." menuItem save_as "Save As..." separator file_menu_1 menuItem quit_tool "Quit" ) subMenu "Edit" ( menuItem commit_menu "Commit Changes" separator edit_menu_1 menuItem uv_menu "Get UV Coordinates..." )
シーン オブジェクトからテクスチャ座標を取得するために使用する、新しいメニュー項目を追加します。
on commit_menu picked do copy theCanvasBitmap theBackgroundBitmap on uv_menu picked do MicroPaint_CanvasRollout.unwrapTexture()
ユーザがこのメニュー項目を選択した場合、新しい関数を呼び出します。新しい関数はロールアウト内に定義します(下記参照)。
subMenu "Help" ( menuItem about_tool "About MicroPaint..." ) on new_menu picked do ( theBackgroundBitmap = theCanvasBitmap = bitmap bitmapX bitmapY color:MicroPaint_CanvasRollout.paperColor.color filename:temp_bitmap_filename MicroPaint_CanvasRollout.theCanvas.bitmap = theCanvasBitmap ) on open_menu picked do ( theOpenBitmap= selectBitmap() if theOpenBitmap != undefined do ( copy theOpenBitmap theCanvasBitmap copy theOpenBitmap theBackgroundBitmap close theOpenBitmap MicroPaint_CanvasRollout.theCanvas.bitmap = theCanvasBitmap ) ) on save_as picked do ( theSaveName = getSaveFileName types:"BMP (*.bmp)|*.bmp|Targa (*.tga)|*.tga|JPEG (*.jpg)|*.jpg" if theSaveName != undefined do ( theCanvasBitmap.filename = theSaveName save theCanvasBitmap theCanvasBitmap.filename = temp_bitmap_filename
新しいファイル名で保存した後、ファイル名を元の一時的な場所に戻します。こうしないと、自動保存により新しいファイルに書き出しが行われてしまいます。
) ) on about_tool picked do messagebox "MicroPaint\nMAXScript Tutorial" title:"About..." on quit_tool picked do destroyDialog MicroPaint_CanvasRollout ) fn mesh_filter obj = superclassof obj == GeometryClass and classof obj != TargetObject
この関数により、ジオメトリ オブジェクトだけが選択できるようになります。 ただし、ターゲット オブジェクトは選択できません。この関数は新しいユーザ インタフェース コントロールである pickbutton でフィルタ関数として使用されます。
rollout MicroPaint_CanvasRollout "MicroPaint" ( bitmap theCanvas pos:[0,0] width:bitmapX height:bitmapY bitmap:theCanvasBitmap colorpicker inkColor height:16 modal:false color:black across:6
新しい自動保存オプションが収まるように、across パラメータを 6 に増やします。
この新しいチェックボタンでは、ペイントが各ストロークの後で常にディスクに保存されるかどうかをコントロールします。
checkbutton airBrush "AirBrush" width:70 spinner AirBrushSpeed "Speed" range:[0.1,50,10] fieldwidth:30 spinner BrushSize "Size" range:[1,50,1] type:#integer fieldwidth:40
既定のブラシ サイズは 1 になりました。10 の厚さでアンラップしたテクスチャの描画を避けるためです。10 の厚さではテクスチャ アンラップの描画にとても時間がかかる可能性があります。
listbox BrushShape items:#("Circle", "Box", "Circle Smooth") pos:[bitmapX+5,0] width:90 pickbutton pickMesh "Pick Mesh" width:90 height:30 pos:[bitmapX+5,140] filter:mesh_filter autodisplay:true
この pickbutton は、テクスチャ座標をアンラップするシーン オブジェクトを選択するときに使用するものです。3ds Max 7 で追加された新しいオプションのキーワード、autoDisplay を使用します。true に設定されると、選択されたオブジェクトの名前が自動的にボタンに表示されます。3ds Max 6 以前のリリースでは、このオプションは無視されますが、それでもスクリプトは正しく機能します。
これは、pickbutton のイベント ハンドラです。ユーザがオブジェクトを選択した場合、obj 変数のハンドラに渡されます。
obj 変数に有効な値が入っている場合(ユーザが選択をキャンセルしなかった場合)、
まず選択オブジェクトを新しい変数に割り当てます。この変数はペイント セッションの間、選択を保存します。
try ( copy theObj.material.diffusemap.bitmap theCanvasBitmap copy theObj.material.diffusemap.bitmap theBackgroundBitmap theCanvas.bitmap = theCanvasBitmap
次に、オブジェクトの diffusemap ビットマップを読み込み、それをペイント ツールのキャンバスおよびバックグラウンドのビットマップにコピーして UI に割り当てます。
ビットマップがない場合、エラーはトラップされ何も起こりません。
)catch() ) ) fn paintBrush pos = ( if isErasing then thePaintColor = (getPixels theBackgroundBitmap pos 1)[1] else thePaintColor = inkColor.color if thePaintColor == undefined then thePaintColor = white case BrushShape.selection of ( 1: ( if distance pos currentPos <= BrushSize.value/2 do setPixels theCanvasBitmap pos #(thePaintColor) ) 2: setPixels theCanvasBitmap pos #(thePaintColor) 3: ( theFactor = (distance pos currentPos) / (BrushSize.value/2.0) if theFactor <= 1.0 do ( theFactor = sin ( 90.0 * theFactor) thePixels = getPixels theCanvasBitmap pos 1 if thePixels[1] != undefined do ( thePixels[1] = (thePixels[1] * theFactor) + (thePaintColor * (1.0 - theFactor)) setPixels theCanvasBitmap pos thePixels ) ) )--end case 3 )--end case )--end fn fn drawStroke lastPos pos drawIt: = (
drawStroke 関数に新しいオプションのキーワードを追加します。このキーワードが false に設定された場合、ストロークは描画されますが、ロールアウト内のビットマップは更新されません。このキーワードが true の場合、またはこのキーワードを省略した場合は、ビットマップはストロークの後に更新されます。これは、テクスチャ面ごとにビューを更新せずに、アンラップされたテクスチャをより速く描画できるようにするためです。
currentPos = lastPos deltaX = pos.x - lastPos.x deltaY = pos.y - lastPos.y maxSteps = amax #(abs(deltaX),abs(deltaY)) deltaStepX = deltaX / maxSteps deltaStepY = deltaY / maxSteps for i = 0 to maxSteps do ( if airBrush.checked then for b = 1 to (BrushSize.value / AirBrushSpeed.value) do paintBrush (currentPos + (random [-BrushSize.value/2,-BrushSize.value/2] [BrushSize.value/2,BrushSize.value/2] )) else for b = -BrushSize.value/2 to BrushSize.value/2 do for c = -BrushSize.value/2 to BrushSize.value/2 do paintBrush (currentPos + [c,b]) currentPos += [deltaStepX, deltaStepY] ) if drawIt== true or drawIt == unsupplied do theCanvas.bitmap = theCanvasBitmap
新しいスイッチが true に設定された場合、または何も設定されない場合、前と同じように表示を更新します。それ以外の場合、更新はスキップされます。
オブジェクトの選択、テクスチャ座標のアンラップ、およびマテリアル/テクスチャ割り当てを実行する新しい関数です。
メッシュ内の面のそれぞれについて、次のコードを繰り返します。
これは、マップ面の定義です。Point3 値を返します。x、y、z はその面が使用するテクスチャ頂点を指すインデックスです。
vert1= meshop.getMapVert theMesh theChannel theFace.x vert2= meshop.getMapVert theMesh theChannel theFace.y vert3= meshop.getMapVert theMesh theChannel theFace.z
このデータを使用して、面の 3 つの頂点を読み込みます。それぞれが、実際の U、V、W の値を含む Point3 値です。ただし、描画するのは UV だけで、W は破棄されます。
drawStroke [vert1.x * bitmapx_1, bitmapy_1- vert1.y * bitmapy_1] [vert2.x * bitmapx_1, bitmapy_1- vert2.y * bitmapy_1] drawIt:false
最初と 2 つ目のテクスチャ頂点の座標をビットマップのサイズで乗じたものを使用して drawStroke 関数を呼び出します。drawIt フラグは、イメージ生成を加速するために false に設定されます。フラグを true に設定すると、関数がイメージに対してインタラクティブに描画されますが、かなり時間がかかる場合があります。
座標の計算は、UV 空間の左下隅が 0,0 で、右上隅が 1,1 という座標であるという事実に基づいて行われます。
もう一方の手のビットマップの左上隅は 0,0 で、右下隅は bitmapx-1,bitmapy-1 です (この例では 511,511)。
UV 座標をピクセル座標に変換するには、ビットマップの幅および高さを U および V の値でそれぞれ乗算し、高さから引くことで垂直方向の高さを逆にします。
drawStroke [vert1.x * bitmapx_1, bitmapy_1- vert1.y * bitmapy_1] [vert3.x * bitmapx_1, bitmapy_1- vert3.y * bitmapy_1] drawIt:false drawStroke [vert3.x * bitmapx_1, bitmapy_1- vert3.y * bitmapy_1] [vert2.x * bitmapx_1, bitmapy_1- vert2.y * bitmapy_1] drawIt:false
残りの 2 つの頂点についても drawStroke 関数を呼び出します。こうして面のすべてのエッジを描画します。
ここでロールアウトのビットマップを更新し、結果を表示します。
選択したオブジェクトにマテリアルがない場合、新しいマテリアルを割り当てます。
try (if theObj.material.diffusemap == undefined do theObj.material.diffusemap = bitmapTexture filename:temp_bitmap_filename
選択したオブジェクトの diffusemap がまだ定義されていない場合は、ペイントの一時コピーを指す新しいテクスチャ マップを割り当てます。オブジェクトに標準的なマテリアルが割り当てられていない場合、diffusemap チャネルにアクセスを試みるとエラーが発生します。これは try()catch() コンテキストで防止します。
マテリアルのオブジェクト レベルで[ビューポート内でマップを表示](Show Map In Viewport)を押します。テクスチャの割り当てに成功した場合、ビットマップがビューポートに表示されます。
すべて問題なく進んだ場合、新しい自動保存オプションをオンにして、選択したオブジェクトが描画に並行してビットマップで強制的に更新されるようにします。
ユーザが新しいチェックボタンを選択した場合、ビットマップはただちに一時的な保存場所に保存されます。
on MicroPaint_CanvasRollout lbuttondown pos do ( lastPos = pos isDrawing = true isErasing = false drawStroke lastPos pos ) on MicroPaint_CanvasRollout rbuttondown pos do ( lastPos = pos isErasing = isDrawing = true drawStroke lastPos pos ) on MicroPaint_CanvasRollout lbuttonup pos do ( isErasing = isDrawing = false if autoSave.checked do save theCanvasBitmap
新しいチェックボタンが押されている場合、ビットマップは各ストロークの後に一時的な保存場所に保存されます。
) on MicroPaint_CanvasRollout rbuttonup pos do ( isErasing = isDrawing = false if autoSave.checked do save theCanvasBitmap
新しいチェックボタンが押されている場合、ビットマップは各ストロークの消去後に一時的な保存場所に保存されます。
) on MicroPaint_CanvasRollout mousemove pos do ( if isDrawing do drawStroke lastPos pos lastPos = pos ) ) createDialog MicroPaint_CanvasRollout (bitmapx+100) (bitmapy+30) menu:CanvasMenu MicroPaint_CanvasRollout.theCanvas.bitmap = theBackgroundBitmap )
この例では、ティーポットに[UVW アンラップ](Unwrap UVW)モディファイヤが適用され、新しいテクスチャ座標を作成するためにフラッテン関数が使用されています。
その後、MicroPaint でオブジェクトが選択され、サイズ 5 の円形のスムーズ ブラシと、オンになって[Airbrush Speed]が 1.0 に設定されたエアブラシを使用してテクスチャ座標が描画されています。
ここで、利用可能なブラシをどれでも使用して UV 座標の上にペイントすることができます。[AutoSave]ボタンが選択されていれば、ストロークはすべてビューポート内で更新されます。