--------------------------------------------------------------------------------------------------------------- --VERTEX COLOUR BLENDER STRUCTURE --functionality for the vertex colour blending part of the edit section --activated through rollout RsRollVertexColourBlenderRoll "Vertex Colour Blender:" UI --------------------------------------------------------------------------------------------------------------- RSTA_LoadCommonFunction #("FN_Geometry.ms") struct CPVDataStruct ( obj, index, smoothingGroups, matId ) struct RsVertexColourBlenderStruct ( ObjectDataList, --list of edge verts for each selected object VertMatchList, --list of coincident edge verts sorted by position of coincidence CoincidentVertList, --list of ObjectData structures representing only coincident vertices tolerance, processVC, processVI, processAlpha, processOther, processOnlyBorderVerts, customChannel, respectSmoothingGroups, respectMaterialIds, progressBarValue = 0.0, debug = false, -- Main function called from the UI fn ProcessEdges = ( local originalSelection = selection as array if (this.processVC) then ( select originalSelection this.ProcessChannel 0 ) if (this.processVI) then ( select originalSelection this.ProcessChannel -1 ) if (this.processAlpha) then ( select originalSelection this.ProcessChannel -2 ) if (this.processOther) then ( select originalSelection this.ProcessChannel this.customChannel ) -- Update the object to reflect the map changes for obj in originalSelection do ( update obj geometry:true topology:false normals:false -- mapped ) select originalSelection ), -- Blend the vertex colours from the given channel fn ProcessChannel channelNum = ( this.PrepObjectsForChannel channelNum local initObjectDataListSuccess = this.InitObjectDataList() if initObjectDataListSuccess then ( this.InitVertMatchList() this.BlendColors() ) if channelNum != 0 then ( this.RevertObjects channelNum ) ), fn GetCPVs obj vertid = ( local vcVerts = #() local CPVsAndSGs = #() local facelist = (meshop.getFacesUsingVert obj #{vertid}) as array local facescount = facelist.count for i = 1 to facescount do ( local vertids = getFace obj facelist[i] local vcids = getVCFace obj facelist[i] local sgs = #{1} local matId = 0 if this.respectSmoothingGroups then sgs = RSGeom_GetFace_SmoothGroupList obj facelist[i] if this.respectMaterialIds then matId = MeshOp_GetFaceMatID obj facelist[i] for j = 1 to 3 do ( if (vertids[j] == vertid) then ( if (appendIfUnique vcVerts #(vcids[j] as integer, matId)) then ( local vertexData = CPVDataStruct index:(vcids[j] as integer) smoothingGroups:sgs matId:matId append CPVsAndSGs vertexData ) ) ) ) return CPVsAndSGs ), --produces a reduced version of the ObjectDataList containing only coincident vertices fn AppendCoincidentVertList obj vertID = ( local found = false for item in this.CoincidentVertList do ( if (item.obj == obj) then ( found = true appendIfUnique item.verts vertID ) ) if (not found) then ( append this.CoincidentVertList (DataPair obj:obj verts:#{vertID}) ) ), fn BlendColors = ( local Matches = this.VertMatchList.count local success = true for item in this.VertMatchList do ( local matchList = item[2] local blendList blendList = this.CreateBlendList matchList if (blendList.count > 0) then this.ProcessBlendList blendList this.progressBarValue += 50.0/Matches ::RsRollVertexColourBlenderRoll.pbEdgeBlender.value = this.progressBarValue ) ::RsRollVertexColourBlenderRoll.pbEdgeBlender.value = 0 return success ), --averages vertex colors for CPVDataStruct groups sent in blendList argument fn ProcessBlendList blendList = ( if this.debug then this.PrintBlendList blendList for item in blendList do ( local averageVC = [0,0,0] local numCPVItems = item.count for i = 1 to numCPVItems do ( local itemColor = getVertColor item[i].obj item[i].index averageVC += [itemColor.r, itemColor.g, itemColor.b] ) averageVC /= numCPVItems for data in item do ( setVertColor data.obj data.index (color averageVC[1] averageVC[2] averageVC[3]) ) ) ), --returns a list of all valid coincident cpv verts and their objects, that have matching smoothing groups fn CreateBlendList matchList = ( if this.debug then this.PrintVertMatchList() --for each smoothing group found, a new item is added to the blendList which contains groups of cpvIDs local blendList = #() local cpvDataList = #() local SGList = #() --part1: get a list of all the cpvs (cpvDataList) for match in matchList do ( local cpvIDs = this.GetCPVs match.obj match.vertID local cpvCount = cpvIDs.count for i = 1 to cpvCount do append cpvDataList (CPVDataStruct obj:match.obj index:cpvIDs[i].index smoothingGroups:cpvIDs[i].smoothingGroups matId:cpvIDs[i].matId) ) --part2: process these to create groups of matching smoothing groups (SGList) and material Id local SGList = #() local MatIdList = #() --work out how many blend groups we have for item in cpvDataList do ( append SGList item.smoothingGroups append MatIdList item.matId ) local continueProcess = true while continueProcess do ( continueProcess = this.ProcessSGList &SGList ) MatIdList = makeUniqueArray MatIdList this.RemoveEmptySGGroups &SGList --part3: compare the cpvDataList to the SGList to produce groups of cpv vertices to blend local numSGGroups = SGList.count local numMatGroups = MatIdList.count i = 1 for iMat = 1 to numMatGroups do ( for iSG = 1 to numSGGroups do ( blendList[i] = #() for item in cpvDataList do ( if (this.CompareBitArrays SGList[iSG] item.smoothingGroups) and (MatIdList[iMat] == item.matId) then append blendList[i] item ) i+=1 ) ) --remove entries from blendList where there are less than two cpv verts to blend this.CullSingleBlendGroups &blendList return blendList ), --remove entries from blendList where there are less than two cpv verts to blend fn CullSingleBlendGroups &blendList = ( local bufferList = #() local listReduced = false for item in blendList do ( if (item.count > 1) then append bufferList item else listReduced = true ) blendList = bufferList return listReduced ), --removes empty items from the list of smoothing groups fn RemoveEmptySGGroups &SGList = ( local buffer = #() local listReduced = false for item in SGList do ( if (item as array).count > 0 then append buffer item else listReduced = true ) SGList = buffer return listReduced ), -- takes the list of all smoothing groups and checks for duplicate items -- duplicate items are concatenated to their first occurence in the SGList item fn ProcessSGList &SGList = ( local numItems = SGList.count local removeList = #() --we check the items against each other working backwards through the list for i = 1 to numItems - 1 do ( local currentID = numItems + 1 - i for j = 1 to numItems - i do ( if (this.CompareBitArrays SGList[j] SGList[currentID]) then ( for item in (SGList[currentID] as array) do ( if (findItem SGList[j] item) == 0 then append SGList[j] item ) append removeList currentID ) ) ) if removeList.count > 0 then ( local bufferList = #() for i = 1 to numItems do ( if (findItem removeList i) == 0 then append bufferList SGList[i] ) SGList = bufferList return true ) else return false ), -- returns true if any items in the two bitarrays match fn CompareBitArrays ba1 ba2 = ( local found = false for item in (ba1 as array) do ( if (findItem ba2 item) > 0 then found = true ) return found ), --function creates a list of edge vertices for all selected geometry fn InitObjectDataList = ( local collapseNeeded = false local executeCollapse local objList = for item in selection where (superClassOf item == GeometryClass) collect item this.ObjectDataList = #() for obj in objList do if (obj.modifiers.count > 0) then collapseNeeded = true if collapseNeeded do executeCollapse = queryBox "About to collapse geometry. Do you wish to continue?" title:"Vertex Colour Edge Blender" if (collapseNeeded and not executeCollapse) then return false else ( convertToMesh objList for obj in objList do ( local edgeList if this.processOnlyBorderVerts then ( edgeList = meshop.getOpenEdges obj append this.ObjectDataList (DataPair obj:obj verts:(meshop.getVertsUsingEdge obj edgeList)) ) else ( append this.ObjectDataList (DataPair obj:obj verts:(obj.Vertices as BitArray)) ) ) return true ) ), --function checks if two objects are close to each other fn CheckProximity obj1 obj2 = ( local proximity = true if not (obj1.max.x >= obj2.min.x and obj1.min.x <= obj2.max.x) then proximity = false if not (obj1.max.y >= obj2.min.y and obj1.min.y <= obj2.max.y) then proximity = false if not (obj1.max.z >= obj2.min.z and obj1.min.z <= obj2.max.z) then proximity = false return proximity ), --function creates a list of coincident vertices: VertMatchList --VertMatchList is an array containing the coordinates of coincidental verts and a list of corresponding objects and the vertID's for each coordinate fn InitVertMatchList = ( this.VertMatchList = #() this.CoincidentVertList = #() local objectDataListCount = this.ObjectDataList.count --iterate through each pair of meshes, and for each pair, iterate through the respective edge verts to find coincident vertices if this.ObjectDataList.count > 1 then ( this.progressBarValue = 0 ::RsRollVertexColourBlenderRoll.pbEdgeBlender.value = this.progressBarValue for i = 1 to objectDataListCount - 1 do ( -- the next loop has a decreasing numerator, as we don't need to compare meshes twice for j = i + 1 to objectDataListCount do ( local iVerts = this.ObjectDataList[i].verts as array local jVerts = this.ObjectDataList[j].verts as array --check if the two meshes are next to one another if (CheckProximity this.ObjectDataList[i].obj this.ObjectDataList[j].obj) then ( for iv = 1 to iVerts.count do ( for jv = 1 to jVerts.count do ( local iVertPosition = this.ObjectDataList[i].obj.verts[iVerts[iv]].position local jVertPosition = this.ObjectDataList[j].obj.verts[jVerts[jv]].position if (distance iVertPosition jVertPosition < this.tolerance) then ( this.AppendVertMatchList iVertPosition this.ObjectDataList[i].obj iVerts[iv] this.ObjectDataList[j].obj jVerts[jv] ) ) ) ) ) this.progressBarValue += 50.0/(objectDataListCount - 1) ::RsRollVertexColourBlenderRoll.pbEdgeBlender.value = this.progressBarValue ) ) ), fn AppendVertMatchlist vertPosition obj1 vertID1 obj2 vertID2 = ( if (this.VertMatchList.count > 0) then ( local foundValue = false for i = 1 to this.VertMatchList.count do ( if (distance this.VertMatchList[i][1] vertPosition <= this.tolerance) then ( foundValue = true -- only need to add the second value, as the first datapair should already be in the list local foundInDataPairList = false --check that the new dataPair isn't already there... for j = 1 to this.VertMatchList[i][2].count do ( if (this.VertMatchList[i][2][j].obj == obj2 and this.VertMatchList[i][2][j].vertID == vertID2) then foundInDataPairList = true ) if not foundInDataPairList then ( append this.VertMatchList[i][2] (datapair obj:obj2 vertID:vertID2) this.AppendCoincidentVertList obj2 vertID2 ) ) ) if not foundValue then ( append this.VertMatchList #(vertPosition, #((datapair obj:obj1 vertID:vertID1), (datapair obj:obj2 vertID:vertID2))) this.AppendCoincidentVertList obj1 vertID1 this.AppendCoincidentVertList obj2 vertID2 ) ) else append this.VertMatchList #(vertPosition, #((datapair obj:obj1 vertID:vertID1), (datapair obj:obj2 vertID:vertID2))) this.AppendCoincidentVertList obj1 vertID1 this.AppendCoincidentVertList obj2 vertID2 ), --------------------------------------------------------------------------------------------------------------- -- CHANNEL FUNCTIONS --------------------------------------------------------------------------------------------------------------- -- deselect objects with no map vertices on the given channel and swaps the channel for the vertex color channel for function access fn PrepObjectsForChannel channelNum = ( local objList = #() for obj in selection where (superClassOf obj == GeometryClass) do ( convertToMesh obj if (this.DoesChannelInfoExist obj 0) and (this.DoesChannelInfoExist obj channelNum) then ( append objList obj if channelNum != 0 then ( this.SwapChannels obj 0 channelNum ) ) ) select objList ), -- swaps the channels back after operation fn RevertObjects channelNum = ( for obj in selection do this.Swapchannels obj 0 channelNum ), -- function swaps the map channels c1 and c2 of the supplied object fn SwapChannels obj c1 c2 = ( if (this.DoesChannelInfoExist obj c1) and (this.DoesChannelInfoExist obj c2) then ( local tempChannel = 26 ChannelInfo.CopyChannel obj 3 c1 ChannelInfo.PasteChannel obj 3 tempChannel ChannelInfo.CopyChannel obj 3 c2 ChannelInfo.PasteChannel obj 3 c1 ChannelInfo.CopyChannel obj 3 tempChannel ChannelInfo.PasteChannel obj 3 c2 convertToMesh obj return true ) else return false ), -- test for if map vertex information is present for the given channel on an object fn DoesChannelInfoExist obj ch = ( local channelInfoExists = false local numMapVerts = 0 try ( if classOf obj == Editable_Mesh then numMapVerts = meshop.getNumMapVerts obj ch else numMapVerts = polyop.getNumMapVerts obj ch ) catch() if (numMapVerts > 0) then channelInfoExists = true return channelInfoExists ), fn PrintBlendList blendList = ( local count = blendList.count format "\nBlendList\n" for i = 1 to count do ( format ("item " + i as string + "\n") local items = blendList[i].count for j = 1 to items do ( format (blendList[i][j].obj.name + " index: " + blendList[i][j].index as string + " smoothing groups: " + blendList[i][j].smoothingGroups as string + "\n") ) ) ), fn PrintVertMatchList clear:true = ( if (clear) then clearListener() format "\nVertMatchList\n" for item in this.VertMatchList do ( local infostring = (item[1] as string + ": " + item[2][1].obj.name + ": " + item[2][1].vertID as string) for i = 2 to item[2].count do infostring += (", " + item[2][i].obj.name + ": " + item[2][i].vertID as string) infoString += "\n" format infoString ) ) ) -- End RsVertexColourBlenderStruct rollout RsRollVertexColourBlenderRoll "Vertex Colour Blender:" ( label edgeblendLbl "This tool blends vert colours across objects." checkbox cbVertexColour "Vertex Colour" checked:true across:2 spinner spnrTolerance "Match Tolerance" range:[0.001, 0.25, 0.01] toolTip:"the maximum distance between two matching vertices" align:#left checkbox cbIllumination "Vertex Illumination" checked:false across:2 checkbox cbOnlyBorderVerts "Only Border Verts" checked:true checkbox cbAlpha "Vertex Alpha" checked:false across:2 checkbox cbRespectSmoothingGroups "Respect Smoothing Groups" checked: true checkbox cbOther "Map Channel" checked:false across:3 spinner spnrCustom "" range:[1, 30, 1] type:#integer offset:[-60,0] width:50 checkbox cbRespectMaterialIds "Respect Material Ids" checked: true offset:[-50,0] button btnBlendEdgeColours "Blend Colours" tooltip:"Blends vert colours across objects." width:RsRollVertexColourBlenderRoll.width align:#center progressBar pbEdgeBlender value:0 color:(color 216 216 216) width:RsRollVertexColourBlenderRoll.width align:#center height:6 local vertexColourBlender = RsVertexColourBlenderStruct() on btnBlendEdgeColours pressed do ( vertexColourBlender.processVC = cbVertexColour.checked vertexColourBlender.processVI = cbIllumination.checked vertexColourBlender.processAlpha = cbAlpha.checked vertexColourBlender.processOther = cbOther.checked vertexColourBlender.customChannel = spnrCustom.Value vertexColourBlender.respectSmoothingGroups = cbRespectSmoothingGroups.checked vertexColourBlender.respectMaterialIds = cbRespectMaterialIds.checked vertexColourBlender.tolerance = spnrTolerance.Value vertexColourBlender.processOnlyBorderVerts = cbOnlyBorderVerts.checked vertexColourBlender.ProcessEdges() ) ) -- End RsRollVertexColourBlenderRoll --Debug --createDialog RsRollVertexColourBlenderRoll width:400