チュートリアル > ビットマップ ペイント ツールの開発 - 3D ペイント |
ビットマップ ペイント ツール開発の手順では、3ds Max ビューポート内のシーン オブジェクトに直接ペイントするオプションを追加します。
全体の流れ: |
ユーザ インタフェースに、3D ペイントの有効と無効を切り替えるチェックボタンを追加します。 |
PainterInterface を使用して、ビューポート内でブラシをコントロールする単純な関数をいくつか定義し、ペイントする面の 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 local temp_bitmap_filename = (getDir #preview +"/microPaint_temp.tga") local theCanvasBitmap = bitmap bitmapX bitmapY color:white filename:temp_bitmap_filename local theBackgroundBitmap = bitmap bitmapX bitmapY color:white local currentPos = lastPos = [0,0] local theChannel = 1 local theObj = undefined local bary = [0,0,0]
この変数は、PainterInterface が面内のヒット ポイントの重心座標を返すために使用します。
この変数は、PainterInterface がヒットした面のインデックスを返すときに使用します。
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..." menuItem paint3d_menu "Toggle 3D Painting..."
新しい[Toggle 3D Painting]のメニュー項目です。メインのユーザ インタフェースにも、よく目立つボタンを追加します。
) on commit_menu picked do copy theCanvasBitmap theBackgroundBitmap on uv_menu picked do MicroPaint_CanvasRollout.unwrapTexture() on paint3d_menu picked do MicroPaint_CanvasRollout.startPainting3D()
新しいメニュー項目[Toggle 3D Painting]のハンドラです。PainterInterface のオンとオフを切り替える関数を呼び出します。
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 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 colorpicker paperColor height:16 modal:false color:white checkbutton autoSave "AutoSave" width:70 offset:[0,-3] highlightcolor:(color 255 200 200) checkbutton airBrush "AirBrush" width:70 offset:[0,-3] highlightcolor:(color 200 255 200) spinner AirBrushSpeed "Speed" range:[0.1,50,10] fieldwidth:30 spinner BrushSize "Size" range:[1,50,1] type:#integer fieldwidth:40 listbox BrushShape items:#("Circle", "Box", "Circle Smooth") pos:[bitmapX+5,0] width:90 pickbutton pickMesh "Pick Mesh" width:90 height:30 highlightcolor:(color 200 200 255) pos:[bitmapX+5,140] filter:mesh_filter autodisplay:true on pickMesh picked obj do ( if obj != undefined do ( theObj = Obj try ( copy theObj.material.diffusemap.bitmap theCanvasBitmap copy theObj.material.diffusemap.bitmap theBackgroundBitmap theCanvas.bitmap = theCanvasBitmap )catch() ) ) checkbutton paint3D "3D PAINT" width:90 height:50 highlightcolor:(color 200 200 255) pos:[bitmapX+5,180]
3D 空間でのペイントをコントロールする新しいチェックボタンです。有効にすると、青色に変わります。
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: = ( 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 ) on pickMesh picked obj do if obj != undefined do theObj = Obj fn unwrapTexture = ( if theObj != undefined then ( theMesh = snapshotAsMesh theObj if meshop.getMapSupport theMesh theChannel do ( faceCount = meshop.getNumMapFaces theMesh theChannel for f = 1 to faceCount do ( theFace = meshop.getMapFace theMesh theChannel f vert1= meshop.getMapVert theMesh theChannel theFace.x vert2= meshop.getMapVert theMesh theChannel theFace.y vert3= meshop.getMapVert theMesh theChannel theFace.z drawStroke [vert1.x * bitmapx_1, bitmapy_1 - vert1.y * bitmapy_1] [vert2.x * bitmapx_1, bitmapy_1 - vert2.y * bitmapy_1] drawIt:false 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 ) ) theCanvas.bitmap = theCanvasBitmap save theCanvasBitmap if theObj.material == undefined do theObj.material = Standard() if theObj.material.diffusemap == undefined do theObj.material.diffusemap = bitmapTexture filename:temp_bitmap_filename showTextureMap theObj.material true autoSave.checked = true ) ) fn StartStroke = ( thePainterInterface.undoStart() )
この関数はストロークの開始時点で実行されます。詳細は、「PainterInterface の説明」を参照してください。
ペイントするオブジェクトの TriMesh のコピーを取得します。
次にこの関数を呼び出し、ブラシが最後にヒットした面を取得します (最後の引数は 0 という面インデックスで、「最後のヒットを取得する」ことを意味します)。スクリプトの初めの方で定義した変数 bary および faceIndex は、「参照によって」渡されます。 つまり、関数の結果はこれらに書き出されます。呼び出し後、bary には面内のヒット ポイントの重心座標が入り、faceIndex には最後にヒットした面のインデックスが入ります。 theObj は、ペイント対象としてシーン内で選択したオブジェクトを入れる変数です。
このデータを使用し、ヒットしたメッシュの面と同じインデックスを持つチャネル 1 のマップ面を取得します。
vert1= meshop.getMapVert theMesh theChannel theFace.x vert2= meshop.getMapVert theMesh theChannel theFace.y vert3= meshop.getMapVert theMesh theChannel theFace.z
thePoint= bary.x*vert1 + bary.y*vert2 + bary.z*vert3 drawStroke [thePoint.x * bitmapx_1, bitmapy_1 -thePoint.y * bitmapy_1] [thePoint.x * bitmapx_1, bitmapy_1 -thePoint.y * bitmapy_1]
面内のヒット ポイントの重心座標と、その面に対応する テクスチャ頂点の座標を使用して、ヒット ポイントの UV 座標を計算します。
オブジェクトのマテリアルの拡散反射光マップ チャネルのビットマップに、ペイントのビットマップ値を強制設定します。
この関数は、ストロークをキャンセルした場合に呼び出されます。
この関数は、ペイントのセッションが終了したときに呼び出されます。ペイントが別の理由で終了する場合に、UI ボタンをオフにします。
すでにペイント中である場合、またはオブジェクトがまだ選択されていない場合は、
3D ペイントのセッションを終了し、UI のボタンのチェックマークを外します。
3D ペイントが有効になっていないときにメッシュ シーン オブジェクトが選択されている場合は、
チェックボタンのチェックマークが必ず付いた状態になっているようにします (メイン メニューからチェックマークを付けるのと同じです)。
選択されている 1 つのオブジェクトだけで PainterInterface を初期化します。
メッシュがヒットされない場合の動作を 2 に設定します。この動作ではメッシュは必要ではありません。この場合にはヒットされる面はなく、ペイントが行われません。
thePainterInterface.ScriptFunctions startStroke paintStroke endStroke cancelStroke SystemEndPaintSession
事前に定義済みのスクリプト関数をすべて PainterInterface に登録し、
ユーザがチェックボタンを押した場合に上の関数を呼び出します。
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 autoSave changed state do if state 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 )