[Closed] CgTalk Maxscript Challenge 003: "Foot Prints"
CgTalk Maxscript Challenge 003: “Foot Prints”
DESCRIPTION: Set up an object that leaves a foot print every time it touches another surface. This can be a texture footprint or an object.
If you have time, set up a simple scene that shows a person walking around making footprints.
INTERMEDIATE SCRIPTERS: Make the footprint actually embed into the surface.
ADVANCED SCRIPTERS: Give the surface two different properties; mud and concrete. Embed into the mud, and leave surface footprints on the concrete. (For extras, make the surface footprint the color of whatever the mud is!)
RULES:
[ul]
[li]You can set up scenes and textures for this one as you see fit. If you can code it all, even better![/li][li]Code from scratch. Try not to use pre-exisitng functions or plugins.[/li][li]Show your script references, if any (eg. Looking at another script to assist you).[/li][li]You are encouraged to ask for help where needed, but try to do it on your own. The maxscript reference is an invaluable resource.[/li][li]Post your final script inside [/li]“`
tags (located on your posting toolbar).
[li]Post all code into THIS thread.[/li][li]NEW RULE: Post the max version you coded in, plus any maxscript extensions you used. (Thanks galagast!)[/li][li]Try to finish the challenge in a week. There is no definite time limit. [/li][li]Voting will occur at the end of each ‘month’ (every 4 challenges). [/li][/ul]NOTES: I think this one will be slightly harder than the last two. Please remember to give feedback as to the ease or difficulty of the challenges. Think about using different methods, such as deformers or even displacement maps.
Great Challenge, don’t have the time to do something myself, but i’m excited to see what people come up with
is pflow allowed?
There’s definitely going to be some scripting involved in the pflow part of it. The only other way I can think of to do this is using script controllers. It’ll be interesting to see the different solutions to this one.
Sure, it’s the first rule of this weeks challenge:
You can set up scenes and textures for this one as you see fit. If you can code it all, even better!
Remember, the challenge is here to make you think creatively about tackling problems!
You should try and complete the challenge in a week (I put up a new challenge every monday), but there isn’t really a time limit as such. We’ll be voting on the best scripts every month.
Okay, I’ve got something here that sort of works, but not nearly as good as I would like. There’s a couple issues with scripting pFlow that I’m trying to work out.
So here we go:
Open Max and run the script. All objects, animation, and pFlow system are created from the script. When you see the particle view window pop up, you need to manually delete the connection (if there is one) from PF_Source 01 to Event01, and then reconnect it. For some reason, it doesn’t always connect correctly when done from a script. Also, you can delete the extra floating events that aren’t connected to anything.
From there, just play the animation. There’s another issue that you’ll notice after the animation plays, and is that the mesh isn’t getting pushed down consistently under the boxes. The collision test seems to randomly ignore some particles.
I’ll keep working on it though and post updates as I complete them. The next step is to try and get the area around the footsteps to push up as though sand were getting displaced by the feet.
-- start with a fresh scene
resetMaxFile #noPrompt
-- maximize the viewport
actionMan.executeAction 0 "50026"
-- turn off real time playback
realTimePlayback = false
-- create the template object, which is a plane with a noise modifier to make it bumpy
templateObj = plane length:150 width:50 pos:[0,85,0] lengthSegs:150 widthSegs:50
-- define the noise modifier
noiseMod = noiseModifier()
-- add it to the template object
addModifier templateObj noiseMod
-- change the settings of the noise modifier
noiseMod.strength = [0,0,1]
noiseMod.scale = 5
-- hide the template object because we don't need to see it
hide templateObj
-- make a new mesh object that will inherit it's mesh from the template object
deformObj = mesh pos:[0,85,0]
-- get the template object's mesh and replace the deform object's mesh with it
deformObj.mesh = templateObj.mesh
-- update the deform object
update deformObj
-- make a couple boxes to use as feet
b1 = box length:10 width:5 height:3 pos:[7,-20,-1]
b2 = box length:10 width:5 height:3 pos:[-7,0,-1]
-- make a couple deflector to act as the soles of the "feet"
d1 = deflector length:10 width:5 pos:b1.pos
d2 = deflector length:10 width:5 pos:b2.pos
-- link them to the boxes
d1.parent = b1
d2.parent = b2
-- animate the feet "walking" forward
for i = 10 to 100 by 20 do (with animate on (at time i (b1.pos += [0,40,0])))
for i = 20 to 100 by 20 do (with animate on (at time i (b1.pos = (at time (i - 10) b1.pos))))
for i = 5 to 100 by 20 do (with animate on (at time i (b1.pos += [0,0,10])))
with animate on (at time 10 (b2.pos = b2.pos))
for i = 20 to 100 by 20 do (with animate on (at time i (b2.pos += [0,40,0])))
for i = 30 to 100 by 20 do (with animate on (at time i (b2.pos = (at time (i - 10) b2.pos))))
for i = 15 to 100 by 20 do (with animate on (at time i (b2.pos += [0,0,10])))
-- define the text for the Birth Script operator
s1 = stringstream ""
format "on ChannelsUsed pCont do (
" to:s1
format " pCont.usePosition = true
" to:s1
format " )
" to:s1
format "
" to:s1
format "on Init pCont do ()
" to:s1
format "
" to:s1
format "on Proceed pCont do (
" to:s1
format " t = pCont.getTimeStart() as float
" to:s1
format " if t < 0 then (
" to:s1
format " obj = $%
" deformObj.name to:s1
format " -- revert the plane's geometry back to it's original state by copying the mesh from the template object
" to:s1
format " obj.mesh = $%.mesh
" templateObj.name to:s1
format " -- get the number of verts in the plane
" to:s1
format " n = getNumVerts obj
" to:s1
format " -- create one particle per vert and place the particle at the vert's position
" to:s1
format " -- also set the particle's age to zero
" to:s1
format " for i = 1 to n do (
" to:s1
format " pCont.addParticle()
" to:s1
format " pCont.particleIndex = i
" to:s1
format " pCont.particleAge = 0
" to:s1
format " pCont.particlePosition = getVert obj i
" to:s1
format " )
" to:s1
format " )
" to:s1
format " )
" to:s1
format "on Release pCont do ()" to:s1
s1 = s1 as string
-- define the text for the "vertsFollowParticles" Script Operator
s2 = stringstream ""
format "on ChannelsUsed pCont do (
" to:s2
format " pCont.usePosition = true
" to:s2
format " )
" to:s2
format "
" to:s2
format "on Init pCont do ()
" to:s2
format "
" to:s2
format "on Proceed pCont do
" to:s2
format "(
" to:s2
format " obj = $%
" deformObj.name to:s2
format " n = pCont.NumParticles()
" to:s2
format " for i in 1 to n do
" to:s2
format " (
" to:s2
format " pCont.particleIndex = i
" to:s2
format " x = pCont.particleID
" to:s2
format " setVert obj x (pCont.getParticlePositionByID x)
" to:s2
format " )
" to:s2
format " update obj
" to:s2
format ")
" to:s2
format "on Release pCont do ()" to:s2
s2 = s2 as string
-- begin editing pFlow
particleFlow.beginEdit()
-- create new particle flow
newPF = PF_Source X_Coord:0 Y_Coord:0 isSelected:on Logo_Size:5 Emitter_Length:10 Emitter_Width:10 Emitter_Height:10 pos:templateObj.center
newPF.quantity_viewport = 100
newPF.particle_amount_limit = 10000000
-- add the Render and Dispaly operators to PF Source 01
newPF.appendAction (renderParticles())
newPF.appendAction (displayParticles type:1)
-- turn off display of particles and PF object
newPF.show_logo = off
newPF.show_emitter = off
newPF.baseObject.activateAction 2 0
-- hook Event 01 to PF Source 01
newPF.appendInitialActionList event01
-- create Event01
event01 = event()
-- add the Birth Script operator and add the script
event01.appendAction (Birth_Script Proceed_Script:s1)
-- add a collision test and change some settings
co = collision()
event01.appendAction co
co.Collision_Nodes = #(d1,d2)
co.Speed_Option = 1
-- get PF Source position in particle view window
newPF_X = newPF_Y = 0
newPF.getPViewLocation &newPF_X &newPF_Y
-- reposition Event 01
event01.setPViewLocation newPF_X (newPF_Y + 100)
-- create Event02
event02 = event()
event02.setPViewLocation newPF_X (newPF_Y + 200)
-- hook Event 02 to the collision test
co.setNextActionList event02 co
-- add the script operator that will deform the mesh using the particles
event02.appendAction (Script_Operator proceed_script:s2 name:"vertsFollowParticles")
-- add the two feet script operators
for i = 1 to 2 do (
if i == 1 then objName = d1.name
if i == 2 then objName = d2.name
local s = stringStream ""
format "on ChannelsUsed pCont do (pCont.usePosition = true)
" to:s
format "on Init pCont do ()
" to:s
format "on Proceed pCont do (
" to:s
format " obj = $%
" objName to:s
format " n = pCont.NumParticles()
" to:s
format " if obj.pos.z < (at time (sliderTime - 1) obj.pos.z) then (
" to:s
format " for i in 1 to n do (
" to:s
format " pCont.particleIndex = i
" to:s
format " if pCont.isParticleNew i then (
" to:s
format " x = pCont.particleID
" to:s
format " pPos = pCont.getParticlePositionByID x
" to:s
format " pCont.setParticlePositionByID x [pPos.x,pPos.y,obj.pos.z]
" to:s
format " )
" to:s
format " )
" to:s
format " )
" to:s
format " )
" to:s
format "on Release pCont do ()
" to:s
s = s as string
event02.appendAction (Script_Operator proceed_script:s name:("foot0" + i as string))
)
-- end pFlow edit
particleFlow.endEdit()
-- open the Particle View window
particleFlow.openParticleView()
Have fun!
Hey James,
that’s great! well done it works really well. no time for me this week so I’ll be in the next one.
J.
Wow, cool script there James … and very imaginative use of pflow … This challange was a big one (too big in this timeframe for me) so I only got very short way – investigating possiblities and noting a few ideas, but hopefully I can join in some time in the future.
Btw. I found that Peter Watje did a plugin (written in C++) doing something similar to this challange, but in a totally different way that yours:
http://www.max3dstuff.com/max4/objectDeform/help.html
Lars
Thanks for the link. That Peter Watje sure knows what he’s doing.
I’ve run into a bit of a road block trying to make the surface not only go down under the foot, but up around it as well. At this point, I’m thinking maybe a displacement modifier with some sort of procedurally generated bitmap might work out better. But, yeah, this is a big one, but I’ll keep working at it and post any updates here.
Okay, I had a Eureka moment and figured out how to write the pFlow script operator much simpler. It actually works faster and better than before, plus I’ve got my sand effect with the area around the feet raising up as the foot presses down into the ground.
Remember to manually link Even01 to PF_Source01 after running the script. You might even have to delete the link, then relink it. It’s weird.
-- start with a fresh scene
resetMaxFile #noPrompt
-- maximize the viewport
actionMan.executeAction 0 "50026"
-- turn off real time playback
realTimePlayback = false
-- create the template object, which is a plane with a noise modifier to make it bumpy
templateObj = plane length:150 width:50 pos:[0,85,0] lengthSegs:150 widthSegs:50
-- define the noise modifier
noiseMod = noiseModifier()
-- add it to the template object
addModifier templateObj noiseMod
-- change the settings of the noise modifier
noiseMod.strength = [0,0,1]
noiseMod.scale = 5
-- hide the template object because we don't need to see it
hide templateObj
-- make a new mesh object that will inherit it's mesh from the template object
deformObj = mesh pos:[0,85,0]
-- get the template object's mesh and replace the deform object's mesh with it
deformObj.mesh = templateObj.mesh
-- update the deform object
update deformObj
-- make a couple boxes to use as feet
b1 = box length:10 width:5 height:3 pos:[7,-20,-1]
b2 = box length:10 width:5 height:3 pos:[-7,0,-1]
-- make a couple deflector to act as the soles of the "feet"
d1 = deflector length:10 width:5 pos:b1.pos
d2 = deflector length:10 width:5 pos:b2.pos
-- link them to the boxes
d1.parent = b1
d2.parent = b2
-- hide them
hide d1
hide d2
-- animate the feet "walking" forward
for i = 10 to 100 by 20 do (with animate on (at time i (b1.pos += [0,40,0])))
for i = 20 to 100 by 20 do (with animate on (at time i (b1.pos = (at time (i - 10) b1.pos))))
for i = 5 to 100 by 20 do (with animate on (at time i (b1.pos += [0,0,10])))
with animate on (at time 10 (b2.pos = b2.pos))
for i = 20 to 100 by 20 do (with animate on (at time i (b2.pos += [0,40,0])))
for i = 30 to 100 by 20 do (with animate on (at time i (b2.pos = (at time (i - 10) b2.pos))))
for i = 15 to 100 by 20 do (with animate on (at time i (b2.pos += [0,0,10])))
-- define the text for the Birth Script operator
s1 = stringstream ""
format "on ChannelsUsed pCont do (
" to:s1
format " pCont.usePosition = true
" to:s1
format " )
" to:s1
format "on Init pCont do (
" to:s1
format " )
" to:s1
format "on Proceed pCont do (
" to:s1
format " t = pCont.getTimeStart() as float
" to:s1
format " if t < 0 then (
" to:s1
format " obj = $Object01
" to:s1
format " -- revert the plane's geometry back to it's original state by copying the mesh from the template object
" to:s1
format " obj.mesh = $Plane01.mesh
" to:s1
format " -- get the position of each vert
" to:s1
format " global bigArryOVertPositions = for i = 1 to getNumVerts obj collect getVert obj i
" to:s1
format " -- get the number of verts in the plane
" to:s1
format " n = getNumVerts obj
" to:s1
format " -- create one particle per vert and place the particle at the vert's position
" to:s1
format " -- also set the particle's age to zero
" to:s1
format " for i = 1 to n do (
" to:s1
format " pCont.addParticle()
" to:s1
format " pCont.particleIndex = i
" to:s1
format " pCont.particleAge = 0
" to:s1
format " pCont.particlePosition = getVert obj i
" to:s1
format " )
" to:s1
format " )
" to:s1
format " )
" to:s1
format "on Release pCont do ()
" to:s1
s1 = s1 as string
-- define the text for the "footsteps" Script Operator
s2 = stringstream ""
format "on ChannelsUsed pCont do (pCont.usePosition = true)
" to:s2
format "on Init pCont do ()
" to:s2
format "on Proceed pCont do (
" to:s2
format " obj1 = $Deflector01
" to:s2
format " obj2 = $Deflector02
" to:s2
format " defMesh = $Object01
" to:s2
format " n = pCont.NumParticles()
" to:s2
format " vertArr = #{}
" to:s2
format " vertArr2 = #{}
" to:s2
format " if (obj1.pos.z < (at time (sliderTime - 1) obj1.pos.z)) or (obj2.pos.z < (at time (sliderTime - 1) obj2.pos.z)) then (
" to:s2
format " for i in 1 to n do (
" to:s2
format " pCont.particleIndex = i
" to:s2
format " if pCont.isParticleNew i then (
" to:s2
format " vertNum = pCont.particleID
" to:s2
format " append vertArr vertNum
" to:s2
format " ) -- end if pCont.isParticleNew
" to:s2
format " ) -- end for i in 1 to n
" to:s2
format " ) -- end obj position test
" to:s2
format " for v in (vertArr as array) do (
" to:s2
format " bigArryOVertPositions[v].z -= random .5 1
" to:s2
format " )
" to:s2
format " edgeArr = meshop.getEdgesUsingVert defMesh vertArr
" to:s2
format " vertArr2 = (meshop.getVertsUsingEdge defMesh edgeArr)
" to:s2
format " for j = 1 to 4 do (
" to:s2
format " edgeArr2 = (meshop.getEdgesUsingVert defMesh vertArr2)
" to:s2
format " vertArr2 = (meshop.getVertsUsingEdge defMesh edgeArr2)
" to:s2
format " setVertSelection defMesh vertArr2
" to:s2
format " for k in ((vertArr2 - vertArr) as array) do (
" to:s2
format " bigArryOVertPositions[k].z += random .1 .2
" to:s2
format " )
" to:s2
format " ) -- end for j = 1 to 10
" to:s2
format " -- apply new vert positions to mesh
" to:s2
format " for j = 1 to bigArryOVertPositions.count do (
" to:s2
format " setVert defMesh j bigArryOVertPositions[j]
" to:s2
format " )
" to:s2
format " update defMesh
" to:s2
format " ) -- end on Proceed pCont
" to:s2
format "on Release pCont do ()
" to:s2
s2 = s2 as string
-- begin editing pFlow
particleFlow.beginEdit()
-- create new particle flow
newPF = PF_Source X_Coord:0 Y_Coord:0 isSelected:on Logo_Size:5 Emitter_Length:10 Emitter_Width:10 Emitter_Height:10 pos:templateObj.center
newPF.quantity_viewport = 100
newPF.particle_amount_limit = 10000000
-- add the Render and Dispaly operators to PF Source 01
newPF.appendAction (renderParticles())
newPF.appendAction (displayParticles type:1)
-- turn off display of particles and PF object
newPF.show_logo = off
newPF.show_emitter = off
newPF.baseObject.activateAction 2 0
-- hook Event 01 to PF Source 01
newPF.appendInitialActionList event01
-- create Event01
event01 = event()
-- add the Birth Script operator and add the script
event01.appendAction (Birth_Script Proceed_Script:s1)
-- add a collision test and change some settings
co = collision()
event01.appendAction co
co.Collision_Nodes = #(d1,d2)
co.Speed_Option = 1
-- get PF Source position in particle view window
newPF_X = newPF_Y = 0
newPF.getPViewLocation &newPF_X &newPF_Y
-- reposition Event 01
event01.setPViewLocation newPF_X (newPF_Y + 100)
-- create Event02
event02 = event()
event02.setPViewLocation newPF_X (newPF_Y + 200)
-- hook Event 02 to the collision test
co.setNextActionList event02 co
-- add the script operator that will deform the mesh using the particles
event02.appendAction (Script_Operator proceed_script:s2 name:"footsteps")
-- end pFlow edit
particleFlow.endEdit()
-- open the Particle View window
particleFlow.openParticleView()