658 lines
18 KiB
Plaintext
Executable File
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 |