585 lines
17 KiB
Plaintext
Executable File
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"
|
|
)
|
|
) |