Notifications
Clear all

[Closed] 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
"
	format " currentTime: %
" 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: %
" (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
	)
)





6 Replies

The #preRenderFrame notification is “sent out after the renderer has taken a snapshot of the scene geometry”.
Have you tryed with the #preRenderEval?

Yes, I tried #prerendereval, but there was no difference in the result

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?

So after some further testing I have come to the realization that these callbacks don’t work the way I thought they did. Either there is some way to make them do what I want, or I will have to find a different approach…

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)
"
	expr += "for lit = 1 to lightArray.count do try (lightArray[lit].enabled = (ctrl.lit == lit); lightArray[lit].ishidden = (ctrl.lit != lit)) catch()
"
	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
	*/
)

I’m starting to think I am going to have to look to the SDK for a solution…