[Closed] Alternative to createFace for editable Poly and Edit Poly modifier?
Does anyone know of a fast way to create multiple faces at once from existing verts in an Editable Poly and/or Edit Poly modifier? The Maxscript createFace function is absurdly slow on dense models, if you want to create more then 1 or 2 faces at a time.
Late response, but wanted to give an update.
I tried polyop.createPolygon and found it wasn’t any faster. So instead wrote a function for generating faces using the mesh “setface” command, and for the most part it does what I want. The problem is the faces created by setface often have flipped normals compared to their adjacent faces, and so far I haven’t been able to figure out why. I can detect and fix the flipped faces afterwards, but getting the needed position and normal data kills performance.
Anyone know how to prevent setface from creating flipped face normals?
You need to set the correct vertex order.
(
delete objects
mdf = edit_normals displaylength:100
---------------------------------------------
tmesh = mesh numverts:3 numfaces:1
setvert tmesh 1 [ 0, 0,0]
setvert tmesh 2 [100, 0,0]
setvert tmesh 3 [ 0,100,0]
setface tmesh 1 [1,2,3]
for j = 1 to 3 do setedgevis tmesh 1 j true
addmodifier tmesh mdf
---------------------------------------------
tmesh = mesh numverts:3 numfaces:1
setvert tmesh 1 [120, 0,0]
setvert tmesh 2 [220, 0,0]
setvert tmesh 3 [120,100,0]
setface tmesh 1 [1,3,2]
for j = 1 to 3 do setedgevis tmesh 1 j true
addmodifier tmesh mdf
---------------------------------------------
max select all
max modify mode
)
I see. I was hoping their was a way to have it directly inherit the flipped direction based on a connected face, but of course nothings ever that easy. Well, here are the functions I’m using right now, maybe someone can recommend some improvements since I’m running out of ideas how to improve performance further:
fn dotFixed V1 V2 = (
--Fixes a rounding error in maxs dot product (if ignored can result in acos producting a "-1.#IND" value).
d = dot V1 V2
case of (
(d < -1.0):( d = -1.0)
(d > 1.0):( d = 1.0)
default:()
)
d --return
)
fn compareFloats f1 f2 = (
f1 == f2 OR abs (f1 - f2) <= (1e-4)
)
fn triangleIsFlipped midPnts outerPnts faceNormals = (
/*
Function takes info from two adjacent triangles and determines if one of them faces is flipped compared to the other.
To do this, first it removes any skewing (makes sure outerPnts are each perpendicular to midPnts line), then checks if they are pointing in the same or opposite direction...
...if they're pointing the same direction the face normals angle should be 90-180 degrees, else if pointing away then 0-90. If that's not the case then the face is flipped.
Arguments;
midPnts is an array of the two vert positions shared between both triangles. order doesn't matter.
outerPnts is an array of the two vert positions that are NOT shared by the triangles. values must be in same order as faceNormals.
faceNormals is an array of the two faces normals (one per face). values must be in same order as outerPnts.
*/
----Get normalized un-skewed triangle directions----
--mid pole
mpC = (midPnts[1] + midPnts[2]) / 2
mpDir = normalize(midPnts[2] - midPnts[1])
--triangle 1
t1Dir = normalize(outerPnts[1] - mpC)
t1DirFixed = normalize(cross mpDir (cross mpDir t1Dir))
if (distance t1DirFixed t1Dir) > (distance (t1DirFixed * -1) t1Dir) do t1DirFixed *= -1
--triangle 2
t2Dir = normalize(outerPnts[2] - mpC)
t2DirFixed = normalize(cross mpDir (cross mpDir t2Dir))
if (distance t2DirFixed t2Dir) > (distance (t2DirFixed * -1) t2Dir) do t2DirFixed *= -1
----
--Get angles
nrmAng = acos(dotFixed faceNormals[1] faceNormals[2])
dirDot = dot t2DirFixed t1DirFixed
--Check if face normals pointing correct direction
faceIsFlipped = false
if compareFloats dirDot 0.0 then (
if (dot faceNormals[1] t2DirFixed) < 0 do faceIsFlipped = true
)
else if dirDot > 0 then ( --close
if nrmAng < 90.0 do faceIsFlipped = true
)
else ( --far
if nrmAng > 90.0 do faceIsFlipped = true
)
faceIsFlipped --return
)
fn createFacesFromVertGroups theClass theObj theMod vertGroups = (
/*
vertGroups contains one sub-array of vert indexs per triangle or quad to be created (in clockwise/counterclockwise order).
New triangle normals are flipped based on the adjacent triangle which shares its first and last vert (for performance reasons other sides not checked).
Only supports triangles and quads atm.
May have issues with instanced edit_poly modifiers (if they have different showendresult states).
*/
--Turn off end result so we can get trimesh without modifiers changes.
serStart = showendresult
showendresult = false --note this does nothing if multiple objects with different showendresult states selected
--Convert vert index arrays into point3 values, and if quads get invisible edge indexs.
triangleVrtGrps = #()
invisEdjIdxs = #()
for curGrp in vertGroups do (
if curGrp.count == 3 then (
append triangleVrtGrps [curGrp[1], curGrp[2], curGrp[3]]
append invisEdjIdxs #(true , true , true)
)
else if curGrp.count == 4 do (
append triangleVrtGrps [curGrp[1], curGrp[2], curGrp[4]]
append invisEdjIdxs #(true , false , true)
append triangleVrtGrps [curGrp[4], curGrp[3], curGrp[2]]
append invisEdjIdxs #(true , true , false)
)
)
extraFaceCount = triangleVrtGrps.count
--Get mesh data
theMesh = snapshotasmesh theObj
oldFaceCount = theMesh.numfaces
newFaceCount = oldFaceCount + extraFaceCount
allFacsBIT = #{1..oldFaceCount}
--Create expanded editable_mesh, and append existing mesh data to it.
tmpEMesh = Editable_mesh()
tmpEMesh.numfaces = newFaceCount
attach tmpEMesh theMesh
--Create new faces on temporary editable_mesh
for i=1 to extraFaceCount do ( setface tmpEMesh (oldFaceCount + i) triangleVrtGrps[i] )
update tmpEMesh
--Update edge visibility (must be done here, before flipping faces, else edge order will be different!)
for i=1 to extraFaceCount do (
facIdx = oldFaceCount + i
edjVis = invisEdjIdxs[i]
setEdgeVis tmpEMesh facIdx 1 edjVis[1]
setEdgeVis tmpEMesh facIdx 2 edjVis[2]
setEdgeVis tmpEMesh facIdx 3 edjVis[3]
)
--For each new face that has an adjacent triangle (on edge between first/last verts), check for and fix if flipped.
--This is slooooow, but haven't found a better way to do it yet unfortunately.
flippedFacs = #{}
gfuv = meshop.getFacesUsingVert
gvuf = meshop.getVertsUsingFace
gvp = meshop.getVert
for i=1 to extraFaceCount do (
facIdx = oldFaceCount + i
vrtAFacesBIT = gfuv tmpEMesh #{triangleVrtGrps[i][1]}
vrtBFacesBIT = gfuv tmpEMesh #{triangleVrtGrps[i][3]}
sharedFac = 0 ; for fac in ((vrtAFacesBIT * vrtBFacesBIT) - #{facIdx}) while (sharedFac=fac;false) do ()
if sharedFac != 0 do (
if flippedFacs[sharedFac] then ()--(append flippedFacs facIdx)
else (
midVrts = #(triangleVrtGrps[i][1],triangleVrtGrps[i][3])
midPnts = for vrt in midVrts collect (gvp tmpEMesh vrt)
f1Vrt = 0 ; for vrt in ((gvuf tmpEMesh #{facIdx}) - #{triangleVrtGrps[i][1],triangleVrtGrps[i][3]}) while (f1Vrt=vrt;false) do ()
f2Vrt = 0 ; for vrt in ((gvuf tmpEMesh #{sharedFac}) - #{triangleVrtGrps[i][1],triangleVrtGrps[i][3]}) while (f2Vrt=vrt;false) do ()
outerVrts = #(f1Vrt,f2Vrt)
outerPnts = for vrt in outerVrts collect (gvp tmpEMesh vrt)
faceNormals = #((getFaceNormal tmpEMesh facIdx), (getFaceNormal tmpEMesh sharedFac))
isFlipped = triangleIsFlipped midPnts outerPnts faceNormals
if isFlipped do (append flippedFacs facIdx)
)
)
)
if flippedFacs.numberset > 0 do (meshop.flipNormals tmpEMesh flippedFacs)
--Attach temp mesh to original obj
if theClass == Edit_Poly then (
totalFaces = theMod.GetNumFaces node:theObj
allFacsBIT = #{1..totalFaces}
allFacsBIT.count = totalFaces --setSlection requires the bitarray to be modified like this beforehand to work!
theMod.SetSelection #Face #{} node:theObj
EditPolyMod.setSelection theMod #Face allFacsBIT node:theObj
theMod.buttonop #deleteface
theMod.attach tmpEMesh
)
else if theClass == Editable_Poly then (
polyop.deleteFaces theObj #{1..(polyop.getNumFaces theObj)}
polyop.deleteVerts theObj #{1..(polyop.getNumVerts theObj)}
polyop.attach theObj tmpEMesh
)
else if theClass == Editable_Mesh do (
meshop.deleteFaces theObj #{1..oldFaceCount}
attach theObj tmpEMesh
update theObj
)
--restore end result setting
showendresult = serStart
--no return...
)
It is hard to guess how you implement the code in a real situation.
Could you provide a fully working test, for example using a teapot, in order to understand what you are trying to achieve?
teapot segments:64