Files
2025-09-29 00:52:08 +02:00

658 lines
18 KiB
Plaintext
Executable File

---------------------------------------------------------------------------------------------------------------
--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