-- Poly Paint, a tool to paint polygos with a flat color from texture, setting all vertices of a UV face to a single point in texture, giving always a flat color -- By Denys Almaral - 2022 -- Updates: -- replace painting using another scene file. -- replace color tool. -- now work without disabling Skin modifier! or any modifier that doesn't change Face count. -- Migrated from Unwrap UVW functions to direct polyOp functions. Faster smooth calls. -- Paint-edit Multiple objects at the same time. -- Remappping textures: Swap old with new, remapping UV, keeping same colors look. -- UI review -- 2021: Auto-isert Unwrap UVW modifier. APPLY button to collapse to Unwrap UVW position in stack. -- 2019: ability to copy paste UV colors coords from Win clipboard -- implmented Undo levels -- added Flood Fill algorithm -- Added Mirror functionality -- Full repaint the whole object with selected color -- 2018: v1.0 basic functionality --- TODO: -- undo all modifications done in current session. -- Smart Fills: detect curvature Ambient Occluson, light/shadows. -- What to do when multiple selected objects have different textures? Limitation or feature? -- Need more UI room?, experiment with all inside subRollouts. Test inter-Rollout communications. macroScript PolyPaint buttonText:"Poly Paint" category:"pX Tools" tooltip:"pX Poly Paint" ( global g_PolyPaint global g_PolyPaint_rf if g_PolyPaint != undefined then ( if g_PolyPaint.open then ( destroyDialog g_PolyPaint ) ) -------------------------------------------------------------------- roll_polyPaint ------------------------------------ rollout roll_polyPaint "Painter" ( local bmpSize = 320 imgTag bmpColorPal bitmap:undefined pos:[0,0] style:#bmp_stretch transparent:(color 68 68 68) width:bmpSize height:bmpSize --label lblInfo "Select object and press Start Painting Tool" pos:[10,10] checkButton paint3D ">>> START Paint Tool <<<" width:(bmpSize - 5) height:25 group "Paint" ( checkbox chk_AutoStart "Auto-Start ^" across:2 checked:true checkbox chkMirror "Mirror Paint" across:2 checked:false button btnFullRepaint "Full Repaint" across:2 width:90 checkbox chkSelectedOnly "Repaint selected faces only" across:2 checkButton btnFill "Fill Tool" across:2 width:90 spinner spnSpread "Spread" range:[0,10,0] type:#integer across:2 checkButton btnReplace "Replace Tool" across:2 button btnUndo ">> UNDO (use this!) <<" across:2 timer timerFill "" interval:1000 active:false ) group "Current Color" ( imgTag imgCurrColor width:50 height:35 across:3 spinner spnU "U" range:[0,1,0] type:#float across:3 spinner spnV "V" range:[0,1,0] type:#float across:3 editText editRGBColor "RGB" text:"[0,0,0]" across:4 readonly:true button btnUV_copy "Copy" across:4 button btnUV_paste "Paste" across:4 button btnUV_set "Set" across:4 ) progressBar pbar "" value:0 group "Texture remapping & swapping" ( button btnSwapMap "Remap with -->" across:3 toolTip:"Remap UV based on texture target, keeping same colors as close as possible" mapbutton mapBtnSwapMap "" across:3 tooltip:"Select bitmap texture to be replaced with" checkbox chkNoReplace "Without replace" across:3 checked:false tooltip:"Do not replace with new bitmap when remapping" button btnReplaceFromFile "Replace from other file..." tooltip:"Get the paint from same object in different file and replace" ) local paint_enable = #( btnFullRepaint, btnFill, btnReplace, btnUndo, btnUV_set, btnSwapMap) local lastTriFace = -1 local Ctrl_Alt_undo = false local lastPolyIndex = -1 local theObj = undefined local lastObj = undefined local theObjects = #() local mod_states = #() local masterObj = undefined local mapChannel = 1 local curr_UV = [0.5,0.5,0] local currStrokeNum = 0 local thePI = thePainterInterface local rmIntersect = RayMeshGridIntersect() local faceMapUndo = #() local undoLevels = #() local undoLevelsMax = 5 local fill_tolerance = 1 local triToPolyTable = #() local tempBitmap = bitmap bmpSize bmpSize color:(color 40 40 40) local colorBitmap = bitmap 50 50 local ini_file = (getDir #userMacros) + "\\pxpolypaint.ini" local ini_section = "config" function set_ini key val = ( setINISetting ini_file ini_section key (val as string) ) function get_ini key = ( getINISetting ini_file ini_section key ) function eval s = ( try execute s catch "" ) --============================ FUNCTIONS ===============================------------------------ function enable_tools bool = ( for ui in paint_enable do ui.enabled = bool ) function updateColorBitmap diffMap = ( copy diffMap.bitmap tempBitmap bmpColorPal.bitmap = tempBitmap g_currBitmap = tempBitmap ) function UVtoColor UV gamma:true = ( local w = tempBitmap.width local h = tempBitmap.height (getPixels tempBitmap [ UV.x * w , (1-UV.y) * h ] 1 linear:gamma )[1] ) function UVgetPixel UV diffMap gamma:false = ( local w = diffMap.bitmap.width local h = diffMap.bitmap.height if UV.x>=1 or UV.y>=1 or UV.x<=0 or UV.y<=0 then ( print "ERROR: pixel coord out of bounds" UV.x = 0.5 UV.y = 0.5 ) (getPixels diffMap.bitmap [ UV.x * w , (1-UV.y) * h ] 1 linear:gamma )[1] ) function findMatDiffuseMap mat = ( local diffMap = undefined if mat != undefined then ( diffMap = case classof(mat) of ( blend: mat.map1 Arch___Design__mi: mat.diffuse_Color_Map standard: mat.diffuseMap VRayMtl: mat.texmap_diffuse PhysicalMaterial: mat.base_color_map PBRMetalRough: mat.base_color_map PBRSpecGloss: mat.base_color_map ai_standard_surface: mat.base_color_shader default: undefined ) ) (diffMap) ) function replaceDiffuseMap mat diffmap = ( case classof(mat) of ( blend: mat.map1 = diffmap Arch___Design__mi: mat.diffuse_Color_Map = diffmap standard: mat.diffuseMap = diffmap VRayMtl: mat.texmap_diffuse = diffmap PhysicalMaterial: mat.base_color_map = diffmap PBRMetalRough: mat.base_color_map = diffmap PBRSpecGloss: mat.base_color_map = diffmap ai_standard_surface: mat.base_color_shader = diffmap ) ) function findDiffuseMap obj = ( local mat = obj.material findMatDiffuseMap mat ) -- updates current Color box on interface. -- draw pixels over bitmap to show current color function updateCurrColor = ( local diffMap = findMatDiffuseMap masterObj.material copy diffMap.bitmap tempBitmap local p = UVtoColor curr_UV if p != undefined then ( free colorBitmap colorBitmap = bitmap 50 50 color:p imgCurrColor.bitmap = colorBitmap -- draw pixel marker xx = curr_UV.x * tempBitmap.width yy = (1-curr_UV.y) * tempBitmap.height for i = 2 to 3 do ( setPixels tempBitmap [xx+1*i, yy] #(color 255 255 255) setPixels tempBitmap [xx-1*i, yy] #(color 255 255 255) setPixels tempBitmap [xx, yy+1*i] #(color 0 0 0) setPixels tempBitmap [xx, yy-1*i] #(color 0 0 0) ) bmpColorPal.bitmap = tempBitmap ) --set UV coords on spinners spnU.value = curr_UV.x spnV.value = curr_UV.y editRGBColor.text = ((UVGetPixel curr_UV diffMap gamma:false) as point3) as string ) function clearUndo = ( -- store it, actually, in undo levels --btnUndo.enabled = false append undoLevels faceMapUndo if undoLevels.count > undoLevelsMax then deleteItem undoLevels 1 faceMapUndo = #() --print ("undo levels" + (undoLevels.count as string) ) ) --DEPRECATED fn __getPolyFaceByTri__ EPolyObj tri = ( -- convert a mesh Face Index to a editable_poly poly index -- need clarification: -- will go each face guess its number of tirangles and make the sum. -- triangles are in order as polygons. there is a correspondance -- MAGIC ALERT! local triIndex = 1 local polyIndex = 0 for k = 1 to polyop.getNumFaces EPolyObj while polyIndex == 0 do ( --TODO: avoid this iterations, make a cached table --current face tirangles is equal to the number of vertices - 2. local numTriangles = (polyop.getFaceDeg EPolyObj k) - 2 if numTriangles == undefined then numTriangles = 0 triIndex = triIndex + numTriangles if (tri < triIndex) then polyIndex = k -- --debug-- print ((index as string) + " | " + (k as string) + " | " + polyIndex as string) ) --return: (polyIndex) ) function getPolyFaceByTri EPolyObj tri = triToPolyTable[tri] function BuildTriToPolyTable EPolyObj = ( -- local list = #() local triIndex = 1 local numTriangles = 0 for k = 1 to polyop.getNumFaces EPolyObj do ( -- Triangles are ordered in correspondence to polygons. -- We just need to count.... local faceDeg = polyop.getFaceDeg EPolyObj K if faceDeg == undefined then ( numTriangles = 0 ) else (numTriangles = (faceDeg) - 2) for i=1 to numTriangles do ( list[triIndex] = k triIndex = triIndex + 1 ) ) --return (list) -- triToPolyTable[triangleN] = polygonN. ) function unwrap_getMapFace obj poly = ( local result = #() result.count = obj.Unwrap_UVW.numberPointsInFace poly for i=1 to result.count do ( result[i] = obj.Unwrap_UVW.getVertexIndexFromFace poly i ) --return (result) ) function setAllUVto poly UV UndoOn:true = ( if theObj != lastObj then ( --changed target node modPanel.setCurrentObject theObj.modifiers[#Unwrap_UVW] ) local mapFace = unwrap_getMapFace theObj poly if UndoOn do ( -- UNDO DATA local faceUV = #() faceUV[1] = poly --saving, only the first vertex faceUV[2] = theObj.Unwrap_UVW.getVertexPosition 0 mapFace[1] faceUV[3] = theObj append faceMapUndo faceUV ) --modifiying for i=1 to mapFace.Count do ( --with unwrap_modifier theObj.Unwrap_UVW.setFaceVertex [ UV.x, UV.y,0] poly i false --directly to Editpoly --polyop.setMapVert theObj 1 mapFace[i] [UV.x, UV.y, 0] ) lastObj = theObj ) function setFaceUV faceIdx UV undoOn:true Obj:undefined = ( if Obj == undefined then ( Obj = theObj ) if UndoOn do ( -- UNDO DATA local faceUV = #() faceUV[1] = faceIdx --saving, only the first vertex faceUV[2] = polyop.getMapVert Obj mapChannel faceIdx faceUV[3] = Obj append faceMapUndo faceUV ) --modifiying, --This works because the mapping was prepared with "prepare_for_polyops" function -- Map Face Indexes and Map Vert Indixes are paired 1 to 1. NumMapFaces = NumMapVerts. -- Each face vert point to a single map vert. polyop.setMapVert Obj mapChannel faceIdx [UV.x, UV.y, 0] ) function putPlyxel faceIdx UV = ( if spnSpread.value == 0 then setFaceUV faceIdx UV else ( local faces = #{faceIdx} local verts = #{} for j = 1 to spnSpread.value do ( verts = verts + polyOp.getVertsUsingFace theObj faces faces = faces + polyOp.getFacesUsingVert theObj verts ) for i in faces do ( setFaceUV i UV ) ) ) function unwrap_getPlyxel poly = ( --local face = polyop.getMapFace theObj 1 poly local face = unwrap_getMapFace theObj poly --local vert = polyop.getMapVert theObj 1 face[1] local vert = theObj.Unwrap_UVW.getVertexPosition 0 Face[1] --return (vert) ) function getPlyxel faceIdx = ( --return (polyop.getMapVert theObj mapChannel faceIdx) ) function mirrorPaint = ( local localHit = [0,0,0], localNormal=[0,0,0], worldHit=[0,0,0], worldNormal=[0,0,0] thePI.getMirrorHitPointData &localHit &localNormal &worldHit &worldNormal 0 local p1 = worldHit - worldNormal local p2 = worldHit + worldNormal local result = rmIntersect.intersectSegment p2 p1 false if result>0 then ( local indexedHit = 1 if result>1 then indexedHit = rmIntersect.getClosestHit() local Poly = getPolyFaceByTri theObj (rmIntersect.getHitFace indexedHit) putPlyxel Poly curr_UV ) ) function SameColor c1 c2 = ( local result = (c1 != undefined) and (c2 != undefined) if result then ( local d = distance (c1 as point3) (c2 as point3) result = d < fill_tolerance ) result ) function fillArea iniPoly UV = ( local faces = #{iniPoly} local bkColor = UVtoColor (getPlyxel iniPoly) --storing the background color local fillColor = UVtoColor UV putPlyxel iniPoly UV -- setting the color of the first Plyxel update theObj local paintLimit = 0 -- To avoid an unexpected ininite loop... do ( local edges = polyOp.getEdgesUsingFace theObj faces local newFaces = polyOp.getFacesUsingEdge theObj edges local removeFaces = #{} for i in newFaces do ( local c = UVtoColor (getPlyxel i) --Check the current color of each face if (not ( sameColor c bkColor)) or (sameColor c fillColor) then -- remove if not == bkColor, or == to fillColor ( removeFaces = removeFaces + #{i} ) else (putPlyxel i UV) --else paint ) newFaces = newFaces - removeFaces --print ( "filling faces " + ((newFaces as array).count as string) ) update theObj redrawViews() faces = newFaces paintLimit = paintLimit + 1 ) while ( ((faces as array).count > 0 ) and (paintLimit < 1000 ) ) ) function replaceColor iniPoly UV = ( local bkUV = getPlyxel iniPoly local bkColor = UVtoColor bkUV --storing the picked color for i=1 to polyOp.getNumFaces theObj do ( local c = UVtoColor (getPlyxel i) if (sameColor c bkColor) then ( putPlyxel i UV ) ) update theObj redrawViews() ) function paintLoop_forward face1 face2 UV alreadyPainted:#{} = ( --TODO: paint direction to the other side too local paintLimit = 0 local paintedFaces = alreadyPainted setFaceUV face1 UV setFaceUV face2 UV paintedFaces[face1] = true paintedFaces[face2] = true do ( local verts1 = polyop.getFaceVerts theObj face1 local verts2 = polyop.getFaceVerts theObj face2 local sharedVerts = (verts1 as bitArray) * (verts2 as bitArray) local face3 = undefined if (sharedVerts as array).count == 2 then ( --ok: local OppositeVerts = (verts2 as bitArray) - sharedVerts if (OppositeVerts as array).count == 2 then ( --ok ok: nearFaces = polyOp.getFacesUsingVert theObj (OppositeVerts as array) local found = -1 for f in nearFaces do ( --not face2 if (f != face2) and (not paintedFaces[f]) then ( --has the two verts? local verts = polyop.getFaceVerts theObj f if ((OppositeVerts * (verts as bitArray)) as array ).count == 2 then ( --OKey! face3 = f ) ) ) ) ) if face3 != undefined then ( setFaceUV face3 UV paintedFaces[face3]=true --looping step face1 = face2 face2 = face3 ) paintLimit += 1 --paranoical stop ) while (face3 != undefined) and (paintLimit < 1000) --return (paintedFaces) ) function paintLoop face1 face2 UV = ( local painted = paintLoop_forward face1 face2 UV paintLoop_forward face2 face1 UV alreadyPainted:painted ) function do_undo = ( local obj = undefined local prevObj = undefined print "doing undo thing..." print faceMapUndo.count for i=faceMapUndo.count to 1 by -1 do ( local faceUV = faceMapUndo[i] obj = faceUV[3] local faceIdx = faceUV[1] local UVpoint = faceUV[2] --local faceMap = polyop.getMapFace theObj 1 faceUV[1] --get the vertex indices; may have changed /* if obj != prevObj then modPanel.setCurrentObject obj.modifiers[#Unwrap_UVW] local faceMap = unwrap_getMapFace obj faceIdx for j=1 to faceMap.count do ( obj.Unwrap_UVW.setFaceVertex UVpoint faceIdx j false ) */ polyop.setMapVert obj mapChannel faceIdx UVpoint prevObj = obj ) for obj in theObjects do update obj redrawViews() --more undo levels? if undoLevels.count > 0 then ( faceMapUndo = undoLevels[ undoLevels.count ] deleteItem undoLevels undoLevels.count ) else ( faceMapUndo = #() btnUndo.enabled = false ) ) --EVENT from painter interface function startStroke = ( --thePI.undoStart() clearUndo() currStrokeNum = currStrokeNum + 1 --viewport.SetShowEdgeFaces true ) -- The main thing happens here ------------ PAINT STROKE -------------- ****************************** function paintStroke = ( --paintStroke: EVENT from painter interface local bary = [0,0,0] local faceIndex = 1 local hitObj = thePI.getHitNode 0 if hitObj != undefined then ( theObj = hitObj thePI.getHitFaceData &bary &faceIndex theObj 0 if (faceIndex != lastTriFace) and (bary != [0,0,0]) then --do not repeat stuff if we are still in the same face -- bary != [0,0,0] dirty trick to detect ourside of node strokes ( local PolyIndex = (getPolyfaceByTri theObj faceIndex) --print bary local shift=false , ctrl=false, alt=false, pressure=0 thePI.getHitPressureData &shift &ctrl &alt &pressure 0 if alt and ctrl then ( Ctrl_Alt_Undo = true ) else if alt then -- PICKING COLOR ( curr_UV = getPlyxel polyIndex updateCurrColor() ) else if (btnFill.checked or ctrl) then ( fillArea polyIndex curr_UV ) else if btnReplace.checked then ( replaceColor polyIndex curr_UV ) else if shift then ( --detect face change to paint loop if (polyIndex != lastPolyIndex) and (lastPolyIndex != -1 ) then ( paintLoop lastPolyIndex polyIndex curr_UV update theObj ) ) else -- DRAWING COLOR ( putPlyxel polyIndex curr_UV if thePI.mirrorEnable == true then mirrorPaint() update theObj ) thePI.clearStroke() lastPolyIndex = PolyIndex ) lastTriFace = faceIndex ) ) function endStroke = ( lastTriFace = -1 lastPolyIndex = -1 if faceMapUndo.count>0 then btnUndo.enabled = true ---do undo if Ctrl+Alt+click: if Ctrl_Alt_Undo then ( print "Ctrl Alt Click = UNDO" if undoLevels.count > 0 then ( faceMapUndo = undoLevels[ undoLevels.count ] deleteItem undoLevels undoLevels.count ) do_undo() Ctrl_Alt_Undo = false ) --viewport.SetShowEdgeFaces false ) function cancelStroke = ( --thePI.undoCancel() if faceMapUndo.count>0 then btnUndo.enabled = true print "cancel stroke?? please!" lastTriFace = -1 lastPolyIndex = -1 do_undo() ) function systemEndPaintSession = ( paint3d.checked = false lastTriFace = -1 ) local mpanel_showEndResult = false local mpanel_subObjectLevel = 0 local mpanel_currObject = undefined function save_modpanel_state = ( max modify mode mpanel_showEndResult = showEndResult mpanel_subObjectLevel = subObjectLevel mpanel_currObject = modPanel.getCurrentObject() if mpanel_currObject == theObj.baseObject then ( mpanel_currObject = "base" ) ) function restore_modpanel_state = ( --max modify mode if mpanel_currObject == "base" then ( mpanel_currObject = masterObj.baseObject ) modPanel.setCurrentObject mpanel_currObject subObjectLevel = mpanel_subObjectLevel showEndResult = mpanel_showEndResult ) --DEPRECATED function prepareObj_for_unwrap obj = ( if (obj.modifiers[#Unwrap_UVW] == undefined) then ( if chkAutoUV.checked then ( addModifier obj (Unwrap_UVW()) before:(obj.modifiers.count) ) ) if (obj.modifiers[#Unwrap_UVW] != undefined) then ( modPanel.setCurrentObject obj.modifiers[#Unwrap_UVW] obj.modifiers[#unwrap_uvw].unwrap5.setShowMapSeams off ) else ( messageBox "Unwrap_UVW not found in stack" ) ) --better than Unwrap_UVW function prepare_for_polyops obj = ( local meshFaces = polyop.getNumFaces obj if not (polyop.getMapSupport obj mapChannel) then ( polyop.setMapSupport obj mapChannel true ) local mapFaces = polyop.getNumMapFaces obj mapChannel if mapFaces != meshFaces then ( --in case faces difer polyop.setNumMapFaces obj mapChannel meshFaces keep:true mapFaces = meshFaces ) local mapVerts = polyOp.getNumMapVerts obj mapChannel -- rebuild map verts for our convenience, one unique vert for each face, each face vert point to that vert -- keep one old vert local vertsCopy = #() vertsCopy.count = mapFaces local mapFaceSizes = #() for i = 1 to mapFaces do ( local mapFace = polyop.getMapFace obj mapChannel i mapFaceSizes[i] = mapFace.count if mapFace[1] > mapVerts then (vertsCopy[i] = polyop.getMapVert obj mapChannel 1) else (vertsCopy[i] = polyop.getMapVert obj mapChannel mapFace[1]) ) polyop.setNumMapVerts obj mapChannel mapFaces keep:true for i = 1 to mapFaces do ( polyop.setMapVert obj mapChannel i vertsCopy[i] local newFace = #() newFace.count = mapFaceSizes[i] for j = 1 to newFace.count do ( newFace[j] = i ) polyop.setMapFace obj mapChannel i newFace ) --cache triangle to polygon conversion triToPolyTable = BuildTriToPolyTable obj update obj ) function prepareObj obj = ( --modPanel.setCurrentObject obj.baseObject prepare_for_polyops obj ) function startPainting3D = ( if thePI.InPaintMode() or theObj == undefined then ( thePI.endPaintSession() paint3D.checked = false ) else ( updateCurrColor() --lblInfo.visible = false paint3D.checked = true thePI.pointGatherEnable = false thePI.initializeNodes 0 theObjects thePI.offMeshHitType =2 thePI.minSize = 0.1 thePI.maxSize = 2 thePI.drawring = true thePI.drawTrace = false thePI.drawNormal = true thePI.normalScale = 5 thePI.pressureEnable = true thePI.mirrorEnable = chkMirror.checked if chkMirror.checked then ( rmIntersect.free rmIntersect.nodeList = #() rmIntersect.initialize 10 rmIntersect.addNode( theObj ) rmIntersect.buildGrid() ) thePI.mirrorAxis = 1 thePI.scriptFunctions startStroke paintStroke endStroke cancelStroke SystemEndPaintSession thePI.startPaintSession() ) ) function endPainting3D = ( thePI.endPaintSession() if paint3D.checked then paint3D.checked = false ) function findClosestColor thisColor fromBitmap = ( thisColor = thisColor as point3 found = [0,0] otherColor = (color 0 0 0) min_dist = distance [0,0,0] [255,255,255] for y = 0 to (fromBitmap.height-1) do ( local pixels = getPixels fromBitmap [0, y] fromBitmap.width linear:false for x = 0 to (pixels.count-1) do ( local dist = distance (pixels[x+1] as point3) thisColor if dist 20 then maxCount = 20 if maxCount > 2 then ( while sameColor do ( count = count + 1 --print count c2 = (getPixels fromBitmap (pos+[count, count]) 1 linear:false)[1] sameColor = (c == c2) and (count < maxCount) ) if count>2 then ( pos.x = floor (pos.x + count / 2.0) pos.y = floor (pos.y + count / 2.0) ) ) --return (pos) ) --This one use what getPixelTable returns -- DEPRECATED function findClosestColorTable thisColor fromPixelTable = ( thisColor = thisColor as point3 local found = [0,0] local otherColor = (color 0 0 0) local min_dist = distance [0,0,0] [255,255,255] local dist = 0.0 for y = 0 to (fromPixelTable.count-1) do ( local pixels = fromPixelTable[y+1] for x = 0 to (pixels.count-1) do ( dist = distance (pixels[x+1] as point3) thisColor if dist 0 then ( paint3D.checked = true theObj = masterObj startPainting3D() ) else paint3D.checked = false ) else ( endPainting3D() if false then --chkAutoUV_end.checked then ( for obj in theObjects do ( local mods = Obj.modifiers local m = mods[#Unwrap_UVW] local idx = findItem mods m print "collapsing" maxOps.CollapseNodeTo Obj idx off ) ) --restoring stacks and modify panel for i=1 to theObjects.count do ( local states = mod_states[i] if states != undefined then ( for j = 1 to states.count do ( theObjects[i].modifiers[j].enabled = states[j] ) ) ) if theObjects.count==1 then restore_modpanel_state() ) enable_tools (paint3D.checked == true) ) on bmpColorPal lbuttondown pos flags do ( if (pos.y <= bmpSize) then ( -- get the position of mouse clicks over the Texture Image and convert it to UV coordinates. curr_UV.x = pos.x / bmpSize curr_UV.y = 1 - ( pos.y / bmpSize ) updateCurrColor() ) ) on roll_polyPaint close do ( print "Good bye!" paint3D.checked = false paint3D.changed false --ini settings local p = g_polyPaint_rf.pos print p set_ini "dialogPos" (p as string) set_ini "curr_uv" (curr_UV as string) ) on roll_polyPaint open do ( -- loading ini settings local s = get_ini "dialogPos" local p = eval s print s if (classof p ) == point2 then ( g_polyPaint_rf.pos = p ) s = get_ini "curr_uv" p = eval s if (classof p) == point3 then ( curr_UV = p --updateCurrColor() ) -- enable_tools false -- auto start painting? if chk_AutoStart.checked then ( print "auto starting..." paint3D.checked = true paint3D.changed true ) ) on btnFullRepaint pressed do ( if theObj != undefined then ( if chkSelectedOnly.checked then ( -- TODO: make this work without unwrap and get selected faces from polyop --local sp = theObj.unwrap_UVW.getSelectedPolygons() local sp = polyop.getFaceSelection theObj for i in sp do ( putPlyxel i curr_UV ) ) else ( for i=1 to (polyOp.getNumFaces theObj) do ( putPlyxel i curr_UV ) ) update theObj redrawViews() ) ) on chkMirror changed state do ( thePI.mirrorEnable = chkMirror.checked if chkMirror.checked then ( rmIntersect.free rmIntersect.nodeList = #() rmIntersect.initialize 10 rmIntersect.addNode( theObj ) rmIntersect.buildGrid() ) ) on btnUndo pressed do ( do_undo() ) on btnUV_copy pressed do ( local ss = (stringStream "") print spnU.value to:ss print spnV.value to:ss setclipboardText (ss as string) ) on btnUV_paste pressed do ( local s = getclipboardText() if s!=undefined then ( local ss = (stringStream s) seek ss 0 spnU.value = (readValue ss) spnV.value = (readValue ss) ) ) on btnUV_set pressed do ( curr_UV.x = spnU.value curr_UV.y = spnV.value updateCurrColor() ) on btnFill changed state do ( if state then ( btnReplace.checked = false thePainterInterface.drawNormal = true thePainterInterface.drawRing = false ) else ( thePainterInterface.drawRing = true thePainterInterface.drawNormal = true ) ) on btnReplace changed state do ( if state then ( btnFill.checked = false ) ) on timerFill tick do ( setSysCur #select ) on mapBtnSwapMap picked textmap do ( if (classof mapBtnSwapMap.map) ==Bitmaptexture then ( mapBtnSwapMap.text = textmap.name ) else ( messageBox "Map needs to be Bitmaptexture class" mapBtnSwapMap.map = undefined ) ) on btnSwapMap pressed do ( if theObj != undefined then ( local map = mapBtnSwapmap.map if map != undefined then ( if (classof map) ==Bitmaptexture then ( remapTo map.bitmap if not chkNoReplace.checked then replaceDiffuseMap theObj.material map redrawViews() updateColorBitmap map ) else ( messageBox "Map needs to be Bitmaptexture class" ) ) ) ) on btnReplaceFromFile pressed do ( if $selection.count != 1 then ( messageBox "ERROR: An object need to be selected." ) else ( local objName = $.name print objName if queryBox "Current scene NEEDS to be SAVED. Proceed?" then ( otherFile = getOpenFileName caption:("Open a scene with "+objName+" object...") types:"Max(*.max)|*.max" if otherFile != undefined then ( print otherFile --saving current scene currMaxFileName = maxFilePath + maxFileName if Paint3D.checked == true then Paint3D.changed false saveMaxFile currMaxFileName quiet:true loadMaxFile otherFile quiet:true local obj = getNodeByName objName if obj == undefined then ( fn poly_filter obj = (classof obj.baseObject)==Editable_Poly obj = SelectByName title:(objName+" not found, select manually") single:true ) if obj == undefined then ( messageBox ("Object name no found: "+objName) ) else ( select obj Paint3D.changed true -- get the paint data here local uvData = undefined print "numfaces from" uvData = #() uvData.count = polyOp.getNumMapFaces obj mapChannel print uvData.count for i=1 to uvData.count do ( uvData[i] = polyOp.getMapVert obj mapChannel i ) Paint3D.changed false --back to original file loadMaxFile currMaxFileName quiet:true obj = getNodeByName objName select obj Paint3D.changed true if $selection.count == 1 then ( -- set copied the paint data here if (theObj != undefined) and (uvData != undefined) then ( print "numfaces to" local maxData = uvData.count local currFaces = polyOp.getNumMapFaces obj mapChannel if currFaces != uvData.count then print "WARNING: Dest Faces Count != Source Faces Count" if currFaces < maxData then maxData = currFaces print currFaces for i=1 to maxData do ( polyOp.setMapVert obj mapChannel i uvData[i] ) update theObj redrawViews() ) ) ) ) ) ) ) ) rollout roll_help "Help" ( label l1 "Alt + Click = Pick color from mesh" align:#left label l2 "Shift + Click drag across edge = Paint loop" align:#left label l3 "Ctrl + Click = Fill Tool" align:#left label l4 "Ctrl + Alt + Click = Undo last" align:#left hyperLink hl_web "pX Poly Paint by Denys Almaral" align:#right address:@"https://denysalmaral.com/2018/09/free-polygon-painting-script-and-lowpoly-owl.html" align:#center ) g_polypaint_rf = newRolloutFloater "pX Poly Paint" (roll_PolyPaint.bmpSize + 10) (roll_PolyPaint.bmpSize + 460 ) addRollout roll_PolyPaint g_polypaint_rf addRollout roll_help g_polyPaint_rf rolledup:true g_polyPaint = roll_PolyPaint --roll_help.height = 100 )