1872 lines
46 KiB
Plaintext
Executable File
1872 lines
46 KiB
Plaintext
Executable File
/*
|
|
Go with the flow
|
|
Flow map generator
|
|
*/
|
|
|
|
|
|
filein (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms")
|
|
RsCollectToolUsageData (getThisScriptFilename())
|
|
|
|
escapeEnable = true
|
|
options.printAllElements = false
|
|
|
|
--helpers
|
|
--filein ( theWildWest + "script/max/rockstar_north/maps/FlowVortex.ms" )
|
|
/*
|
|
struct CPV
|
|
(
|
|
index, colour
|
|
)
|
|
*/
|
|
|
|
struct FlowMapperConfig
|
|
(
|
|
theWildWest = RsConfigGetWildWestDir(),
|
|
configPath = theWildWest + "etc/config/general/flowmapper.dat",
|
|
centreFlowBias = 1.0,
|
|
flowWidthMin = 10.0,
|
|
flowWidthMax = 50.0,
|
|
shoreDrag = 0.15,
|
|
slopeScale = 1.0,
|
|
quality = 1,
|
|
flowMapPath = (RsConfigGetArtDir() + "textures/"),
|
|
|
|
--------------------------------------
|
|
--CONFIGURATION HANDLING
|
|
--------------------------------------
|
|
fn createConfig =
|
|
(
|
|
fs = createFile configPath
|
|
|
|
--write defaults
|
|
format "--FlowMapper user config file\n\n" to:fs
|
|
format "cfg_centreFlowBias = %\n" centreFlowBias to:fs
|
|
format "cfg_flowWidthMin = %\n" flowWidthMin to:fs
|
|
format "cfg_flowWidthMax = %\n" flowWidthMax to:fs
|
|
format "cfg_slopeScale = %\n" slopeScale to:fs
|
|
format "cfg_quality = %\n" quality to:fs
|
|
format "cfg_flowMapPath = %\n" ("\""+flowMapPath+"\"") to:fs
|
|
|
|
--close filestream
|
|
close fs
|
|
|
|
),
|
|
|
|
fn loadConfig =
|
|
(
|
|
centreFlowBias = (getINISetting configPath "baseFlow" "cfg_centreFlowBias") as Float
|
|
flowWidthMin = (getINISetting configPath "baseFlow" "cfg_flowWidthMin") as Float
|
|
flowWidthMax = (getINISetting configPath "baseFlow" "cfg_flowWidthMax") as Float
|
|
shoreDrag = (getINISetting configPath "baseFlow" "cfg_shoreDrag") as Float
|
|
slopeScale = (getINISetting configPath "baseFlow" "cfg_slopeScale") as Float
|
|
quality = (getINISetting configPath "output" "cfg_quality") as Integer
|
|
flowMapPath = getINISetting configPath "output" "cfg_flowMapPath"
|
|
),
|
|
|
|
fn saveConfig =
|
|
(
|
|
--print configPath
|
|
if doesFileExist configPath == true then
|
|
(
|
|
--delete the old version
|
|
try
|
|
(
|
|
deleteFile configPath
|
|
)
|
|
catch
|
|
(
|
|
messagebox "Couldnt delete config file"
|
|
)
|
|
)
|
|
|
|
setINISetting configPath "baseFlow" "cfg_centreFlowBias" (centreFlowBias as string)
|
|
setINISetting configPath "baseFlow" "cfg_flowWidthMin" (flowWidthMin as string)
|
|
setINISetting configPath "baseFlow" "cfg_flowWidthMax" (flowWidthMax as string)
|
|
setINISetting configPath "baseFlow" "cfg_shoreDrag" (shoreDrag as string)
|
|
setINISetting configPath "baseFlow" "cfg_slopeScale" (slopeScale as string)
|
|
|
|
setINISetting configPath "output" "cfg_quality" (quality as string)
|
|
setINISetting configPath "output" "cfg_flowMapPath" ("\""+flowMapPath+"\"")
|
|
),
|
|
|
|
on create do
|
|
(
|
|
--print "\n\nconfig constructor called!!!\n\n"
|
|
--Check config exists and update UI values accordingly
|
|
--otherwise create a config file with defaults
|
|
if doesFileExist configPath == true then
|
|
(
|
|
--Load config
|
|
loadConfig()
|
|
--break()
|
|
)
|
|
else --create config
|
|
(
|
|
--createConfig()
|
|
saveConfig()
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
struct FlowSpline
|
|
(
|
|
name,
|
|
uid,
|
|
|
|
fn getUID name =
|
|
(
|
|
return uid
|
|
),
|
|
|
|
fn getName uid =
|
|
(
|
|
return name
|
|
)
|
|
|
|
)
|
|
|
|
struct FlowMapper
|
|
(
|
|
|
|
flowSplineItems = #(),
|
|
config = FlowMapperConfig(),
|
|
|
|
--UI
|
|
rolloutUI,
|
|
flowSplineListUI,
|
|
flowHelperID = dotNetObject "RSG.MaxUtils.MaxDictionary",
|
|
|
|
--Meshes
|
|
baseObject,
|
|
flowObject,
|
|
|
|
--Flow splines
|
|
flowSplines = #(),
|
|
flowSplineID = dotnetobject "RSG.MaxUtils.MaxDictionary", --key=node.handle value=node.name
|
|
|
|
--Flow Helper types
|
|
flowHelperTypes = #("Blocker", "Faster", "Slower"),
|
|
|
|
--Flowmap bake
|
|
bakeSize = 512,
|
|
bakeLength = bakeSize,
|
|
bakeWidth = bakeSize,
|
|
artFolderRoot = RsConfigGetArtDir(),
|
|
flowMapPath = config.flowMapPath,
|
|
flowMapTexturePath,
|
|
flowMapBmp,
|
|
quality = config.quality,
|
|
|
|
--vert positions
|
|
aVPos = #(),
|
|
--flow colours
|
|
aCPV = #(),
|
|
|
|
--Generation parameters
|
|
centreFlowBias = config.centreFlowBias,
|
|
slopeScale = config.slopeScale,
|
|
shoreDrag = 0.15,
|
|
|
|
obj,
|
|
|
|
--callbacks
|
|
cb_nodeSelChange = undefined,
|
|
cb_nodeRename = undefined,
|
|
cb_nodeDelete = undefined,
|
|
cb_undo = undefined,
|
|
|
|
|
|
--///////////////////////////////////////////////////////////////
|
|
--STRUCT FUNCTIONS
|
|
--//////////////////////////////////////////////////////////////
|
|
fn addToHelperDict helperObj =
|
|
(
|
|
if flowHelperID.ContainsKey(helperObj.handle) == true then
|
|
(
|
|
messageBox "This object is already a flow helper" title:"Error"
|
|
return false
|
|
|
|
)
|
|
else if flowHelperID.ContainsValue(helperObj.name) == true then --check for name in dict
|
|
(
|
|
messageBox "There is already a FlowHelper with the same name, please rename it" title:"Error"
|
|
return false
|
|
)
|
|
else
|
|
(
|
|
flowHelperID.add helperObj.handle helperObj.name
|
|
return true
|
|
)
|
|
return false
|
|
),
|
|
|
|
fn removeFromHelperDict helperObj =
|
|
(
|
|
if flowHelperID.ContainsKey(helperObj.handle) == true then
|
|
(
|
|
flowHelperID.remove(helperObj.handle)
|
|
)
|
|
|
|
),
|
|
|
|
--Returns a list of Flowhelpers that have been defined and stored in flowHelperID dict
|
|
fn getFlowHelperList =
|
|
(
|
|
helperList = #()
|
|
helperHnd = #()
|
|
it = flowHelperID.GetEnumerator()
|
|
|
|
nextIt = true
|
|
while nextIt do
|
|
(
|
|
item = it.current
|
|
append helperHnd item.key
|
|
nextIt = it.movenext()
|
|
)
|
|
format "helperHnd: %\n" helperHnd
|
|
if helperHnd.count > 0 then
|
|
(
|
|
for h in helperHnd do
|
|
(
|
|
if h != undefined then
|
|
(
|
|
append helperList (maxOps.getNodeByHandle h)
|
|
)
|
|
)
|
|
--helperList = for h in helperHnd collect (maxOps.getNodeByHandle h) where h != undefined
|
|
)
|
|
|
|
--return
|
|
helperList
|
|
),
|
|
|
|
fn getFlowSplinesFromObject =
|
|
(
|
|
--get the current selected object retreive flowsplines from it
|
|
obj = getCurrentSelection()
|
|
obj = obj[1]
|
|
|
|
--get the splines from the object
|
|
ss = StringStream ""
|
|
objHasAppData = getAppData obj 1
|
|
appData = #()
|
|
if objHasAppData != undefined then
|
|
(
|
|
ss = StringStream ( objHasAppData )
|
|
|
|
while not eof ss do
|
|
(
|
|
append appData (readValue ss)
|
|
)
|
|
)
|
|
--return
|
|
appData
|
|
),
|
|
|
|
--find the nearest point on a spline to a given position
|
|
fn nearestPointOnSpline pos spline splineIndex:1 =
|
|
(
|
|
pathParam = nearestPathParam spline splineIndex pos
|
|
uniformParam = pathToLengthParam spline splineIndex pathParam steps:50
|
|
closestPointOnSpline = lengthInterp spline splineIndex uniformParam steps:50
|
|
--return
|
|
closestPointOnSpline
|
|
),
|
|
|
|
fn normalizeValue vMin vMax val =
|
|
(
|
|
newVal = vMin + (1.0 / vMax) * (val - vMin)
|
|
newVal = vMax - vMin
|
|
--return
|
|
newVal
|
|
|
|
),
|
|
|
|
|
|
--find the closest point on a given spline from a given position of interest
|
|
fn distanceToSpline pos spline =
|
|
(
|
|
d = undefined
|
|
|
|
splinePos = FlowMapper.nearestPointOnSpline pos spline
|
|
|
|
d = distance splinePos pos
|
|
|
|
--return
|
|
d
|
|
),
|
|
|
|
--find the sortest distance to a spline for an array of splines
|
|
fn closestSpline pos aSplines =
|
|
(
|
|
if aSplines == undefined then throw("Missing Array")
|
|
--assert (aSplines != undefined)
|
|
/*
|
|
closest = 99999
|
|
for spline in aSplines do
|
|
(
|
|
d = FlowMapper.distanceToSpline pos spline
|
|
--break()
|
|
if d < closest then
|
|
(
|
|
closest = d
|
|
)
|
|
)
|
|
format "closest: %\n" closest
|
|
--return
|
|
closest
|
|
*/
|
|
closest = #()
|
|
for spline in aSplines do
|
|
(
|
|
if spline != undefined do (
|
|
d = FlowMapper.distanceToSpline pos spline
|
|
splineDist = #(spline, d)
|
|
append closest splineDist
|
|
)
|
|
)
|
|
|
|
--return
|
|
closest
|
|
|
|
),
|
|
|
|
--From a given spline and index on that spline,
|
|
--return the vector for the spline at that point
|
|
fn getFlowDirection pos spline splineIndex:1 =
|
|
(
|
|
pathParam = nearestPathParam spline splineIndex pos
|
|
uniformParam = pathToLengthParam spline splineIndex pathParam steps:50
|
|
tangent = lengthTangent spline splineIndex uniformParam steps:50
|
|
--resetLengthInterp()
|
|
--return
|
|
tangent
|
|
),
|
|
|
|
--Average normalized vector from an array of splines
|
|
fn weightedAvgFlowDirection pos aSplines =
|
|
(
|
|
vec = [0, 0, 0]
|
|
|
|
for spline in aSplines do
|
|
(
|
|
--format "spline: %\n" spline
|
|
tempVec = FlowMapper.getFlowDirection pos spline[1]
|
|
--distance falloff
|
|
distFalloff = 1.0 / spline[2]^2
|
|
--distFalloff = amax distFalloff 2.0
|
|
vec += tempVec * distFalloff
|
|
)
|
|
vec = normalize(vec)
|
|
--return
|
|
vec
|
|
),
|
|
|
|
--weighted average of distances from splines
|
|
--takes in the two elemement array pairs of spline and distance, so we want element [2]
|
|
fn weightedAvgFlowDistance aDistances =
|
|
(
|
|
avg = 0
|
|
combined = 0
|
|
for d in aDistances do
|
|
(
|
|
combined += d[2]
|
|
)
|
|
avg = combined / aDistances.count
|
|
|
|
--return
|
|
avg
|
|
),
|
|
|
|
fn createFlowHelper helperType =
|
|
(
|
|
--spline selected?
|
|
obj = getcurrentselection()
|
|
obj = obj[1]
|
|
|
|
objClass = classOf obj
|
|
print objClass
|
|
|
|
if isKindOf obj shape != true then
|
|
(
|
|
messageBox "Not a spline!" title:"Selection invalid:"
|
|
return undefined
|
|
)
|
|
|
|
--not a flowspline?
|
|
if MatchPattern obj.name pattern:"flowSpline_*" then
|
|
(
|
|
messageBox "Spline is a flowspline_!" title:"Selection invalid:"
|
|
return undefined
|
|
)
|
|
|
|
--closed?
|
|
if not isClosed $ 1 then
|
|
(
|
|
messageBox "Spline isn't closed!" title:"Selection invalid:"
|
|
return undefined
|
|
)
|
|
|
|
--all good then prefix spline with name of type
|
|
obj.name = ("Flow_" + helperType + "_" + obj.name)
|
|
|
|
--setAppData channel 2
|
|
setAppData obj 2 ("Flow_" + helperType)
|
|
|
|
--add to FlowHelperID dict
|
|
addToHelperDict obj
|
|
|
|
--add watermesh handle to node appdata
|
|
|
|
return obj
|
|
|
|
),
|
|
|
|
------------------------------------------
|
|
--Convert the flow vector to a colour
|
|
------------------------------------------
|
|
fn getFlowColour vec =
|
|
(
|
|
redXCol = ( vec.x * 127) + 128
|
|
|
|
greenYCol = ( vec.y * 127) + 128
|
|
|
|
flowColour = color redXCol greenYCol 127
|
|
|
|
--return
|
|
flowColour
|
|
|
|
),
|
|
|
|
--Take the accumulated generated CPV and apply to the mesh
|
|
fn setFlowColours obj =
|
|
(
|
|
aCPVCount = aCPV.count
|
|
rolloutUI.prgProcess.color = red
|
|
for v = 1 to aCPVCount do
|
|
(
|
|
--convert aCPV value to a colour
|
|
colour = getFlowColour aCPV[v]
|
|
|
|
--set flow vec colour
|
|
polyOp.setVertColor obj 0 v colour
|
|
|
|
rolloutUI.prgProcess.value = 100 * v / aCPVCount
|
|
)
|
|
|
|
--reset progress
|
|
rolloutUI.prgProcess.value = 0
|
|
|
|
|
|
),
|
|
|
|
|
|
fn createFlowObject obj =
|
|
(
|
|
-----------------------------------------
|
|
--get object selected, create a copy
|
|
-----------------------------------------
|
|
|
|
if classOf obj != Editable_Poly then
|
|
(
|
|
messagebox "Select an Editable Poly object"
|
|
return false
|
|
)
|
|
|
|
--store a referenc to the original object in the struct
|
|
baseObject = obj
|
|
|
|
--create a copy fo the obj to make a flow object that can be uprezzed to the quality we want
|
|
flowObject = copy obj
|
|
flowObject.name = obj.name + "FLOW"
|
|
|
|
--subdivide for detail
|
|
quality = rolloutUI.spnQuality.value
|
|
smoothMod = TurboSmooth name:"Detail" iterations:quality
|
|
addModifier flowObject smoothMod
|
|
addModifier flowObject (Turn_To_Poly())
|
|
collapseStack flowObject
|
|
|
|
--add vertex color material
|
|
flowObject.material = standardMaterial()
|
|
flowObject.material.diffuseMap = Vertex_Color()
|
|
flowObject.material.name ="FLOW_VERT_COLOR"
|
|
|
|
--show vertex colours
|
|
flowObject.showVertexColors = on
|
|
|
|
flowObject
|
|
),
|
|
|
|
fn defaultBakePath =
|
|
(
|
|
imagesDir = getDir #image
|
|
flowMapPath = imagesDir
|
|
rolloutUI.txtBakePath.text = flowMapPath
|
|
|
|
flowMapPath
|
|
),
|
|
|
|
|
|
fn getBakePath =
|
|
(
|
|
--bakePath = artFolderRoot+"Textures/_AREAS/Countryside/CS3_05/"+flowObject.name+"_bake.bmp"
|
|
if flowMapPath == undefined then
|
|
(
|
|
defaultBakePath()
|
|
return flowMapPath
|
|
)
|
|
bakeRoot = artFolderRoot+"Textures"
|
|
|
|
grabPath = getOpenFileName caption:"FlowMap Bake Location:" filename:bakeRoot
|
|
--handle user cancel dialog
|
|
if grabPath == undefined then
|
|
(
|
|
return false
|
|
)
|
|
grabPath = substituteString grabPath "\\" "/"
|
|
--fo'git the file, we jus' want the path
|
|
flowMapPath = getFileNamePath grabPath
|
|
flowMapPath = substitutestring flowMapPath "\\" "/"
|
|
|
|
--bakePath += "/"+flowObject.name+"_bake.bmp"
|
|
--print bakePath
|
|
rolloutUI.txtBakePath.text = flowMapPath
|
|
|
|
--return
|
|
flowMapPath
|
|
),
|
|
|
|
fn generateFlowTextures =
|
|
(
|
|
--reset the bake dimensions
|
|
bakeWidth = bakeSize
|
|
bakeLength = bakeSize
|
|
|
|
--make sure the vfb is closed
|
|
if flowMapBmp != undefined then
|
|
(
|
|
undisplay flowMapBmp
|
|
)
|
|
|
|
--create uvwmap
|
|
uvmap = UVWMap()
|
|
addModifier baseObject uvmap
|
|
addModifier flowObject uvmap
|
|
|
|
|
|
ratio = 1
|
|
if uvmap.width > uvmap.length then
|
|
(
|
|
ratio = (uvmap.width / uvmap.length) as integer
|
|
|
|
if ratio < 1 then ratio = 1
|
|
|
|
for i = 1 to ratio by 2 do
|
|
(
|
|
bakeLength /= 2
|
|
)
|
|
|
|
)
|
|
else if uvmap.length > uvmap.width then
|
|
(
|
|
ratio = (uvmap.length / uvmap.width) as integer
|
|
|
|
if ratio < 1 then ratio = 1
|
|
|
|
for i = 1 to ratio by 2 do
|
|
(
|
|
bakeWidth /= 2
|
|
)
|
|
|
|
)
|
|
--format "bakeWidth: % bakeLength:% \n" bakeWidth bakeLength
|
|
|
|
|
|
--collapse stack
|
|
|
|
--render to texture
|
|
|
|
--create bakepath
|
|
flowMapTexturePath = flowMapPath+flowObject.name+".bmp"
|
|
|
|
--remove old bake texture
|
|
try
|
|
(
|
|
if doesFileExist (flowMapTexturePath) == true then
|
|
(
|
|
deleteFile flowMapTexturePath
|
|
)
|
|
)
|
|
catch
|
|
(
|
|
messagebox "couldnt delete old bake texture"
|
|
)
|
|
|
|
--Clear all render elements
|
|
flowObject.iNodeBakeProperties.removeAllBakeElements()
|
|
|
|
diffuseBake = diffusemap()
|
|
|
|
--background colour
|
|
diffuseBake.backgroundColor = ( color 127 127 127 0 )
|
|
--set size
|
|
diffuseBake.outputSzX = bakeWidth
|
|
diffuseBake.outputSzY = bakeLength
|
|
|
|
--file location
|
|
diffuseBake.fileName = flowMapTexturePath
|
|
diffuseBake.fileType = flowMapTexturePath
|
|
|
|
diffuseBake.filterOn = true --enable filtering
|
|
|
|
diffuseBake.shadowsOn = false --disable shadows
|
|
|
|
diffuseBake.lightingOn = false --disable lighting
|
|
|
|
diffuseBake.enabled = true --enable baking
|
|
|
|
--Preparing the object for baking:
|
|
|
|
flowObject.INodeBakeProperties.addBakeElement diffuseBake --add first element
|
|
|
|
flowObject.INodeBakeProperties.bakeEnabled = true --enabling baking
|
|
|
|
flowObject.INodeBakeProperties.bakeChannel = 1 --channel to bake
|
|
|
|
flowObject.INodeBakeProperties.nDilations = 5 --expand the texture a bit
|
|
|
|
select flowObject --we are baking the selection, so we select the object
|
|
|
|
--Call the renderer to bake both elements:
|
|
render rendertype:#bakeSelected vfb:off progressBar:true outputSize:[bakeSize, bakeSize]
|
|
|
|
--display the result
|
|
flowMapBmp = openBitMap flowMapTexturePath
|
|
if flowMapBmp != undefined then
|
|
(
|
|
display flowMapBmp
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
----------------------------------
|
|
--NEW VERSION
|
|
----------------------------------
|
|
fn lowFreqFlow =
|
|
(
|
|
--interface lookup
|
|
polyop_getvert = polyop.getvert
|
|
|
|
--get the splines from UI selection
|
|
--flowSplines = rolloutUI.lstFlowSplines.items
|
|
--get the flowsplines from the object AppData
|
|
flowSplines = getFlowSplinesFromObject()
|
|
|
|
--Check for any flow splines
|
|
if flowSplines.count == 0 then
|
|
(
|
|
messagebox "No flow splines selected"
|
|
return false
|
|
)
|
|
|
|
--get flowsplines
|
|
aFlows = #()
|
|
for splineHnd in flowSplines do
|
|
(
|
|
--get the obj from handle
|
|
itemName = maxOps.getNodeByHandle splineHnd
|
|
append aFlows itemName
|
|
)
|
|
|
|
--openEdges on baseObject mesh that will be enough detail to test against and faster than the flowObject
|
|
aBorderVertIdx = (polyop.getVertsUsingEdge flowObject (polyop.getOpenEdges flowObject)) as array
|
|
aBorderVerts = #()
|
|
for v in aBorderVertIdx do
|
|
(
|
|
--pos = baseObject.GetVertex v
|
|
pos = polyop_getvert flowObject v node:flowObject
|
|
append aBorderVerts pos
|
|
)
|
|
|
|
--Width flow scaling from UI vars
|
|
wMin = rolloutUI.spnFlowWidthMin.value
|
|
wMax = rolloutUI.spnFlowWidthMax.value
|
|
--wDiff = wMax - wMin
|
|
wDiff = 1.0 / (wMax - wMin)
|
|
wMinBase = wMin * wDiff
|
|
format "wMin: % wMax: % wDIff: % wMinBase: %\n" wMin wMax wDiff wMinBase
|
|
--iterate verts
|
|
numVerts = flowObject.vertices.count
|
|
for i=1 to numVerts do
|
|
(
|
|
--vPos
|
|
vPos = polyop_getvert flowObject i node:flowObject
|
|
|
|
--lets stuff this into a struct array for use in later passes
|
|
append aVPos vPos
|
|
|
|
--spline distances
|
|
flowDistance = FlowMapper.closestSpline vPos aFlows
|
|
--centre flow vec
|
|
flowVec = FlowMapper.weightedAvgFlowDirection vPos flowDistance
|
|
flowVec = normalize(flowVec)
|
|
--init flowScale
|
|
--flowScale = 1.0
|
|
------------------------------------------------------------------
|
|
--WIDTH SCALING
|
|
------------------------------------------------------------------
|
|
--Now find the closest border egdes verts for width scaling
|
|
|
|
aBVDistance = #()
|
|
for v = 1 to aBorderVerts.count do
|
|
(
|
|
d = distance aBorderVerts[v] vPos
|
|
append aBVDistance d
|
|
)
|
|
sort aBVDistance
|
|
--format "aBVDistance: %\n" aBVDistance
|
|
|
|
|
|
flowWidth = aBVDistance[1] + aBVDistance[2]
|
|
--flowClose = amin aBVDistance[1] aBVDistance[2]
|
|
--format "flowWidth: %" flowWidth
|
|
|
|
--flowWidth = amin flowWidth wMin
|
|
--flowWidth = amax flowWidth wMax
|
|
--if flowWidth > wMin and flowWidth < wMax then
|
|
--(
|
|
--flowScale = abs(0.0 - wMinBase + (flowWidth^2 * wDiff))
|
|
--flowScale = 1.0 - (flowWidth * wDiff)
|
|
--flowScale = 1.0 - (0.0 - wMinBase + (flowWidth * wDiff))
|
|
|
|
flowScale = flowWidth * (1.0 / wMax)
|
|
--flowScale = amin flowScale 1.0
|
|
--)
|
|
--format "flowscale: %\n" flowScale
|
|
|
|
---------------------------------------------------------------
|
|
--BORDER PROXIMITY
|
|
---------------------------------------------------------------
|
|
--are we a border vert?
|
|
|
|
if finditem aBorderVertIdx i != 0 then
|
|
(
|
|
--print "Border\n"
|
|
flowScale *= shoreDrag
|
|
)
|
|
|
|
|
|
--format "before: %" flowVec
|
|
flowVec.x *= flowScale
|
|
flowVec.y *= flowScale
|
|
|
|
|
|
flowVec.z = 0.5
|
|
--format " after: %\n" flowVec
|
|
|
|
append aCPV flowVec
|
|
--update progress
|
|
rolloutUI.prgProcess.value = 100 * i / numVerts
|
|
)
|
|
|
|
rolloutUI.prgProcess.value = 0
|
|
),
|
|
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
--FLOW SLOPE VECTOR
|
|
-- For each vertex point in the mesh work out the slope vector using the connected edges
|
|
-- and colour it according to how fast the mesh is rising or falling at that point and in which direction
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
fn flowSlopeVector =
|
|
(
|
|
--add a new vertexpaint modifier to hold the new data
|
|
--flowSlopeColour = VertexPaint name:"SlopeFlow" mapChannel:0 colorBy:2 layerMode:"Multiply"
|
|
--addModifier flowObject flowSlopeColour
|
|
|
|
numVerts = flowObject.vertices.count
|
|
--Iterate obj verts
|
|
for v = 1 to numVerts do
|
|
(
|
|
--get face normals from surrounding faces and compute average vector weighted by face area
|
|
|
|
--For each vertex pos
|
|
--vPos = polyop.getvert flowObject v
|
|
vPos = aVPos[v]
|
|
|
|
--get a normal using the surrounding faces
|
|
--get faces for vert
|
|
vFaces = polyop.getFacesUsingVert flowObject v
|
|
vFaces = vFaces as array
|
|
--get faceNormals
|
|
vNormal = [0, 0, 0]
|
|
for f =1 to vFaces.count do
|
|
(
|
|
fNormal = polyop.getFaceNormal flowObject vFaces[f]
|
|
vNormal += fNormal
|
|
)
|
|
vNormal = normalize vNormal
|
|
|
|
--colour vertex
|
|
flowColour = [0, 0, 0]
|
|
|
|
dotZ = dot [0, 0, 1] vNormal
|
|
|
|
--create a rotation matrix
|
|
matrix = matrix3 1
|
|
|
|
-- angle of rotation:
|
|
ang = acos (dot vNormal matrix.row3)
|
|
-- axis of rotation:
|
|
axis = normalize (cross vNormal matrix.row3)
|
|
-- rotate about pos of tm:
|
|
rot_tm = preRotate matrix (quat ang axis)
|
|
rot_tm.row4 = vPos
|
|
|
|
/* DEBUG
|
|
--create point help
|
|
--viz = point pos:vPos size:4 isSelected:on
|
|
--viz.transform = rot_tm
|
|
DEBUG */
|
|
|
|
-- new X and Y axis:
|
|
xVec = rot_tm.row1
|
|
yVec = rot_tm.row2
|
|
|
|
--dot the xVec and yVec with the world Z axis to get gradient
|
|
dotX = dot [0, 0, 1] xVec
|
|
dotY = dot [0, 0, 1] yVec
|
|
--format "v: % dotX: % dotY: % \n" v dotX dotY
|
|
|
|
--Create the colour
|
|
--slopeScale up the dot prodcut value as we are considering a hemisphere rather than a full sphere direction
|
|
gradientScale = 1.0 - (slopeScale * dotZ)
|
|
|
|
flowColour.x = gradientScale * ( slopeScale * dotY * 0.5 ) + 0.5
|
|
flowColour.y = gradientScale * ( slopeScale * dotX * 0.5 ) + 0.5
|
|
flowColour.z = 0.5
|
|
--format "slopeCol: %\n" flowColour
|
|
|
|
--get current colour Point3
|
|
--colourNow = polyOp.getMapVert flowObject 0 v
|
|
colourNow = aCPV[v]
|
|
--format "colourNow: %\n" colourNow.colour
|
|
--create the combined color
|
|
newColour = [0.5, 0.5, 0.5]
|
|
|
|
newColour.x = colourNow.x + ( 0.5 - flowColour.x )
|
|
newColour.y = colourNow.y + ( 0.5 - flowColour.y )
|
|
|
|
newColour = normalize(newColour)
|
|
--format "newColour: %\n" newColour
|
|
--update array vert colour
|
|
aCPV[v] = newColour
|
|
|
|
--update progress
|
|
rolloutUI.prgProcess.value = 100 * v / numVerts
|
|
|
|
)
|
|
|
|
--reset progress
|
|
rolloutUI.prgProcess.value = 0
|
|
|
|
--Show the map colours
|
|
--show vertex colours
|
|
flowObject.showVertexColors = on
|
|
flowObject.vertexColorType = 5
|
|
flowObject.vertexColorMapChannel = 0
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
-- EVALUATE USER FEATURE SPLINES
|
|
-- if the artists have created feature splines to influence direction of the flow then evaluate these
|
|
--and adjust the flow accordingly dependent on distance from the feature spline
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
),
|
|
|
|
|
|
--///////////////////////////////////////////////////////////////////////////////////////////////
|
|
--FLOW HELPERS
|
|
--Determine the contribution that user placed flow helper objects make to the flowmap
|
|
--//////////////////////////////////////////////////////////////////////////////////////////////
|
|
fn evalHelpers obj =
|
|
(
|
|
--get bounds of the selected river mesh
|
|
objBB = nodeLocalBoundingBox obj
|
|
objBB_xDiff = objBB[1][1] - objBB[2][1]
|
|
objBB_yDiff = objBB[1][2] - objBB[2][2]
|
|
objBB_Radius = sqrt( objBB_xDiff^2 + objBB_yDiff^2 )
|
|
objBB_Centre = [ 0.5 * objBB_xDiff, 0.5 * objBB_yDiff, 0 ]
|
|
|
|
--for each helper see if its bounds intersect the selected mesh bounds
|
|
--flowHelpers = rolloutUI.lstFlowHelpers.items
|
|
aFlowHelpers = getFlowHelperList()
|
|
--format "aFlowHelpers: % \n" aFlowHelpers
|
|
--up vector for perpendicular vec calculation
|
|
upVec = [0, 0, 1]
|
|
|
|
--Get a list of flow helpers that lie within the bounds of our object so we cut down some work
|
|
aFHelpers = #()
|
|
for fh in aFlowHelpers do
|
|
(
|
|
--test intersection between flowhelper and river mesh
|
|
if (intersects obj fh) == true then
|
|
(
|
|
append aFHelpers fh
|
|
)
|
|
)
|
|
--format "aFHelpers % \n" aFHelpers
|
|
|
|
--with the helpers identified that are significant. Build capped versions for testing.
|
|
aCappedHelpers = #()
|
|
for fh in aFHelpers do
|
|
(
|
|
solidHelper = copy fh
|
|
solidHelper.name = (fh.name + "_CAP")
|
|
addModifier solidHelper (Turn_to_Mesh())
|
|
nm = NormalModifier()
|
|
nm.flip = true
|
|
addModifier solidHelper nm
|
|
append aCappedHelpers solidHelper
|
|
)
|
|
--format "aCappedHelpers % \n" aCappedHelpers
|
|
--Now we got our list of helpers, lets figure out what effect they will have
|
|
numVerts = obj.vertices.count
|
|
|
|
--Iterate obj verts
|
|
for v = 1 to numVerts do
|
|
(
|
|
--vtx Position
|
|
vPos = aVPos[v]
|
|
vRay = ray vPos upVec
|
|
|
|
--iterate helpers
|
|
for fh = 1 to aFHelpers.count do
|
|
(
|
|
--get type and properties
|
|
fhNode = aFHelpers[fh]
|
|
fhType = classof fhNode
|
|
--break()
|
|
--need to match name prefix for type
|
|
--format "fhNode.name % \n" fhNode.name
|
|
if MatchPattern fhNode.name pattern:"Flow_Vortex*" then fhType = "FlowVortex"
|
|
else if MatchPattern fhNode.name pattern:"Flow_Blocker*" then fhType = "FlowBlocker"
|
|
else if MatchPattern fhNode.name pattern:"Flow_Slower*" then fhType = "FlowSlower"
|
|
else if MatchPattern fhNode.name pattern:"Flow_Faster*" then fhType = "FlowFaster"
|
|
|
|
case fhType of
|
|
(
|
|
"FlowBlocker":
|
|
(
|
|
--print "WE GOT A FLOWBLOCKEEEEER!!!"
|
|
--test if inside shape
|
|
hit = intersectRay aCappedHelpers[fh] vRay
|
|
|
|
if hit != undefined then --we inside the shape outline, so full stop on flow vector
|
|
(
|
|
aCPV[v] = [0.0, 0.0, 0.0]
|
|
)
|
|
else --block proportionate to distance from closest point to edge
|
|
(
|
|
--distance
|
|
splinePos = nearestPointOnSpline vPos aFHelpers[fh]
|
|
d = distance vPos splinePos
|
|
d = 1.0 / (d*d)
|
|
|
|
--tangent
|
|
fhTangent = getFlowDirection vPos aFHelpers[fh]
|
|
fhTangent = normalize fhTangent
|
|
|
|
--normal
|
|
fhNormal = [fhTangent[2], -fhTangent[1], fhTangent[3]]
|
|
fhNormal = normalize fhNormal
|
|
|
|
--current flow
|
|
flowNow = aCPV[v]
|
|
flowNow = normalize flowNow
|
|
blockerDir = dot fhNormal flowNow
|
|
|
|
newFlow = [flowNow[1]*blockerDir*d, flowNow[2]*blockerDir*d, 0.0]
|
|
--format "newFlow: %\n" newFlow
|
|
aCPV[v] = flowNow - newFlow
|
|
|
|
|
|
)
|
|
)
|
|
|
|
"FlowFaster":
|
|
(
|
|
--print "WE GOT A FLOWFASTEEEEER!!!"
|
|
--test if inside shape
|
|
hit = intersectRay aCappedHelpers[fh] vRay
|
|
|
|
if hit != undefined then --we inside the shape outline, so increase flow vector
|
|
(
|
|
--print "hit!"
|
|
--current flow
|
|
flowNow = aCPV[v]
|
|
flowNow.x = flowNow.x * 2.0
|
|
flowNow.y = flowNow.y * 2.0
|
|
|
|
if flowNow.x > 1.0 or flowNow.y > 1.0 then
|
|
(
|
|
f = amax flowNow.x flowNow.y
|
|
f = 1.0 / f
|
|
|
|
flowNow.x = f * flowNow.x
|
|
flowNow.y = f * flowNow.y
|
|
)
|
|
|
|
aCPV[v] = [flowNow.x, flowNow.y, 0.5]
|
|
)
|
|
else --block proportionate to distance from closest point to edge
|
|
(
|
|
--distance
|
|
splinePos = nearestPointOnSpline vPos aFHelpers[fh]
|
|
d = distance vPos splinePos
|
|
d = 1.0 / d
|
|
|
|
|
|
--current flow
|
|
flowNow = aCPV[v]
|
|
--flowNow = normalize flowNow
|
|
--blockerDir = dot fhNormal flowNow
|
|
|
|
newFlow = [flowNow.x + d, flowNow.y + d, 0.0]
|
|
--format "newFlow: %\n" newFlow
|
|
aCPV[v] = flowNow + newFlow
|
|
)
|
|
)
|
|
|
|
"FlowSlower":
|
|
(
|
|
--print "WE GOT A FLOWSLOWEEEEER!!!"
|
|
--test if inside shape
|
|
hit = intersectRay aCappedHelpers[fh] vRay
|
|
|
|
if hit != undefined then --we inside the shape outline, so increase flow vector
|
|
(
|
|
--print "hit!"
|
|
--current flow
|
|
flowNow = aCPV[v]
|
|
flowNow.x = flowNow.x * 0.5
|
|
flowNow.y = flowNow.y * 0.5
|
|
|
|
aCPV[v] = [flowNow.x, flowNow.y, 0.5]
|
|
)
|
|
else --block proportionate to distance from closest point to edge
|
|
(
|
|
--distance
|
|
splinePos = nearestPointOnSpline vPos aFHelpers[fh]
|
|
d = distance vPos splinePos
|
|
d = 1.0 / d
|
|
|
|
--current flow
|
|
flowNow = aCPV[v]
|
|
--flowNow = normalize flowNow
|
|
--blockerDir = dot fhNormal flowNow
|
|
|
|
newFlow = [flowNow.x - d, flowNow.y - d, 0.0]
|
|
--format "newFlow: %\n" newFlow
|
|
aCPV[v] = newFlow
|
|
)
|
|
|
|
|
|
)
|
|
)
|
|
)
|
|
|
|
--progress
|
|
rolloutUI.prgProcess.value = 100 * v / numVerts
|
|
)--vert iterator
|
|
|
|
--DEBUG
|
|
--hObj = copy flowObject
|
|
--setFlowColours hObj
|
|
--/DEBUG
|
|
--reset progress
|
|
rolloutUI.prgProcess.value = 0
|
|
|
|
--delete capped helpers
|
|
for n in aCappedHelpers do delete n
|
|
|
|
),
|
|
|
|
|
|
--//////////////////////////
|
|
--main process control func
|
|
--main process control func
|
|
--main process control func
|
|
--//////////////////////////
|
|
fn doFlows =
|
|
(
|
|
windows.processPostedMessages ()
|
|
--print self.flowSplineListUI
|
|
--set coordinate system to world
|
|
setRefCoordSys #world
|
|
|
|
--clear the arrays
|
|
aCPV = #()
|
|
aVPos = #()
|
|
|
|
--get current object selection
|
|
obj = getcurrentselection()
|
|
obj = obj[1]
|
|
|
|
--persist object
|
|
persistObj = obj
|
|
|
|
collapseStack obj
|
|
|
|
--set struct var for the flowObject that other functions will use
|
|
createFlowObject(obj)
|
|
|
|
--call low freq flow generation
|
|
success = lowFreqFlow()
|
|
if success == false then
|
|
(
|
|
delete flowObject
|
|
return false
|
|
)
|
|
|
|
success = flowSlopeVector()
|
|
|
|
|
|
--if not success then return false
|
|
|
|
--call high freq flow generation
|
|
evalHelpers(flowObject)
|
|
|
|
--set the flow colour from CPV array
|
|
setFlowColours(flowObject)
|
|
|
|
--generate textures from cpv layers
|
|
generateFlowTextures()
|
|
|
|
--apply flow map to base mesh
|
|
RstSetVariable obj.material 3 flowMapTexturePath
|
|
|
|
--clean up
|
|
delete flowObject
|
|
|
|
collapseStack persistObj
|
|
--select obj
|
|
|
|
--trash compacter
|
|
gc
|
|
),
|
|
|
|
--/////////////////////////////////////////////
|
|
--Callbacks ///////////////////////////////////
|
|
--/////////////////////////////////////////////
|
|
fn onNodeSelChange =
|
|
(
|
|
--query the object for appData to see if it has any splines associated with it or conversely any object
|
|
obj = getCurrentSelection()
|
|
obj = obj[1]
|
|
|
|
if obj != undefined then
|
|
(
|
|
--so we have an object at least
|
|
--format "obj: % \n" obj.name
|
|
|
|
appData = #()
|
|
objHasAppData = getAppData obj 1
|
|
--format "objHasAppData: %\n" objHasAppData
|
|
if objHasAppData != undefined then
|
|
(
|
|
--print "has appData\n"
|
|
--check its an editable poly
|
|
if isKindOf obj.baseobject Editable_Poly == true then
|
|
(
|
|
ss = StringStream ( objHasAppData )
|
|
|
|
while not eof ss do
|
|
(
|
|
append appData (readValue ss)
|
|
)
|
|
|
|
|
|
splines = #()
|
|
--check first value in appData to see if its a node handle
|
|
--test = (appData[1] as IntegerPtr)
|
|
--format "test % \n" test
|
|
--print appData.count
|
|
if appData.count != 0 then
|
|
(
|
|
if (appData[1] as IntegerPtr) != undefined then
|
|
(
|
|
--Now deal with based on type
|
|
for item in appData do
|
|
(
|
|
--get name from node
|
|
spline = maxOps.getNodeByHandle (item as Integer)
|
|
--format "spline: %\n" spline
|
|
if spline != undefined then
|
|
(
|
|
append splines spline.name
|
|
)
|
|
)
|
|
)
|
|
)
|
|
--format "splines: %\n" splines
|
|
|
|
--update struct var holding flowSplines
|
|
flowSplines = splines
|
|
|
|
--finally update the UI with the splines associated with this object
|
|
rolloutUI.lstFlowSplines.items = splines
|
|
)
|
|
)
|
|
else
|
|
(
|
|
--Update the UI with an empty array
|
|
rolloutUI.lstFlowSplines.items = #()
|
|
)
|
|
)
|
|
else
|
|
(
|
|
--Update the UI with an empty array
|
|
rolloutUI.lstFlowSplines.items = #()
|
|
)
|
|
),
|
|
|
|
fn onNodeDelete =
|
|
(
|
|
--------------------------------------
|
|
--First case deal with flowsplines
|
|
--------------------------------------
|
|
--get its appdata
|
|
obj = callbacks.notificationParam()
|
|
--print obj
|
|
objHasAppData = getAppData obj 1
|
|
|
|
if objHasAppData != undefined then
|
|
(
|
|
--get the splines it holds from its appData
|
|
appData = #()
|
|
--check its an editable poly
|
|
if isKindOf obj.baseobject Editable_Poly == true then
|
|
(
|
|
ss = StringStream ( objHasAppData )
|
|
|
|
while not eof ss do
|
|
(
|
|
append appData (readValue ss)
|
|
)
|
|
)
|
|
|
|
--go through each spline and remove its appData
|
|
for item in appData do
|
|
(
|
|
--get the spline from handle
|
|
spline = maxOps.getNodeByHandle (item as Integer)
|
|
deleteAppData spline 1
|
|
)
|
|
--Update the UI with an empty array
|
|
rolloutUI.lstFlowSplines.items = #()
|
|
)
|
|
|
|
--------------------------------------------------
|
|
--Second case deal with deleting flowObjects
|
|
--------------------------------------------------
|
|
if classOf obj == FlowVortex then
|
|
(
|
|
format "FlowVortex: % \n" obj.name
|
|
--get Flow helper list
|
|
helperList = rolloutUI.lstFlowHelpers.items
|
|
format "helperList: % \n" helperList
|
|
found = findItem helperList obj.name
|
|
format "found: %\n" found
|
|
|
|
if found != 0 then
|
|
(
|
|
newList = deleteItem helperList found
|
|
rolloutUI.lstFlowHelpers.items = newList
|
|
)
|
|
)
|
|
),
|
|
|
|
fn onNodeRename =
|
|
(
|
|
return false
|
|
),
|
|
|
|
---------------------------
|
|
--If we have a scene undo re-check the ui lists incase somethign was deleted and then undone.
|
|
---------------------------
|
|
fn onSceneUndo =
|
|
(
|
|
--Check flow helpers
|
|
|
|
--get current UI list
|
|
helperList = getFlowHelperList()
|
|
|
|
for o in $helpers do
|
|
(
|
|
if isKindOf o FlowVortex == true then
|
|
(
|
|
--check if its in the list
|
|
found = finditem o.name helperList
|
|
if found != 0 then
|
|
(
|
|
|
|
)
|
|
)
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
-------------------------------------------------------------------------------
|
|
-------------------------------------------------------------------------------
|
|
|
|
--Destroy an open dialog if exists
|
|
--if flowMapperTool != undefined then
|
|
--(
|
|
-- destroydialog flowMapperTool.rolloutUI
|
|
--)
|
|
|
|
--create tool
|
|
fn pickFilter obj = classOf obj == SplineShape and matchpattern obj.name pattern:"flowspline_*"
|
|
|
|
if FlowMapperUI != undefined then
|
|
(
|
|
destroydialog FlowMapperUI
|
|
)
|
|
|
|
--CREATE TOOL STRUCT INSTANCE
|
|
--flowMapperConfig = FlowMapperConfig()
|
|
flowMapperTool = FlowMapper()
|
|
|
|
|
|
rollout FlowMapperUI "FlowMapper"
|
|
(
|
|
group "FlowSplines"
|
|
(
|
|
listbox lstFlowSplines
|
|
pickbutton btnPickSplines "Pick" filter:pickFilter across:3
|
|
button btnRemoveSpline "Remove"
|
|
button btnClearPicked "Clear"
|
|
)
|
|
|
|
group "Base Flow"
|
|
(
|
|
--spinner spnCentreFlowBias "Centre Flow Bias:" range:[0.1, 4.0, 1.0] type:#float
|
|
spinner spnFlowWidthMin "Flow Width Min:" range:[1, 100, 5] type:#float
|
|
spinner spnFlowWidthMax "Flow Width Max:" range:[1, 100, 40] type:#float
|
|
spinner spnShoreDrag "Shore Drag:" range:[0.001, 1.0, 0.15] type:#float
|
|
)
|
|
|
|
group "Slope"
|
|
(
|
|
spinner spnSlopeScale "Slope Scale:" range:[1.0, 4.0, 1.0] type:#float
|
|
)
|
|
|
|
group "Flow helpers"
|
|
(
|
|
listbox lstFlowHelpers
|
|
dropdownlist ddlFlowHelpers items:#("Blocker", "Slower", "Faster") across:4
|
|
button btnCreateFlowHelper "Create!"
|
|
button btnRemoveFlowHelper "Remove"
|
|
button btnDestroyFlowHelper "Destroy!"
|
|
)
|
|
|
|
group "FlowMap"
|
|
(
|
|
spinner spnQuality "Quality: " range:[1,8,1] type:#integer
|
|
edittext txtBakePath "Path" style_sunkenedge:true width:185 height:20 labelontop:true readonly:true across:2
|
|
button btnBakePath "..." align:#right offset:[0, 18]
|
|
button btnViewLast "View Last" align:#left
|
|
)
|
|
button btnDo "Go With The Flow!" width:200
|
|
progressbar prgProcess color:orange
|
|
|
|
|
|
---------------------------
|
|
--EVENTS dear boy
|
|
---------------------------
|
|
|
|
on FlowMapperUI open do
|
|
(
|
|
--Set UI values to saved config values
|
|
flowMapperTool.config.loadConfig()
|
|
|
|
--set spinner value struct siblings
|
|
spnSlopeScale.value = flowMapperTool.config.slopeScale
|
|
--spnCentreFlowBias.value = flowMapperTool.config.centreFlowBias
|
|
|
|
spnFlowWidthMin.value = flowMapperTool.config.flowWidthMin
|
|
spnFlowWidthMax.value = flowMapperTool.config.flowWidthMax
|
|
|
|
--mesh quality
|
|
spnQuality.value = flowMapperTool.config.quality
|
|
|
|
--set the flowmap bake path to the config setting
|
|
txtBakePath.text = flowMapperTool.config.flowMapPath
|
|
|
|
--populate the flow helpers with any found in the current scene
|
|
flowHelpers = #()
|
|
for o in $shapes do
|
|
(
|
|
--check appdata
|
|
appData = getAppData o 2
|
|
|
|
if appData != undefined and matchPattern appData pattern:"Flow_*" then
|
|
(
|
|
append flowHelpers o.name
|
|
success = flowMapperTool.addToHelperDict o
|
|
format "Added to FlowHelper dict?: %\n" success
|
|
)
|
|
)
|
|
|
|
lstFlowHelpers.items = flowHelpers
|
|
|
|
--clear out callbacks incase they are present from before
|
|
--flowMapperTool.cb_nodeSelChange = undefined
|
|
--flowMapperTool.cb_nodeDelete = undefined
|
|
--flowMapperTool.cb_nodeRename = undefined
|
|
callbacks.removeScripts id:#FlowMapper
|
|
--gc light:true
|
|
|
|
--register callbacks
|
|
--flowMapperTool.cb_nodeSelChange = NodeEventCallback mouseUp:true selectionChanged:flowMapperTool.onNodeSelChange()
|
|
flowMapperTool.cb_nodeSelChange = callbacks.addScript #selectionSetChanged "flowMapperTool.onNodeSelChange()" id:#FlowMapper
|
|
flowMapperTool.cb_nodeDelete = callbacks.addScript #nodePreDelete "flowMapperTool.onNodeDelete()" id:#FlowMapper
|
|
flowMapperTool.cb_nodeRename = callbacks.addScript #nodeNameSet "flowMapperTool.onNodeRename()" id:#FlowMapper
|
|
flowMapperTool.cb_undo = callbacks.addScript #sceneUndo "flowMappertool.onSceneUndo()" id:#FlowMapper
|
|
|
|
|
|
)
|
|
|
|
on FlowMapperUI close do
|
|
(
|
|
--remove callbacks
|
|
callbacks.removeScripts id:#FlowMapper
|
|
)
|
|
|
|
on btnPickSplines picked spline do
|
|
(
|
|
--get whats there
|
|
--items = lstFlowSplines.items
|
|
items = for i in flowMapperTool.flowSplines collect i
|
|
--this.flowSplineItems = items
|
|
--add pick to it
|
|
|
|
--check for dupes
|
|
for item in items do
|
|
(
|
|
if spline.name == item then
|
|
(
|
|
return false
|
|
)
|
|
)
|
|
|
|
--create a new FlowSpline
|
|
--newFlowSpline = FlowSpline()
|
|
|
|
--get objects unique handle
|
|
hnd = spline.handle
|
|
|
|
--add to dict
|
|
--flowMapperTool.flowSplineID.add spline.name hnd
|
|
--newFlowSpline.name = obj.name
|
|
--newFlowSpline.uid = hnd
|
|
|
|
append flowMapperTool.flowSplines spline.name
|
|
|
|
--append items obj.name
|
|
lstFlowSplines.items = for i in flowMapperTool.flowSplines collect (i as string)
|
|
--this.flowSplineItems = items
|
|
|
|
--add the spline to the object app data for later retreival
|
|
|
|
--add object handle to spline app data to make it exclusive
|
|
selObj = $selection[1]
|
|
selObjHnd = selObj.handle
|
|
deleteAppData spline 1
|
|
setAppData spline 1 (selObjHnd as string)
|
|
|
|
--get the appData for the object as an array, then add the newly picked spline to
|
|
--it and re-add the appData to the object
|
|
|
|
ss = StringStream ""
|
|
objHasAppData = getAppData selObj 1
|
|
appData = #()
|
|
|
|
if objHasAppData != undefined then
|
|
(
|
|
ss = StringStream ( objHasAppData )
|
|
|
|
while not eof ss do
|
|
(
|
|
append appData (readValue ss)
|
|
)
|
|
)
|
|
|
|
--Now append the new spline to the appData
|
|
appendIfUnique appData hnd
|
|
|
|
--Bung it back into the obejct
|
|
ss = StringStream ""
|
|
for d in appData do print d to:ss
|
|
setAppData selObj 1 ss
|
|
|
|
)
|
|
|
|
--remove spline from object
|
|
on btnRemoveSpline pressed do
|
|
(
|
|
--check an object is selected with flowspline data
|
|
selObj = $selection[1]
|
|
objHasAppData = getAppData selObj 1
|
|
--break()
|
|
if objHasAppData == undefined then
|
|
(
|
|
return false
|
|
)
|
|
|
|
--check a flowspline is selected in the ui list
|
|
items = lstFlowSplines.items
|
|
selectedItem = lstFlowSplines.selected
|
|
selectedItemIdx = lstFlowSplines.selection
|
|
|
|
--find the flowspline handle and remove it from the object data
|
|
|
|
appData = #()
|
|
ss = StringStream ( objHasAppData )
|
|
while not eof ss do
|
|
(
|
|
append appData (readValue ss)
|
|
)
|
|
--check for empty appData
|
|
if appData.count == 0 then
|
|
(
|
|
return false
|
|
)
|
|
|
|
aNewSplines = #()
|
|
removeSplineHnd = undefined
|
|
for splineHnd in appData do
|
|
(
|
|
--splineHnd = flowMapperTool.flowSplineID.Item spline.name
|
|
spline = maxOps.getNodeByHandle (splineHnd as Integer)
|
|
splineName = spline.name
|
|
if spline.name != selectedItem then
|
|
(
|
|
append aNewSplines splineHnd
|
|
)
|
|
else
|
|
(
|
|
removeSplineHnd = splineHnd
|
|
)
|
|
|
|
)
|
|
|
|
--add the new list to the object
|
|
ss = StringStream ""
|
|
for d in aNewSplines do print d to:ss
|
|
setAppData selObj 1 ss
|
|
|
|
--remove the object handle from the spline data
|
|
killSpline = maxOps.getNodeByHandle (removeSplineHnd as Integer)
|
|
deleteAppData killSpline 1
|
|
|
|
--remove the spline from flowsplines struct array
|
|
format "flowSplines: %\n" flowMapperTool.flowSplines
|
|
flowMapperTool.flowSplines = deleteItem flowMapperTool.flowSplines selectedItemIdx
|
|
|
|
--remove the spline from the ui list
|
|
aNewList = deleteItem items selectedItemIdx
|
|
lstFlowSplines.items = aNewList
|
|
|
|
--done
|
|
)
|
|
|
|
--clear the listbox of items
|
|
on btnClearPicked pressed do
|
|
(
|
|
--get the current selected object retreive flowsplines from it
|
|
obj = getCurrentSelection()
|
|
obj = obj[1]
|
|
|
|
--get the splines from the object
|
|
ss = StringStream ""
|
|
objHasAppData = getAppData obj 1
|
|
|
|
appData = #()
|
|
if objHasAppData != undefined then
|
|
(
|
|
ss = StringStream ( objHasAppData )
|
|
|
|
while not eof ss do
|
|
(
|
|
append appData (readValue ss)
|
|
)
|
|
)
|
|
|
|
--erase spline appData
|
|
for splineHnd in appData do
|
|
(
|
|
--splineHnd = flowMapperTool.flowSplineID.Item spline.name
|
|
spline = maxOps.getNodeByHandle (splineHnd as Integer)
|
|
if spline != undefined then
|
|
(
|
|
deleteAppData spline 1
|
|
)
|
|
)
|
|
|
|
deleteAppData obj 1
|
|
|
|
--clear UI values
|
|
lstFlowSplines.items = #()
|
|
flowMapperTool.flowSplines = #()
|
|
|
|
)
|
|
|
|
--flowMapPath
|
|
on btnBakePath pressed do
|
|
(
|
|
flowMapperTool.getBakePath()
|
|
if flowMapperTool.flowMapPath != undefined then
|
|
(
|
|
txtBakePath.text = flowMapperTool.flowMapPath
|
|
)
|
|
else
|
|
(
|
|
txtBakePath.text = flowMapperTool.config.flowMapPath
|
|
)
|
|
)
|
|
|
|
--CentreFlowBias
|
|
on spnCentreFlowBias changed arg do
|
|
(
|
|
flowMapperTool.centreFlowBias = spnCentreFlowBias.value
|
|
|
|
)
|
|
|
|
--shore drag
|
|
on spnShoreDrag changed arg do
|
|
(
|
|
flowMapperTool.shoreDrag = spnShoreDrag.value
|
|
)
|
|
|
|
--SlopeScale
|
|
on spnSlopeScale changed arg do
|
|
(
|
|
flowMapperTool.slopeScale = spnSlopeScale.value
|
|
)
|
|
|
|
--quality
|
|
on spnQuality changed arg do
|
|
(
|
|
flowMapperTool.quality = spnQuality.value
|
|
)
|
|
|
|
--FlowMap Helper Create
|
|
on btnCreateFlowHelper pressed do
|
|
(
|
|
--get dropdown selection
|
|
aItems = ddlFlowHelpers.items
|
|
sel = ddlFlowHelpers.selection
|
|
opt = aItems[sel]
|
|
|
|
case opt of
|
|
(
|
|
"Vortex":
|
|
(
|
|
flowVortex = flowMapperTool.createFlowHelper "Vortex"
|
|
if flowVortex != undefined then
|
|
(
|
|
--update UI list
|
|
curList = for i in lstFlowHelpers.items collect i
|
|
append curList flowVortex.name
|
|
|
|
lstFlowHelpers.items = curList
|
|
)
|
|
)
|
|
|
|
"Blocker":
|
|
(
|
|
blocker = flowMapperTool.createFlowHelper "Blocker"
|
|
|
|
if blocker != undefined then
|
|
(
|
|
--add to the helper list
|
|
curList = for i in lstFlowHelpers.items collect i
|
|
append curList blocker.name
|
|
|
|
lstFlowHelpers.items = curList
|
|
)
|
|
)
|
|
|
|
"Slower":
|
|
(
|
|
slower = flowMapperTool.createFlowHelper "Slower"
|
|
|
|
if slower != undefined then
|
|
(
|
|
--add to the helper list
|
|
curList = for i in lstFlowHelpers.items collect i
|
|
append curList slower.name
|
|
|
|
lstFlowHelpers.items = curList
|
|
)
|
|
)
|
|
|
|
"Faster":
|
|
(
|
|
faster = flowMapperTool.createFlowHelper "Faster"
|
|
|
|
if faster != undefined then
|
|
(
|
|
--add to the helper list
|
|
curList = for i in lstFlowHelpers.items collect i
|
|
append curList faster.name
|
|
|
|
lstFlowHelpers.items = curList
|
|
)
|
|
)
|
|
)
|
|
|
|
)
|
|
|
|
fn getHandleByName sel =
|
|
(
|
|
handle = undefined
|
|
it = flowMapperTool.flowHelperID.GetEnumerator()
|
|
|
|
nextIt = true
|
|
while nextIt do
|
|
(
|
|
dict = it.current
|
|
if sel == dict.value then
|
|
(
|
|
handle = dict.key
|
|
--format "handle: %\n" handle
|
|
return handle
|
|
)
|
|
nextIt = it.movenext()
|
|
)
|
|
)
|
|
|
|
on btnRemoveFlowHelper pressed do
|
|
(
|
|
--get dropdown selection
|
|
aItems = ddlFlowHelpers.items
|
|
sel = ddlFlowHelpers.selection
|
|
helperItem = aItems[sel]
|
|
|
|
--get the curent flowhelper from the list
|
|
sel = lstFlowHelpers.selected
|
|
|
|
--get the object using the flowhelper dict
|
|
handle = getHandleByName sel
|
|
obj = maxOps.getNodeByHandle handle
|
|
|
|
--remove the appdata defining the flowhelper type
|
|
deleteAppData obj 2
|
|
|
|
--remove the name prefix
|
|
for n in flowMapperTool.flowHelperTypes do
|
|
(
|
|
if matchPattern obj.name pattern:("*"+ n + "*") then
|
|
(
|
|
--rename
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
--remove it from the flowhelper dict
|
|
flowMapperTool.removeFromHelperDict helperItem
|
|
|
|
--remove it from the ui list
|
|
aItems = deleteItem aItems sel
|
|
ddlFlowHelpers.items = aItems
|
|
)
|
|
|
|
on lstFlowHelpers doubleClicked item do
|
|
(
|
|
--focus on selected item
|
|
aItems = lstFlowHelpers.items
|
|
sel = aItems[item]
|
|
|
|
try
|
|
(
|
|
handle = getHandleByName sel
|
|
|
|
fhNode = maxOps.getNodeByHandle handle
|
|
select ( fhNode )
|
|
max zoomext sel
|
|
)
|
|
catch
|
|
(
|
|
print "FlowHelper dictionary fetch error"
|
|
)
|
|
|
|
)
|
|
|
|
--DO
|
|
on btnDo pressed do
|
|
(
|
|
--check for anything valid selected to work on
|
|
obj = getCurrentSelection()
|
|
|
|
if obj.count != 0 then
|
|
(
|
|
flowSplines = flowMapperTool.getFlowSplinesFromObject()
|
|
|
|
if flowSplines.count != 0 then
|
|
(
|
|
--Save config settings
|
|
--update config
|
|
--flowMapperTool.config.centreFlowBias = spnCentreFlowBias.value
|
|
flowMapperTool.config.slopeScale = spnSlopeScale.value
|
|
flowMapperTool.config.flowWidthMin = spnFlowWidthMin.value
|
|
flowMapperTool.config.flowWidthMax = spnFlowWidthMax.value
|
|
flowMapperTool.config.quality = spnQuality.value
|
|
--flowMapperTool.config.flowMapPath = substitutestring ("\""+flowMapperTool.flowMapPath+"\"") "\\" "/"
|
|
flowMapperTool.config.flowMapPath = txtBakePath.text
|
|
--save config
|
|
flowMapperTool.config.saveConfig()
|
|
|
|
--Do that thang
|
|
flowMapperTool.doFlows()
|
|
)
|
|
|
|
else
|
|
(
|
|
messagebox "Are you sure you have a river mesh selected? I find no flowsplines assigned"
|
|
return false
|
|
)
|
|
)
|
|
|
|
else
|
|
(
|
|
messagebox "Nothing selected, I'll just have a rest till you sort yourself out :)"
|
|
return false
|
|
)
|
|
|
|
)
|
|
|
|
--View last
|
|
on btnViewLast pressed do
|
|
(
|
|
format "flowMapBmp: %\n" flowMapperTool.flowMapBmp
|
|
if flowMapperTool.flowMapBmp != undefined then
|
|
(
|
|
display flowMapperTool.flowMapBmp
|
|
)
|
|
)
|
|
|
|
)
|
|
|
|
flowMapperTool.rolloutUI = FlowMapperUI
|
|
createDialog FlowMapperUI width:250 style:#(#style_titlebar, #style_minimizebox, #style_sysmenu)
|
|
|