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

585 lines
17 KiB
Plaintext
Executable File

global gExportString = stringstream ""
global gOptimisedMesh = undefined
filein "pipeline/util/scene.ms"
fn RsIsVertOnPolyEdge vMiddleIndex obj connectedVerts:undefined &hasNoSurface:undefined =
(
-- set edge selection to visible edges
setedgeselection obj.mesh #{}
local edgeSelSet=#()
for face = 1 to obj.mesh.numfaces do -- Go through all faces
for edgei = 1 to 3 do -- And for every of the 3 edges
if (getedgevis obj.mesh face edgei) do
(
append edgeSelSet (((face-1)*3)+edgei)
)
setEdgeSelection (obj.mesh) edgeSelSet
local edges = meshop.getEdgesUsingVert obj vMiddleIndex
local selEdges = getEdgeSelection obj.mesh
edges = edges * selEdges
local allVerts = meshop.getVertsUsingEdge obj edges
local vertArray = allverts as array
-- print vertArray
-- print vMiddleIndex
if undefined!=connectedVerts then
for v in vertArray where v!=vMiddleIndex do append connectedVerts v
-- print ("num edges: "+edges as string)
if edges.numberSet >2 or edges.numberSet <2 then
(
return true
)
local vertDataArray = for v in vertArray where v!=vMiddleIndex collect getVert obj.mesh v
if vertDataArray.count<2 then
(
return true
)
local vMiddleData = getvert obj.mesh vMiddleIndex
-- print vertDataArray
local vec1 = vertDataArray[1] - vMiddleData
vec1 = normalize vec1
local vec2 = vertDataArray[2] - vMiddleData
vec2 = normalize vec2
if undefined!=hasNoSurface then
(
print ("cross prod length:"+(length (cross vec1 vec2)) as string)
hasNoSurface = (length (cross vec1 vec2)) < 0.1
)
-- print (vec1 as string +", "+ vec2 as string +" dot product:"+(dot vec1 vec2) as string)
return (dot vec1 vec2) > -0.95
)
fn RsSnapVert v gridSize dirx:0 diry:0 =
(
local halfTileSize = gridSize/2
local modX = mod v.x gridSize
v.x = if modX>halfTileSize or dirx>0 then (v.x+(gridSize-modX)) else (v.x-modX)
local modY = mod v.y gridSize
v.y = if modY>halfTileSize or diry>0 then (v.y+(gridSize-modY)) else (v.y-modY)
)
-- $RsWaterHelper002.optimise()
plugin Helper RsWaterHelper
extends:dummy
name:"RSWaterMesh"
classID:#(0x32ab796e, 0x4322237a)
category:"Gta Helpers"
(
local WATER_TILE_SIZE = 4
parameters main rollout:ParticleParams
(
meshObj type:#maxObject
)
struct sTreeNode(size, children)
fn getQuadTree =
(
refsArray = (refs.dependents this)
local theNode = for i in refsArray where isvalidnode i collect i
if (classof theNode)==Array then theNode = theNode[1]
local bb = nodeGetBoundingBox theNode (matrix3 1)
local startX = bb[1].x
local startY = bb[1].y
local endX = bb[2].x
local endY = bb[2].y
--meshOps.startSlicePlane meshObj.mesh
while startX<endX do
(
meshOp.slice meshObj.mesh meshObj.mesh.faces (point3 1 0 0) startX
startX = startX+WATER_TILE_SIZE
)
while startY<endY do
(
meshOp.slice meshObj.mesh meshObj.mesh.faces (point3 0 1 0) startY
startY = startY+WATER_TILE_SIZE
)
)
fn WalkThroughPolys obj polycallback depth:0 createMesh:false =
(
-- refsArray = (refs.dependents this)
-- local theNode = for i in refsArray where isvalidnode i collect i
-- theNode = theNode[1]
if depth==0 then
progressStart ("Extracting water polygons")
local polysWalked = #()
if createMesh then
local newMesh = createinstance Editable_Mesh
for fi=1 to obj.mesh.numfaces do
(
local polyfaces = meshop.getPolysUsingFace obj.mesh #{fi} threshhold:45.0
-- was poly already processed
local polyAlreadyWalked = false
try (
for p in polysWalked do
(
if (p - polyfaces).isEmpty then
(
polyAlreadyWalked = true
throw "poly already walked"
)
)
append polysWalked polyfaces
) catch()--print (getCurrentException()))
if polyAlreadyWalked then
(
continue
)
-- trigger callback
local optimisedMesh = (polycallback obj polyfaces depth createMesh:createMesh)
if createMesh and undefined!=optimisedMesh then
(
meshop.attach newMesh optimisedMesh
)
if depth==0 then
progressUpdate ((fi as float / obj.mesh.numfaces)*100)
)
if depth==0 then
progressend()
if createMesh then
(
if depth==0 then
gOptimisedMesh.mesh = newMesh.mesh
else
obj.mesh = newMesh.mesh
)
)
fn isAbout a b =
(
a = floor (a*1000)
b = floor (b*1000)
return a==b
)
fn TestIsRectAngle vertDataArray bb:undefined &isTooSmall:undefined =
(
-- if at least
local type = 0
local minTri = (point3 9999 9999 9999)
local maxTri = (point3 -9999 -9999 -9999)
for v in vertDataArray do
(
if minTri.x>v.x then minTri.x = v.x
if minTri.y>v.y then minTri.y = v.y
if maxTri.x<v.x then maxTri.x = v.x
if maxTri.y<v.y then maxTri.y = v.y
-- print v as string
)
if bb!=undefined then
(
append bb minTri
append bb maxTri
)
local delta = maxTri-minTri
-- print ("minTri:"+minTri as string)
-- print ("maxTri:"+maxTri as string)
-- print ("delta:"+delta as string)
if delta.x<WATER_TILE_SIZE or delta.y<WATER_TILE_SIZE then
(
-- print vertDataArray
-- print "==> too small to cut"
if undefined!=isTooSmall then
isTooSmall = true
)
local maxXFound = for v in vertDataArray where (isAbout v.x maxTri.x) collect v
-- print ("maxXFound:"+maxXFound.count as string)
local minXFound = for v in vertDataArray where (isAbout v.x minTri.x) collect v
-- print ("minXFound:"+minXFound.count as string)
local maxYFound = for v in vertDataArray where (isAbout v.y maxTri.y) collect v
-- print ("maxYFound:"+maxYFound.count as string)
local minYFound = for v in vertDataArray where (isAbout v.y minTri.y) collect v
-- print ("minYFound:"+minYFound.count as string)
-- print "*********"
local colour = (color 0 0 0)
if minXFound.count==2 and minYFound.count==2 and maxXFound.count!=2 and maxYFound.count!=2 then (type =2; colour = (color 255 0 0))
else if minXFound.count==2 and maxYFound.count==2 and maxXFound.count!=2 and minYFound.count!=2 then (type =3; colour = (color 0 255 0))
else if maxXFound.count==2 and maxYFound.count==2 and minXFound.count!=2 and minYFound.count!=2 then (type =4; colour = (color 0 0 255))
else if maxXFound.count==2 and minYFound.count==2 and minXFound.count!=2 and maxYFound.count!=2 then (type =5; colour = (color 255 0 255))
else if maxXFound.count==2 and minYFound.count==2 and minXFound.count==2 and maxYFound.count==2 then (type =1; colour = (color 255 255 255))
-- print type
return type
)
fn SnapVertsToClosestGridPoint obj maximise:false =
(
local objBB = #()
local allVerts = for i=1 to getnumVerts obj.mesh collect (getVert obj.mesh i)
TestIsRectAngle allVerts bb:objBB
local bbmin = objBB[1]
local bbmax = objBB[2]
local objCenter = (bbmax + bbmin) / 2
for vi=1 to obj.mesh.numverts do
(
local currVert = getvert obj.mesh vi
local dirx=0
local diry=0
if maximise then
(
if currVert.x>objCenter.x then
(
currVert.x = bbmax.x
dirx = 1
)
else
(
currVert.x = bbmin.x
dirx = -1
)
if currVert.y>objCenter.y then
(
currVert.y = bbmax.y
diry = 1
)
else
(
currVert.y = bbmin.y
diry = -1
)
)
RsSnapVert currVert WATER_TILE_SIZE -- dirx:dirx diry:diry
setvert obj.mesh vi currVert
)
)
fn OptimiseFaceRec obj recurseFunction depth createMesh:false =
(
-- is valid poly
-- 1) go thruogh verts and optimise away ones not on corners
-- 2) snap verts to closest grid to make closest to a valid face - inconsitencies come from subdivision cutting on sloped adges
-- 3) subdivide if still not valid
local allVertsOnPolyEdge = false
local allVerts = #()
local catcher = 1
allVerts = for i=1 to getnumVerts obj.mesh collect (getVert obj.mesh i)
while false==allVertsOnPolyEdge and catcher<10 do
(
allVerts = for i=1 to getnumVerts obj.mesh collect (getVert obj.mesh i)
-- print allVerts
-- print "******"
try
(
for vi=1 to allVerts.count do
(
local connectedVerts = #()
if not RsIsVertOnPolyEdge vi obj connectedVerts:connectedVerts then
(
-- for channels = for i=1 to 1 do collect meshop.getMapVert obj i formerVert
local formerVert = connectedVerts[1]
-- print ("formerVert:"+formerVert as string)
meshop.weldVertSet obj #{vi,formerVert} weldpoint:allVerts[formerVert]
--deleteVert obj.mesh vi
throw "welded vertex - vertcount out of sync."
-- for ci=1 to channels.count do
-- (
-- meshop.setMapVert obj i formerVert
-- )
)
)
-- print "all fine - go ahead"
allVertsOnPolyEdge = true
)catch(
-- print (getCurrentException())
)
catcher = catcher+1
)
-- subdivide if still not valid
local tooSmall = false
local type = TestIsRectAngle allVerts isTooSmall:&tooSmall
local doSubdivide = false
if allVerts.count>4 then
(
doSubdivide = true
)
else if allVerts.count>2 and (type == 0) and not tooSmall then
(
doSubdivide = true
)
else if allVerts.count<=2 then
(
print ("poly with less than 3 verts?!:"+allVerts.count as string)
return false
)
-- snap to closest grid point
if (not doSubdivide) or tooSmall then
SnapVertsToClosestGridPoint obj maximise:true
if doSubdivide then
(
local oldNumVerts = obj.mesh.numverts
local startVertIndex = 1
local hasNoSurface = false
while obj.mesh.numverts == oldNumVerts and
allVerts.count>=startVertIndex and
(RsIsVertOnPolyEdge startVertIndex obj hasNoSurface:&hasNoSurface)
do
(
local currVert = allVerts[startVertIndex]
meshOp.slice obj obj.mesh.faces (point3 0 1 0) currVert.y
startVertIndex = startVertIndex + 1
-- print ("obj.mesh.numverts:"+obj.mesh.numverts as string)
)
startVertIndex = 1
while obj.mesh.numverts == oldNumVerts and
allVerts.count>=startVertIndex and
(RsIsVertOnPolyEdge startVertIndex obj hasNoSurface:&hasNoSurface)
do
(
local currVert = allVerts[startVertIndex]
meshOp.slice obj obj.mesh.faces (point3 1 0 0) currVert.x
startVertIndex = startVertIndex + 1
-- print ("obj.mesh.numverts:"+obj.mesh.numverts as string)
)
if obj.mesh.numverts == oldNumVerts then
(
print "couldnt subdivide mesh"
return true
)
else if hasNoSurface then
(
print "poly has no surface!"
return false
)
else if undefined!=recurseFunction and depth<10 then
(
WalkThroughPolys obj recurseFunction depth:(depth+1) createMesh:createMesh
return true
)
return true
)
-- else print "poly is perfect"
return true
)
fn OptimiseFace obj polyfaces depth createMesh:false =
(
-- print (obj as string + " polyfaces to detach :"+polyfaces as string)
local mYMesh = createinstance Editable_Mesh
mYMesh.mesh = meshop.detachFaces obj.mesh polyfaces delete:false asMesh:true
local validMeshProduced = OptimiseFaceRec mYMesh OptimiseFace depth createMesh:createMesh
-- print (depth as string + " was optimised "+wasoptimised as string)
if validMeshProduced then
(
return mYMesh
)
else
(
local newMesh = Editable_Mesh name:"Abandoned geom"
newMesh.mesh = mYMesh.mesh
return undefined
)
)
fn Optimise =
(
gOptimisedMesh = createinstance Editable_Mesh -- name:"newMesh"
WalkThroughPolys meshobj OptimiseFace depth:0 createMesh:true
meshobj.mesh = gOptimisedMesh.mesh
)
fn setMyMesh obj optimiseMesh:false =
(
local copyObj = copy obj
converttomesh copyObj
if "Gta Object"==(getattrclass copyObj) then
(
meshObj = copyObj.mesh
delete copyObj
for vi=1 to getNumVerts meshObj.mesh do
(
local v = (getvert meshObj.mesh vi)
RsSnapVert v WATER_TILE_SIZE
--v.z = if (mod v.z 1)>0.5 then ceil v.z else floor v.z
setvert meshObj.mesh vi v
)
if optimiseMesh then Optimise()
refsArray = (refs.dependents this)
local theNode = for i in refsArray where isvalidnode i collect i
if (classof theNode)==Array then theNode = theNode[1]
theNode.transform = obj.transform
return true
)
return false
)
fn ExtractCallback obj polyfaces depth =
(
local allVerts = meshop.getVertsUsingFace meshObj.mesh polyfaces
local polyVertArray = allVerts as array
local vertDataArray = #()
local mapVertChannel1DataArray = #()
local mapVertChannel2DataArray = #()
for v in polyVertArray do
(
local vd = (getVert meshObj.mesh v)
append vertDataArray vd
local vdc1 = (point3 0 0 0)--(meshop.getMapVert meshObj.mesh 1 v)
append mapVertChannel1DataArray vdc1
local vdc2 = (point3 0 0 0)--(meshop.getMapVert meshObj.mesh 2 v)
append mapVertChannel2DataArray vdc2
)
local isTriangle = polyVertArray.count ==3
local type = 1
if isTriangle then
(
if polyVertArray.count!=3 then
(
messagebox ("vert count in triangle not correct:"+polyVertArray.count as string+" on poly using faces "+polyfaces as string)
setFaceSelection meshObj.mesh polyfaces
continue
)
type = TestIsRectAngle vertDataArray
)
else
(
if polyVertArray.count!=4 then
(
messagebox ("vert count in quad not correct:"+polyVertArray.count as string+" on poly using faces "+polyfaces as string)
setFaceSelection meshObj.mesh polyfaces
continue
)
)
refsArray = (refs.dependents this)
local theNode = for i in refsArray where isvalidnode i collect i
if (classof theNode)==Array then theNode = theNode[1]
-- print ("theNode:"+theNode as string)
isVisible = getattr theNode (getattrindex "Gta Water" "Visible")
isLimitedDepth = getattr theNode (getattrindex "Gta Water" "Limited Depth")
isStandard = getattr theNode (getattrindex "Gta Water" "Standard")
isCalming = getattr theNode (getattrindex "Gta Water" "Calming")
valCalmness = getattr theNode (getattrindex "Gta Water" "Calmness")
outputFlags = 0
-- print ("isVisible:"+isVisible as string)
-- print ("isLimitedDepth:"+isLimitedDepth as string)
-- print ("isStandard:"+isStandard as string)
-- print ("isCalming:"+isCalming as string)
-- print ("valCalmness:"+valCalmness as string)
if isVisible == true then (
outputFlags = outputFlags + 1
)
if isLimitedDepth == true then (
outputFlags = outputFlags + 2
)
if isStandard == true then (
outputFlags = outputFlags + 4
)
if isCalming == true then (
outputFlags = outputFlags + 8
)
for vi=1 to vertDataArray.count do
(
format "% % % % % % % " \
vertDataArray[vi].x vertDataArray[vi].y vertDataArray[vi].z \ -- position
mapVertChannel1DataArray[vi].x mapVertChannel1DataArray[vi].y \ -- flow direction
mapVertChannel2DataArray[vi].x \ -- big wave streength
mapVertChannel2DataArray[vi].y \ -- small wave strength
to:gExportString
)
format " % % %\n" outputFlags valCalmness type to:gExportString
return undefined
)
fn getWaterpolys =
(
gExportString = stringstream ""
WalkThroughPolys meshobj ExtractCallback
return gExportString as string
)
fn getMesh = meshObj
rollout ParticleParams ""
(
pickbutton btnPickMesh "pick new mesh" across:2
checkbox chckOptimise "optimise" offset:[13,5] checked:true
button btnMeshConvert "Extract mesh" width:130
on btnPickMesh picked obj do
(
if undefined==getattrclass or "Gta Object"==(getattrclass obj) then
setMyMesh obj optimiseMesh:(chckOptimise.state)
)
on btnMeshConvert pressed do
(
refsArray = (refs.dependents this)
local theNode = for i in refsArray where isvalidnode i collect i
if (classof theNode)==Array then theNode = theNode[1]
local newMesh = Editable_Mesh name:theNode.name transform:theNode.transform
newMesh.mesh = meshObj.mesh
select newMesh
--delete theNode
)
)
--fn getDisplayMesh =
on getDisplayMesh do
(
--print ("forceRebuild:"+gForceParticleHelperRebuild as string)
if undefined==meshobj then
(
local newMesh = createinstance plane
newMesh.width=1
newMesh.length=1
newMesh.lengthsegs=1
newMesh.widthsegs=1
meshobj = newMesh
)
return meshObj.mesh
)
tool create
(
on mousePoint click do
(
if undefined!=RsMouseclickBoundsIntersect then
(
in coordsys screen objs = RsMouseclickBoundsIntersect viewPoint:viewPoint
for o in objs do
(
if not o.isHiddenInVpt then
(
print ("found:"+o as string)
setMyMesh o optimiseMesh:(ParticleParams.chckOptimise.state)
return #stop
)
)
messagebox "No node was hit, creatin at origin. Please link to object via according button."
)
return false
)
on mouseAbort clickno do print "aborted"
on end do print "Ending"
)
)