Notifications
Clear all

[Closed] Replace "Render button/max quick render" sequence with custom function?

I’ve almost completed a scripted plugin, but Max’s default render does not update the scene between frames as I need it to. I’ve been able to solve this by writing my own function to render animations, and would like to be able to drop it in place of the standard sequence.

Looking back at http://forums.cgsociety.org/showthread.php?f=98&t=636458 I discovered it is possible to halt and cancel Max’s render sequence. However, when I try to run the following, for example:

dialogMonitorOps.unregisterNotification id:#test
dialogMonitorOps.enabled = false

fn customRender = (
	local hwnd = dialogMonitorOps.getWindowHandle()
	local hwndText = uiAccessor.getWindowText hwnd

	if classOf hwndText == string do (
		local substr = substring hwndText 1 9

		if substr == "Rendering" do (
			-- Cancel standard render
			uiAccessor.pressButtonByName hwnd "Cancel" 

			-- Substitute scripted render
			render() -- PLACEHOLDER
		)
	)
	
	true
)

dialogMonitorOps.enabled = true
dialogMonitorOps.interactive = false
dialogMonitorOps.registerNotification customRender id:#test

I get the following:

– Error occurred in customRender(); filename: C:\Users\Kevin\Documents\3dsMax\scripts\customRender.ms; position: 462; line: 16
– Frame:
– hwndText: “Rendering”
– substr: “Rendering”
– hwnd: 5444878P

To save the trouble of counting, that’s the line which in this case simply says render()
Obviously that works when run outside the function, and Max doesn’t seem to want to tell me what the error actually IS. Any tips on getting this working?

23 Replies
1 Reply
(@denist)
Joined: 10 months ago

Posts: 0

maybe it’s better to find a way to let max update the scene?

I’ve tried several different workarounds for this, all of them either giving me the same error as this one, or

– Runtime error: Unable to open renderer, possible recursion <<

Can anyone tell me if there is any way to do this???

I’d hoped this might be a possible workaround, but unfortunately it still returns an unidentified error.

global wasRender = false
dialogMonitorOps.unregisterNotification id:#test
dialogMonitorOps.enabled = false

fn check = (
	if wasRender then (
		render()
		wasRender = false
	)
	else (
		local hwnd = dialogMonitorOps.getWindowHandle()
		local hwndText = uiAccessor.getWindowText hwnd		

		if classOf hwndText == string do (
			local substr = substring hwndText 1 9

			if substr == "Rendering" do (
				uiAccessor.pressButtonByName hwnd "Cancel"
				wasRender = true
			)
		)	
	)

	return true
)

dialogMonitorOps.registerNotification check id:#test
dialogMonitorOps.enabled = true

Well, it’s ugly, but it will get the job done until something better comes along.

rollout RO_Delay "Preparing Render" (
  timer clock "testClock" interval:1000

  on clock tick do (
    render()
    destroyDialog RO_Delay
  )
)


dialogMonitorOps.unregisterNotification id:#test
dialogMonitorOps.enabled = false

fn check = (
	local hwnd = dialogMonitorOps.getWindowHandle()
	local hwndText = uiAccessor.getWindowText hwnd		

	if classOf hwndText == string do (
		local substr = substring hwndText 1 9

		if substr == "Rendering" do (
			uiAccessor.pressButtonByName hwnd "Cancel"
			createDialog RO_Delay
		)
	)

	return true
)

dialogMonitorOps.registerNotification check id:#test
dialogMonitorOps.enabled = true

http://forums.cgsociety.org/showthread.php?f=98&t=1434925

Feel free to take a crack at it if you like. I’ve thrown all the time at it that I’m going to, and have been told by more experienced people than myself that it can’t be done.

how is about just make several instances of the same light with all available shadow generators. And turn them ON/OFF using a parameter (instead of reassign a new generator).

In this case it will be truly animatable and renderable for every built-in renderer

2 Replies
(@malkalypse)
Joined: 10 months ago

Posts: 0

