[Closed] Flippin Flipper
So I recently came across some cool tools for making flipping animation and from those tools I’ve become inspired to make ones myself and not only that but make it in a collaborative environment with the community. In doing this I’d love to get everyone’s ideas and improvements along the way so we can make a solid and free tool for everyone to use and play with. My inspiration came from http://www.scriptspot.com/3ds-max/scripts/flaps and this http://vimeo.com/49746483 . The goal is to take these tools and not only recreate them but vastly improve upon them with new implemented ideas. Anyone is more than welcome to jump in and supply ideas, improvements upon scripts, code snippets, or anything else that would help improve this tool.
So lets begin!..
First thing I’d like to do is narrow down the super basic methodology of how this operates.
My initial thought was that he was exploding the object’s polygon’s into individual objects…on second thought I do believe he is just doing all the animation within the editable poly object. He appears to be however ‘splitting’ all edges. From there its going polygon by polygon rotating each one from it’s edge.
So the first step is getting something basic like this to work and then we will add all the fun gadgets and controls.
Part one: Edge splitting.
clearlistener()
curSel = selection as array
for i = 1 to curSel.count do (
obj = curSel[i]
edgeCount = polyop.getNumEdges obj
edgeList = (for s = 1 to edgeCount collect s)
polyop.splitEdges obj edgeList
)
clearlistener()
/*------------------Snapshot/copy the original mesh and collapse to editable poly---------------*/
curSel = for o in (getCurrentSelection()) where superclassof o == GeometryClass collect (
convertTo (snapshot o) editable_poly
)
/*------------------Split all polygons into seperate elements for each object---------------*/
for i = 1 to curSel.count do (
obj = curSel[i]
edgeCount = polyop.getNumEdges obj
edgeList = (for s = 1 to edgeCount collect s)
polyop.splitEdges obj edgeList
)
curSel.wirecolor = yellow
select curSel
it might help:
fn sharedEdge node f1 f2 =
(
ee = (polyop.getedgesusingface node f1)*(polyop.getedgesusingface node f2)
(ee as array)[1]
)
fn setPivotRotation obj point =
(
worldAlignPivot obj
local rot = inverse point.rotationpart
animate off in coordsys local obj.rotation *= rot
obj.objectoffsetrot *= rot
obj.objectoffsetpos *= rot
obj.objecttransform
)
fn getFlipTM node face edge flipnode: =
(
vv = polyop.getedgeverts node edge
normal = polyop.getfacenormal node face
center = (polyop.getvert node vv[1] + polyop.getvert node vv[2])/2
side = normalize (polyop.getvert node vv[2] - polyop.getvert node vv[1])
front = normalize (cross side normal)
tm = matrix3 front side normal center
if isvalidnode flipnode do
(
setPivotRotation flipnode tm
flipnode.pivot = center
)
tm
)
fn getFaceGrowing node = if iskindof node Editable_Poly do
(
fn getNeighbours node face exclude:#{} shareEdges:on =
(
ee = polyop.getedgesusingface node face
ff = (polyop.getfacesusingedge node ee) - exclude
if shareEdges do
(
for f in ff where (ee*(polyop.getedgesusingface node f)).isempty do ff[f] = off
)
ff
)
faces = #()
done = #{}
ff = node.selectedfaces as bitarray
done = copy ff
group_id = 0
while not ff.isempty do
(
old = copy done
group_id += 1
for f in ff do
(
neighbours = getNeighbours node f exclude:done
nn = #(f, neighbours, group_id)
--format "%
" nn
join done neighbours
append faces nn
)
ff = done - old
)
faces
)
delete objects
n = 5
p = converttopoly (plane widthsegs:n lengthsegs:n isseelcted:on)
--seed 0
p.selectedfaces = #{random 1 p.numfaces,random 1 p.numfaces,random 1 p.numfaces}
flips = getFaceGrowing p
s = 8
tt = [0,s]
by_group = off
for ff in flips do
(
for f in ff[2] do
(
t = if by_group then (tt + ff[3]*s) else (tt += ff[3]*s)
e = sharedEdge p ff[1] f
polyop.detachFaces p f name:("f" + formattedprint f format:"03d") delete:off asNode:on
n = objects[objects.count]
tm = getFlipTM p f e flipnode:n
addnewkey n.rotation.controller t[1]
addnewkey n.rotation.controller t[2]
n.rotation.controller[2].keys[1].value = if dot (cross (polyop.getfacecenter p f - polyop.getfacecenter p ff[1]) tm[3]) tm[2] > 0 then 180 else -180
n.rotation.controller[2].keys[2].value = 0
n.visibility = Boolean_Float()
addnewkey n.visibility.controller (t[1]-1)
addnewkey n.visibility.controller t[1]
n.visibility.controller.keys[1].value = 0
n.visibility.controller.keys[2].value = 1
)
)
polyop.deletefaces p (p.faces as bitarray - p.selectedfaces as bitarray)
update p
i didn’t do any optimization. (!!!)
Hey Denis,
Thanks for the huge contribution of nearly the entire basic foundation to the script. Great work, rather really quick turn around.
So the optimization begins.
Bugs to fix:
- Running this on a teapot we notice the rotation of each poly can be incorrect and doesn’t appear to line up.
it’s not a bug. the current code just doesn’t support not flat geometry. i always rotate on -180(or 180) deg. which is correct for flat geometry only.
I understand what your saying, I guess I meant the ‘bug’ as something to aware of that’s all. And something which needs attention.
Returns the angle between two objects who are made up of only 1 polygon each.
fn getAngleBetweenFaces objA objB =
(
faceA = polyop.getFaceNormal objA 1
faceB = polyop.getFaceNormal objB 1
theAngle = acos(dot (normalize faceA) (normalize faceB))
if theAngle < 0 then (
theAngle = 360 - theAngle
)
normAngle = 180 - theAngle
return normAngle
)
getAngleBetweenFaces selection[1] selection[2]
here is not flat version:
fn sharedEdge node f1 f2 =
(
ee = (polyop.getedgesusingface node f1)*(polyop.getedgesusingface node f2)
(ee as array)[1]
)
fn setPivotRotation obj point =
(
worldAlignPivot obj
local rot = inverse point.rotationpart
animate off in coordsys local obj.rotation *= rot
obj.objectoffsetrot *= rot
obj.objectoffsetpos *= rot
obj.objecttransform
)
fn getFlipTM node face edge flipnode: =
(
vv = polyop.getedgeverts node edge
normal = polyop.getfacenormal node face
center = (polyop.getvert node vv[1] + polyop.getvert node vv[2])/2
side = normalize (polyop.getvert node vv[2] - polyop.getvert node vv[1])
front = normalize (cross side normal)
tm = matrix3 front side normal center
if isvalidnode flipnode do
(
flipnode.pivot = center
setPivotRotation flipnode tm
)
tm
)
fn getFaceGrowing node = if iskindof node Editable_Poly do
(
fn getNeighbours node face exclude:#{} shareEdges:on =
(
ee = polyop.getedgesusingface node face
ff = (polyop.getfacesusingedge node ee) - exclude
if shareEdges do
(
for f in ff where (ee*(polyop.getedgesusingface node f)).isempty do ff[f] = off
)
ff
)
faces = #()
done = #{}
ff = node.selectedfaces as bitarray
done = copy ff
group_id = 0
while not ff.isempty do
(
old = copy done
group_id += 1
for f in ff do
(
neighbours = getNeighbours node f exclude:done
nn = #(f, neighbours, group_id)
--format "%
" nn
join done neighbours
append faces nn
)
ff = done - old
)
faces
)
fn getAngleBetween up1 up2 side =
(
ang = acos (dot up1 up2)
act = (dot (cross up1 side) (cross up2 side)) <= 0
if not act then 180-ang else 360-ang
)
delete objects
n = 20
--p = converttopoly (plane widthsegs:n lengthsegs:n isselected:on)
--p = converttopoly (teapot handle:off lid:off spout:off isselected:on)
p = converttopoly (geosphere segments:2 isselected:on)
--p = converttopoly (box widthsegs:n lengthsegs:n heightsegs:n isselected:on)
seed 0
--fetchmaxfile quiet:on
max select all
p = $
p.selectedfaces = #{random 1 p.numfaces,random 1 p.numfaces,random 1 p.numfaces}
--p.selectedfaces = #{random 1 p.numfaces}
flips = getFaceGrowing p
s = 10
tt = [0,s]
by_group = off
max create mode
slidertime = 0
nodes = #()
for ff in flips do
(
for f in ff[2] do
(
t = if by_group then (tt + ff[3]*s) else (tt += ff[3]*s)
e = sharedEdge p ff[1] f
polyop.detachFaces p f name:("f" + formattedprint f format:"03d") delete:off asNode:on
n = objects[objects.count]
--n.wirecolor = random white black
ptm = getFlipTM p ff[1] e
tm = getFlipTM p f e flipnode:n
c = n.rotation.controller = Rotation_List()
a = c.available.controller = Euler_XYZ()
c.active = c.count
addnewkey a[2].controller t[1]
addnewkey a[2].controller t[2]
ang = getAngleBetween ptm[3] tm[3] tm[2]
act = dot (cross (polyop.getfacecenter p f - polyop.getfacecenter p ff[1]) tm[3]) tm[2] < 0
ang = if act then ang-360 else 360-ang
a[2].controller.keys[1].value = ang
n.visibility = Boolean_Float()
v = n.visibility.controller
addnewkey v (t[1]-1)
addnewkey v t[1]
v.keys[1].value = 0
v.keys[2].value = 1
)
)
polyop.deletefaces p (p.faces as bitarray - p.selectedfaces as bitarray)
update p
Great.
These are the controls I’m looking to add. What other ideas do you guys have for controls…
Controls:
Supports a multiple selection of objects
If no edge selection is made then randomly select starting edges.
- Option for X-number of random starting edges if user doesn’t want to select any
Time Settings:
-
Start/End frame
-
Starting delay (the time ‘paused’ between each starting flipper)
0.0 = means all starting flippers begin at the same time
1.0 = means flipper(2) starts 1 frame after flipper(1) & flipper(3) starts 1 frame after flipper(2)… -
Flipping durtion (180 degree based) Larger angles = longer time, Shorter Angles = shorter time
-
Flipping Offset (the time between each flipped polygon) only useable if not using Start/End frame
0.0 = means next flip starts as soon as the previous ends
1.0 = means next flip starts 1 frame after the previous one ends
-1.0 = means the next flip starts 1 frame before the previous one ends -
Age (time in which the polygon remains visible after completing its rotation)
Flipping Options:
- UseFaceAngle
- Offset = subtracts from the original face rotation angle. Results in having less of a rotation than it originally did. (Only available when using faceAngle)
- Set Rotation Angle = a set angle in which the all faces will rotate no matter what.
- Pivot (center or edge)
- Scaling option going from 0.0% at its starting rotation and then 100% at it’s end rotation with ease in curves.