home *** CD-ROM | disk | FTP | other *** search
/ Maximum 3D 3 / m3d-p3.iso / 3DS_MAX / 3DSMAX.2_0 / SCRIPTS / PRTLGHTS.MS < prev    next >
Text File  |  1997-10-19  |  18KB  |  479 lines

  1. --  light_particle.ms
  2. --                            John Wainwright
  3. --
  4. -- A scripted utility that lets you attach omni-lights to
  5. -- the particles of a selected particle system and animate
  6. -- a brightness decay and color ramp over the particle life.
  7. --
  8.  
  9. utility light_particle "Light Particles" 
  10. (
  11.     local  system, master, particles, pcount,
  12.            start_times, end_times, noise_ctrlrs,
  13.            start_tangent, end_tangent,
  14.            start = animationRange.start.frame,
  15.            end = animationRange.end.frame, last_age
  16.     local  lpp        -- rollout & fn forwards
  17.     
  18.     rollout aboutbox "About Light Particles"
  19.     (
  20.         label lp1 "Light Particle System"
  21.         label lp3 "John Wainwright and"
  22.         label lp4 "Marcus Morgan"
  23.     )
  24.     
  25.     group "Particle System" 
  26.     (
  27.          label pps "Pick master particle system:" align:#left
  28.          pickbutton pick_system "none" width:90         
  29.     )
  30.  
  31.     -- particle creation panel - choose frame range and % coverage
  32.     -- of particle, create btn turns to recreate after initial creation
  33.     -- must recreate particles to change coverage or accomodate psys changes
  34.     group "Particle Creation"
  35.     (
  36.         spinner start_frame "Start frame:" type:#integer range:[start, end, start]
  37.         spinner end_frame "End frame:" type:#integer range:[start, end, end]
  38.         spinner make_percent "Particle coverage %:" fieldwidth:35 type:#integer range:[0,100,10]
  39.         button create_parts "Create Light Particles" offset:[0,5] enabled:false width:127
  40.         label status
  41.         button delete_parts "Delete Particles" enabled:false
  42.     )
  43.         
  44.     fn next_index cur_psi =
  45.     (
  46.         -- computes next selected particle system index based on coverage %
  47.         if make_percent.value == 100 then cur_psi + 1
  48.         else if make_percent.value > 50 then
  49.         (
  50.             -- > 50%, skip every nth
  51.             local skip = ceil (particleCount system / (particleCount system * (100.0 - make_percent.value) / 100))
  52.             if mod (cur_psi - 1) skip == 0 then cur_psi + 2 else cur_psi + 1
  53.         )
  54.         else
  55.             -- <= 50%, pick every nth
  56.             cur_psi + (100 / make_percent.value) as integer            
  57.     )
  58.     
  59.     -- get base keyable light multiplier controller depending on noise presence
  60.     fn get_mult_controller light =
  61.         if lpp.add_noise.checked
  62.             then light.multiplier.controller.bezier_float.controller
  63.             else light.multiplier.controller
  64.  
  65.     -- compute controller keyframe values at start & end frames to
  66.     -- ensure correct decay curve and disable lights outside of active 
  67.     -- frame range
  68.     fn add_interpolated_end_frames light =
  69.     (
  70.             local mc = get_mult_controller light,
  71.               cc = light.color.controller, 
  72.               last_key, new_key, endf_key
  73.         -- interpolate multiplier, place keys at start & end frames
  74.         last_key = getKey mc (numkeys mc)
  75.         new_key = addNewKey mc (last_key.time + system.life)
  76.         new_key.value = lpp.end_mult.value
  77.         new_key.intangenttype = end_tangent
  78.         endf_key = addNewKey mc end_frame.value #interpolate
  79.         endf_key.intangenttype = end_tangent
  80.         endf_key.outtangenttype = #linear
  81.         sortkeys mc
  82.         last_key = getKey mc (numkeys mc)
  83.         last_key.value = 0
  84.         last_key.time = endf_key.time + 1
  85.         last_key.intangenttype = #linear
  86.         -- interpolate color, place keys at start & end frame
  87.         last_key = getKey cc (numkeys cc)
  88.         at time (last_key.time + system.life) animate on light.color = lpp.end_color.color
  89.         sortkeys cc
  90.         new_key = getKey cc (numKeys cc)
  91.         new_key.intangenttype = end_tangent
  92.         endf_key = addNewKey cc end_frame.value #interpolate
  93.         endf_key.intangenttype = end_tangent
  94.         endf_key.outtangenttype = #linear
  95.         sortkeys cc
  96.         last_key = getKey cc (numkeys cc)
  97.         last_key.time = endf_key.time + 1
  98.         last_key.intangenttype = #linear
  99.     )
  100.  
  101.     on pick_system picked obj do 
  102.     ( 
  103.          -- master particle system picked, remember it in a local 
  104.         -- and stick name in picker button label
  105.         system = obj; pick_system.text = obj.name
  106.         create_parts.enabled = true 
  107.     )
  108.  
  109.     on create_parts pressed do
  110.     (
  111.         -- create lights to cover chosen % of particles, store them in an array
  112.  
  113.         start_tangent = if lpp.ldecay.checked then #linear else if lpp.sdecay.checked then #slow else #fast
  114.         end_tangent = if lpp.ldecay.checked then #linear else if lpp.sdecay.checked then #fast else #slow
  115.         
  116.         -- delete any existing light particles & loop over creation
  117.         delete particles
  118.         start_times = #(); end_times = #(); noise_ctrlrs = #()
  119.         pcount = particleCount system * make_percent.value / 100
  120.         particles = for i in 1 to pcount collect
  121.         (
  122.             local light = omnilight name:"plight"                            \
  123.                                     contrast:lpp.contrast.value             \
  124.                                     farattenstart:lpp.far_start.value        \
  125.                                     farattenend:lpp.far_end.value            \
  126.                                     nearattenstart:lpp.near_start.value        \
  127.                                     nearattenend:lpp.near_end.value            \
  128.                                     usenearatten:lpp.use_near.checked        \
  129.                                     shownearatten:lpp.show_near.checked        \
  130.                                     usefaratten:lpp.use_far.checked            \
  131.                                     showfaratten:lpp.show_far.checked        \
  132.                                     castshadows:lpp.cast_shad.checked        \
  133.                                     useglobalshadowsettings:lpp.use_global.checked \
  134.                                     absolutemapbias:lpp.abs_bias.checked    \
  135.                                     mapbias:lpp.map_bias.value                \
  136.                                     mapsize:lpp.map_size.value                \
  137.                                     samplerange:lpp.map_smprange.value        \
  138.                                     raytracebias:lpp.ray_bias.value            \
  139.                                     multiplier: 0                            \
  140.                                     pos:system.pos
  141.                                         
  142.             -- ensure we have color & multiplier controllers with a start-frame key
  143.             if light.color.controller == undefined then
  144.                 light.color.controller = bezier_color()
  145.             addNewKey light.color.controller start_frame.value
  146.             if light.multiplier.controller == undefined then
  147.                 light.multiplier.controller = bezier_float()
  148.             addNewKey light.multiplier.controller start_frame.value
  149.             -- build & optionally add noise controller to multiplier
  150.             local n = noise_float frequency:lpp.noiz_freq.value
  151.             (addNewKey n.noise_strength.controller start_frame.value).value = 0
  152.             (addNewKey n.noise_strength.controller end_frame.value).value = 0
  153.             noise_ctrlrs[i] = n
  154.             if lpp.add_noise.checked then
  155.             (
  156.                 local fl = float_list ()
  157.                 fl.available.controller = bezier_float ()
  158.                 fl.available.controller = n
  159.                 light.multiplier.controller = fl
  160.             )
  161.             -- init life start & end time arrays
  162.             start_times[i] = #(); end_times[i] = #()
  163.             light
  164.         )
  165.         
  166.         -- animate particles        
  167.         animate on for t in start_frame.value to end_frame.value do at time t
  168.         (
  169.             status.text = "Processing frame " + (t as string)
  170.             local psi = 1  -- particle system particle index
  171.             for i in 1 to pcount do
  172.             (
  173.                 -- grab host system particle
  174.                 local pos = particlePos system psi,
  175.                       age = particleAge system psi,
  176.                       light = particles[i]    
  177.                   
  178.                 -- check for birth/rebirth or dying transitions and plant
  179.                 -- color & multiplier keys
  180.                 if last_age[i] != undefined and 
  181.                    (age == undefined or age < last_age[i]) then at time (t-1)
  182.                 (
  183.                     -- death, plant end color, multiplier, noise keys
  184.                     light.color = lpp.end_color.color
  185.                     light.multiplier = lpp.end_mult.value
  186.                     -- condition tangent types from decay choice
  187.                     local mc = get_mult_controller light,
  188.                           cc = light.color.controller,
  189.                           ck = getkey cc (getkeyindex cc currentTime),
  190.                           mk = getkey mc (getkeyindex mc currentTime)
  191.                     ck.intangenttype = mk.intangenttype = end_tangent
  192.                     ck.outtangenttype = mk.outtangenttype = #linear
  193.                     -- add noise key & set tangents
  194.                     local nk = addNewKey noise_ctrlrs[i].noise_strength.controller currentTime
  195.                     nk.value = 0
  196.                     nk.intangenttype =     nk.outtangenttype = #step
  197.                     append end_times[i] currentTime
  198.                 )
  199.                 
  200.                 if age != undefined and
  201.                    (last_age[i] == undefined or age < last_age[i]) then
  202.                 (
  203.                     -- birth or rebirth, plant start color, multiplier, noise keys
  204.                     light.color = lpp.start_color.color
  205.                     light.multiplier = lpp.start_mult.value
  206.                     -- condition tangent types from decay choice
  207.                     local mc = get_mult_controller light,
  208.                           cc = light.color.controller,
  209.                           ck = getkey cc (getkeyindex cc currentTime),
  210.                           mk = getkey mc (getkeyindex mc currentTime)
  211.                     ck.intangenttype =    mk.intangenttype = #linear
  212.                     ck.outtangenttype =    mk.outtangenttype = start_tangent
  213.                     -- add noise key & set tangents
  214.                     local nk = addNewKey noise_ctrlrs[i].noise_strength.controller currentTime
  215.                     nk.value = lpp.noiz_strength.value * lpp.start_mult.value / 100
  216.                     nk.intangenttype =     nk.outtangenttype = #step
  217.                     append start_times[i] currentTime
  218.                 )
  219.                 
  220.                 -- if midlife plant pos key; dead disable light
  221.                 if pos != undefined then
  222.                 (
  223.                     light.pos = pos
  224.                 )
  225.                 else
  226.                 (
  227.                     light.pos = system.pos
  228.                     light.multiplier = 0
  229.                     light.color = color 0 0 0
  230.                 )
  231.                     
  232.                 -- record age and bump to next selected source system particle
  233.                 last_age[i] = age
  234.                 psi = next_index psi 
  235.             )
  236.         )
  237.         
  238.         -- add interpolated keyframes at end frame for still-alive particles
  239.         for i in 1 to pcount where last_age[i] != undefined do
  240.             add_interpolated_end_frames particles[i] 
  241.         
  242.         -- change create button to recreate button
  243.         delete_parts.enabled = true
  244.         create_parts.text = "Recreate Light Particles"
  245.     )
  246.     
  247.     on delete_parts pressed do
  248.     (
  249.         delete particles
  250.         particles = #(); pcount = 0
  251.         create_parts.text = "Create Light Particles"
  252.     )
  253.     
  254.     -- particle light parameter controls
  255.     rollout lpp "Light Particle Parameters"
  256.     (
  257.         -- brightness ramp controls
  258.         group "Multiplier"
  259.         (
  260.             spinner start_mult "Start multiplier:" range:[0, 100, 1] fieldwidth:40 align:#center
  261.             spinner end_mult "End multiplier:" range:[0, 100, 0] fieldwidth:40 align:#center
  262.             label mdl "Multiplier decay:" align:#left
  263.             checkbutton ldecay images:#("btn3.bmp", "btn3m.bmp", 6, 1, 4, 1, 4) width:40 align:#left offset:[8,0] checked:true
  264.             checkbutton fdecay images:#("btn3.bmp", "btn3m.bmp", 6, 2, 5, 2, 5) width:40 align:#center offset:[4,-35]
  265.             checkbutton sdecay images:#("btn3.bmp", "btn3m.bmp", 6, 3, 6, 3, 6) width:40 align:#right offset:[0,-35]
  266.             checkbox add_noise "Add noise"
  267.             spinner noiz_strength "Noise strength %:" type:#integer fieldwidth:40 range:[0, 200, 50] align:#right enabled:false
  268.             spinner noiz_freq "Noise frequency:" fieldwidth:40 range:[0, 10, 0.5] align:#right enabled:false
  269.             spinner contrast "Contrast:" fieldwidth:40 align:#center offset:[0,4]
  270.         )
  271.         -- color ramp controls
  272.         group "Color"
  273.         (
  274.             label cpl "Start color:      End color:"  
  275.             colorpicker start_color across:2 color:(color 255 255 255) offset:[-20,0]
  276.             colorpicker end_color color:(color 255 100 9) offset:[-20,0]
  277.         )
  278.         -- rest of standard life controls - these are not rampable by this tool
  279.         group "Attenuation"
  280.         (
  281.             label al1 "Near             Far"
  282.             spinner near_start "Start:" fieldwidth:47 across:2 offset:[10,0] range:[0,10000,0]
  283.             spinner far_start fieldwidth:47  offset:[3,0] range:[0,10000,80]
  284.             spinner near_end "End:" fieldwidth:47 across:2 offset:[10,0] range:[0,10000,40]
  285.             spinner far_end fieldwidth:47   offset:[3,0] range:[0,10000,200]
  286.             checkbox use_near "Use" across:2 offset:[27,0]
  287.             checkbox use_far "Use" offset:[22,0]
  288.             checkbox show_near "Show" across:2 offset:[27,0]
  289.             checkbox show_far "Show" offset:[22,0]
  290.             label adl "Decay:" align:#left offset:[0,20]
  291.             radiobuttons atten_decay labels:#("None", "Inverse", "Inverse Square") align:#middle offset:[20,-35]
  292.         )
  293.         group "Shadow"
  294.         (
  295.             checkbox cast_shad "Cast Shadows"
  296.             checkbox use_global "Use Global Settings"
  297.             radiobuttons map_ray labels:#("Use Shadow Maps", "Use Ray-traced Shadows") offset:[3,0]
  298.             label mbl "Map Bias    Size    Smp Range"
  299.             spinner map_bias fieldwidth:33 across:3 range:[0,200,4]
  300.             spinner map_size fieldwidth:35 type:#integer range:[0,5000,256] offset:[1,0]
  301.             spinner map_smprange fieldwidth:33 range:[0,20,4]
  302.             checkbox abs_bias "Absolute Map Bias" offset:[1,0] checked:true
  303.             spinner ray_bias "Ray Trace Bias:" fieldwidth:30 align:#center range:[0,20,0.2]
  304.         )
  305.         
  306.         fn get_mult_controller light =
  307.             if add_noise.checked
  308.                 then light.multiplier.controller.bezier_float.controller
  309.                 else light.multiplier.controller
  310.  
  311.         -- if ramps changes, recompute interpolated start & end keyframes
  312.         fn recompute_end_frame_keys i =
  313.             if end_times[i].count > 0 and end_times[i][end_times[i].count] != end_frame.value then
  314.             (
  315.                 -- last end time didn't land on end_frame, so we do have interp'd keys
  316.                 local light = particles[i],
  317.                       mc = get_mult_controller light,
  318.                       cc = light.color.controller
  319.                 -- delete last two keys in mc & cc, recreate interp'd end keys
  320.                 deleteKey mc (numkeys mc); deleteKey mc (numkeys mc)
  321.                 deleteKey cc (numkeys cc); deleteKey cc (numkeys cc)
  322.                 add_interpolated_end_frames light
  323.             )
  324.             
  325.         -- ramp control change handlers
  326.         on start_color changed val do 
  327.             animate on for i in 1 to particles.count do
  328.             (
  329.                 for t in start_times[i] do at time t particles[i].color = val
  330.                 recompute_end_frame_keys i
  331.             )
  332.                 
  333.          on end_color changed val do 
  334.             animate on for i in 1 to particles.count do
  335.             (
  336.                 for t in end_times[i] do at time t particles[i].color = val
  337.                 recompute_end_frame_keys i
  338.             )
  339.                 
  340.         on start_mult changed val do 
  341.             animate on for i in 1 to particles.count do
  342.             (
  343.                 for t in start_times[i] do at time t particles[i].multiplier = val
  344.                 recompute_end_frame_keys i
  345.             )
  346.                 
  347.          on end_mult changed val do 
  348.             animate on for i in 1 to particles.count do
  349.             (
  350.                 for t in end_times[i] do at time t particles[i].multiplier = val
  351.                 recompute_end_frame_keys i
  352.             )
  353.                 
  354.         fn set_decay_tan_types =
  355.             for i in 1 to particles.count do
  356.             (
  357.                 -- for each light, change the tangent types to create selected ramp
  358.                 local light = particles[i],
  359.                       mc = get_mult_controller light,
  360.                       cc = light.color.controller
  361.                 for t in start_times[i] do
  362.                     (getkey cc (getkeyindex cc t)).outtangenttype = 
  363.                         (getkey mc (getkeyindex mc t)).outtangenttype = start_tangent
  364.                 for t in end_times[i] do
  365.                     (getkey cc (getkeyindex cc t)).intangenttype = 
  366.                         (getkey mc (getkeyindex mc t)).intangenttype = end_tangent
  367.                         
  368.                 -- delete & recompute end frame interpolated keys
  369.                 recompute_end_frame_keys i
  370.             )
  371.             
  372.         on ldecay changed state do 
  373.         (
  374.             ldecay.checked = true; fdecay.checked = sdecay.checked = false
  375.             start_tangent = end_tangent = #linear
  376.             set_decay_tan_types ()
  377.         )
  378.         on fdecay changed state do 
  379.         (
  380.             fdecay.checked = true; ldecay.checked = sdecay.checked = false 
  381.             start_tangent = #fast; end_tangent = #slow
  382.             set_decay_tan_types ()
  383.         ) 
  384.         on sdecay changed state do 
  385.         (
  386.             sdecay.checked = true; fdecay.checked = ldecay.checked = false 
  387.             start_tangent = #slow; end_tangent = #fast
  388.             set_decay_tan_types ()
  389.         ) 
  390.  
  391.          on add_noise changed state do 
  392.         (
  393.             noiz_strength.enabled = noiz_freq.enabled = state
  394.             -- add or delete noise controllers in existing lights
  395.             for i in 1 to particles.count do
  396.             (
  397.                 local light = particles[i] 
  398.                 if add_noise.checked then
  399.                 (
  400.                     local fl = float_list ()
  401.                     fl.available.controller = light.multiplier.controller
  402.                     fl.available.controller = noise_ctrlrs[i]
  403.                     light.multiplier.controller = fl
  404.                 )
  405.                 else
  406.                     light.multiplier.controller = light.multiplier.controller.bezier_float.controller
  407.             )
  408.         )
  409.         
  410.          on noiz_strength changed val do 
  411.             for i in 1 to particles.count do
  412.                 animate on for t in start_times[i] do at time t
  413.                     noise_ctrlrs[i].noise_strength = val * start_mult.value / 100
  414.  
  415.          on noiz_freq changed val do 
  416.             for i in 1 to particles.count do
  417.                 noise_ctrlrs[i].frequency = val
  418.         
  419.         on contrast changed val do particles.contrast = val
  420.         
  421.         on near_start changed val do
  422.         (
  423.             if val > near_end.value then particles.nearAttenEnd = near_end.value = val
  424.             if val > far_start.value then particles.farAttenStart = far_start.value = val
  425.             if val > far_end.value then particles.farAttenEnd = far_end.value = val
  426.             particles.nearAttenStart = val
  427.         )
  428.         on far_start changed val do
  429.         (
  430.             if val < near_end.value then particles.nearAttenEnd = near_end.value = val
  431.             if val < near_start.value then particles.nearAttenStart = near_start.value = val
  432.             if val > far_end.value then particles.farAttenEnd = far_end.value = val
  433.             particles.farAttenStart = val
  434.         )
  435.         on near_end changed val do
  436.         (
  437.             if val < near_start.value then particles.nearAttenStart = near_start.value = val
  438.             if val > far_start.value then particles.farAttenStart = far_start.value = val
  439.             if val > far_end.value then particles.farAttenEnd = far_end.value = val
  440.             particles.nearAttenEnd = val
  441.         )
  442.         on far_end changed val do
  443.          (
  444.             if val < near_end.value then particles.nearAttenEnd = near_end.value = val
  445.             if val < near_start.value then particles.nearAttenStart = near_start.value = val
  446.             if val < far_start.value then particles.farAttenStart = far_start.value = val
  447.             particles.farAttenEnd = val
  448.         )
  449.         
  450.         on show_near changed state do particles.showNearAtten = state
  451.         on show_far changed state do particles.showFarAtten = state
  452.         on use_near changed state do particles.useNearAtten = state
  453.         on use_far changed state do particles.useFarAtten = state
  454.         
  455.          on cast_shad changed state do particles.castShadows = state
  456.          on use_global changed state do particles.useglobalshadowsettings = state
  457.         
  458.         on map_bias changed val do particles.mapbias = val
  459.         on map_size changed val do particles.mapsize = val
  460.         on map_smprange changed val do particles.samplerange = val
  461.          on abs_bias changed state do particles.absolutemapbias = state
  462.         on ray_bias changed val do particles.raytracebias = val
  463.     )
  464.  
  465.     on light_particle open do
  466.     (
  467.         particles = #(); last_age = #(); pcount = 0
  468.         addRollout lpp rolledUp:true
  469.         addRollout aboutbox rolledUp:true
  470.     )
  471.     
  472.     on light_particle close do
  473.     (
  474.         removeRollout lpp
  475.         removeRollout aboutbox
  476.     )
  477. )
  478.  
  479. --openUtility light_particle