It seems like it should be possible to use a #prerender callback to create those additional lights and set their shadow generator values prior to rendering any individual frames (and then removing them when the render is done). But I was never able to get that working. Can you look at this setup and see if you are able to get the createShadowLightArray function working with the callbacks?


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
	*/
)
(@aaandres)
Joined: 10 months ago

Posts: 0

I’ve done some tests again and I’ve found a few things that may be interesting,
1/ When you do:
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())
I don’t know why but when you assign the shadowGenerator to one of them, all the lights get this new value for shadowGenerator. A bug in copy lights?
So, you may create new ligths from scratch:
L1 = omniLight pos:src.pos castShadows:on; L1.name = “L1”; L1.shadowGenerator = (Adv__Ray_Traced())
L2 = omniLight pos:src.pos castShadows:on; L2.name = “L2”; L2.shadowGenerator = (mental_ray_Shadow_Map())
L3 = omniLight pos:src.pos castShadows:on; L3.name = “L3”; L3.shadowGenerator = (Area_Shadows())
L4 = omniLight pos:src.pos castShadows:on; L4.name = “L4”; L4.shadowGenerator = (shadowMap())
L5 = omniLight pos:src.pos castShadows:on; L5.name = “L5”; L5.shadowGenerator = (raytraceShadow())

2/ When I render the whole animation I have no chance… BUT, if I render from frame 1 to 50 AND the TimeSlider is at frame 1, it renders perfectly all the frames.

Don’t know if this can help you.

6th post down that was what I was trying to do actually, but I couldn’t figure out how to make it work right.

here is a setup that doesn’t need any callbacks to work:


ca_sg = attributes ca_sg attribID:#(111,222)
(
	fn update = 
	(
		if iskindof this.light RefTargMonitor do
		(
			format "% > %
" currenttime this.current
			this.light.refTarg.shadowGenerator = this.shadowGenerator
			1
		)
		0
	)
	parameters params
	(
		light type:#maxobject
		current type:#integer default:1 animatable:on 
		shadowGenerators type:#maxobjecttab tabsize:3
		shadowGenerator type:#maxObject 
		
		on shadowGenerator get val do
		(
			val = shadowGenerators[current]
		)
	)
)

delete objects
d = dummy()
custattributes.add d ca_sg
d.ca_sg.shadowGenerators = #(Area_Shadows(), ShadowMap(), RayTraceShadow())
	
	
lit = omnilight()
d.ca_sg.light = RefTargMonitor refTarg:lit

c = d.scale.controller = scale_script()
c.addobject "owner" (RefTargMonitor refTarg:d.ca_sg)
	
c.setexpression "owner.reftarg.update(); [1,1,1]"	

at time 0 animate on d.current = 1
at time 10 animate on d.current = 2
at time 20 animate on d.current = 3	
	
--lit.shadowGenerator



but it doesn’t solve your problem.

the ‘shadowGenerator’ parameter is not animatable. It’s being taken once works for whole rendering session. There is no way to change it on the fly

2 Replies
(@malkalypse)
Joined: 10 months ago

Posts: 0

There is and there isn’t. The solution I am currently using is to cancel the built-in render sequence, and instead call a function which renders each frame separately. The end result is exactly what I want.

(@denist)
Joined: 10 months ago

Posts: 0

you should not care about overriding built-in render.
You just can simply say –

Want My Render – Use My Button.

That’s it

I’d considered it, but I’d rather have it run invisibly. This way people can use what they’re used to and start it from the same dialog where they set all the parameters. Besides, Max’s built-in render can be accessed so many different ways, and I was annoyed by the way it handles non-animatable parameters.

Also, it’s been an opportunity to learn some pretty interesting things.

So now the only thing I need to figure out how to implement is the “Current Task” progress bar as shown in the rendering dialog (i.e. Transforming Vertices/Rendering Image).

Unfortunately I’m not really sure where those values actually come from. Any tips on where I might be able to find this information?

Page 1 / 2