How can I make this update between frames on render?

I'm working on something to improve working with lights in Max. The sample below is a simplified version of a small part of what I'm doing, but it illustrates the issue I'm having. Basically, this script should allow a light's shadow type to be animated. It works while scrubbing the slider, but not while rendering multiple frames at a time. What is it I'm doing wrong?

-- Function to set a light's shadow type
fn setShadowType lit t = (
    local sg = &lit.shadowGenerator
 
    case t of (
        1: if classOf (*sg) != Adv__Ray_Traced do (*sg) = Adv__Ray_Traced()
        2: if classOf (*sg) != mental_ray_Shadow_Map do (*sg) = mental_ray_Shadow_Map()
        3: if classOf (*sg) != Area_Shadows do (*sg) = Area_Shadows()
        4: if classOf (*sg) != ShadowMap do (*sg) = ShadowMap()
        5: if classOf (*sg) != RayTraceShadow do (*sg) = RayTraceShadow()
        default: ()
    )
)
 
 
-- Function called by ddl_ShadowType handler
fn h_ddl_ShadowType ctrl = (
    local h = ctrl.modifiers[#holder]
    setShadowType h.lit h.shadowType
)
 
 
-- Custom attributes for control object
ca_ShadowControl = attributes "shadowControl" (
    parameters params rollout:RO (
        lit type:#maxObject
        shadowType type:#integer ui:ddl_ShadowType animatable:true default:1
    )
 
    rollout RO "Shadow Control" (
        local ctrl
 
        dropDownList ddl_ShadowType "Shadow Type" \
            items:#("Adv. Ray Traced", "mental ray Shadow Map", "Area Shadows", "Shadow Map", "Ray Traced Shadows")
 
        on RO open do (
            animButtonState = true
            ctrl = (refs.dependentNodes (custAttributes.getOwner this))[1]
        )		
 
        on ddl_ShadowType selected s do h_ddl_ShadowType ctrl
    )
)
 
 
-- Set up callbacks
-------------------
unregisterTimeCallback updateShadows
callbacks.removeScripts id:#shadowControl
 
fn updateShadows = (
	format "fn updateShadows\n"
	format " currentTime: %\n" currentTime
	local ctrl
	for o in objects where (o.modifiers[#holder] != undefined) do ctrl = o
 
	local h = ctrl.modifiers[#holder]
	local lit = refs.dependentNodes h.lit firstOnly:true
	local sg = &lit.shadowGenerator
 
	case h.shadowType of (
		1: if classOf (*sg) != Adv__Ray_Traced do (*sg) = Adv__Ray_Traced()
		2: if classOf (*sg) != mental_ray_Shadow_Map do (*sg) = mental_ray_Shadow_Map()
		3: if classOf (*sg) != Area_Shadows do (*sg) = Area_Shadows()
		4: if classOf (*sg) != ShadowMap do (*sg) = ShadowMap()
		5: if classOf (*sg) != RayTraceShadow do (*sg) = RayTraceShadow()
	)
 
	format " classOf lit.shadowGenerator: %\n" (classOf lit.shadowGenerator)
)
 
registerTimeCallback updateShadows
callbacks.addScript #preRenderEval "updateShadows()" id:#shadowControl
 
 
-- Set up test scene
(
    delete objects
    p = plane pos:[0,0,0] length:200 width:200
    s = sphere pos:[37.5,25,50]
 
    lit = freeSpot pos:[0,0,200] castShadows:on
    ctrl = point pos:[0,0,0] size:100 box:on cross:off
 
    holder = emptyModifier name:"holder"
    custAttributes.add holder ca_ShadowControl #unique
    addModifier ctrl holder
 
    holder.lit = lit
 
    with animate on for k = 0 to 4 do at time k ( -- General Parameters: (Shadow Types)
        ctrl.modifiers[#holder].shadowType = k + 1	
        h_ddl_ShadowType ctrl
    )
)

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Malkalypse's picture

Another scripter

Another scripter (PolyTools3D) helped me out on a previous project, and I thought I might be able to apply what I learned from there to this script.
My idea is that maybe I can create an array of temporary lights, each with its own shadow generator, just prior to rendering, and delete them afterward. When I run the following it works as expected, but it requires the associated function to be run by the user manually. My attempts to incorporate calling the functions directly through the callbacks result in the created script controller executing improperly.

Can anyone take a look at this and see if there is a way to fix that?

fn createShadowLightArray ctrl src = (
	L1 = copy src; L1.name = "L1"; L1.shadowGenerator = (Adv__Ray_Traced())
	L2 = copy src; L2.name = "L2"; L2.shadowGenerator = (mental_ray_Shadow_Map())
	L3 = copy src; L3.name = "L3"; L3.shadowGenerator = (Area_Shadows())
	L4 = copy src; L4.name = "L4"; L4.shadowGenerator = (shadowMap())
	L5 = copy src; L5.name = "L5"; L5.shadowGenerator = (raytraceShadow())
 
	global lightArray = #(L1, L2, L3, L4, L5)
 
	scr = ctrl.visibility.controller
	for lit in lightArray do scr.AddNode lit.name lit
 
	expr  = "lightArray = #(L1, L2, L3, L4, L5)\n"
	expr += "for lit = 1 to lightArray.count do try (lightArray[lit].enabled = (ctrl.lit == lit); lightArray[lit].ishidden = (ctrl.lit != lit)) catch()\n"
	expr += "1"
	scr.setExpression expr
 
	return lightArray
)
 
 
fn destroyShadowLightArray ctrl lightArray = (
	scr = ctrl.visibility.controller
	for lit in lightArray do (
		scr.deleteVariable lit.name
		delete lit
	)
	scr.setExpression "1"
)
 
 
(
	delete objects
	box()
 
	global testLight = omniLight pos:[0,0,50] castShadows:on
	global ctrl = plane length:50 width:150 lengthSegs:1 widthSegs:1 wireColor:white --isSelected:true
 
	ctrl.visibility = float_script()
	scr = float_script()
	ctrl.visibility.controller = scr
	scr.AddNode "ctrl" ctrl	
	scr.setExpression "1"
 
	ca = attributes ca attribID:#(0, 0)	(
		parameters params rollout:params (
			lit type:#integer default:1 animatable:on ui:lightID
		)
 
		rollout params "Shadow Lights Control"	(
			radiobuttons lightID "Light ID" labels:#("1", "2", "3", "4", "5")
		)
	)		
 
	custAttributes.add ctrl ca #unique
 
	callbacks.removeScripts id:#test
	callbacks.addScript #preRender "testLight.enabled = false; for lit in lightArray do lit.enabled = true" id:#test
	callbacks.addScript #postRender "testLight.enabled = true; destroyShadowLightArray ctrl lightArray" id:#test
 
	seed 0
	with animate on (
		for j = 50 to 0 by (random -1 -5) do at time j ctrl.lit = random 1 5
	)
 
	completeRedraw()
 
	/*
	-- RUN PRIOR TO RENDERING
	global lightArray = createShadowLightArray ctrl testLight
	*/
)
Malkalypse's picture

I've edited the script to

I've edited the script to output every time the setShadowType function runs, along with the current time and the resulting shadow generator of the light.

As it renders, it displays the following in the listener:

fn updateShadows
currentTime: 0f
classOf lit.shadowGenerator: Adv__Ray_Traced
fn updateShadows
currentTime: 1f
classOf lit.shadowGenerator: mental_ray_Shadow_Map
fn updateShadows
currentTime: 2f
classOf lit.shadowGenerator: Area_Shadows
fn updateShadows
currentTime: 3f
classOf lit.shadowGenerator: shadowMap
fn updateShadows
currentTime: 4f
classOf lit.shadowGenerator: raytraceShadow

So the function is clearly being called, but for whatever reason the resulting changes are being ignored by the renderer. Hopefully this can help someone figure out what the problem is?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.