Notifications
Clear all

[Closed] Converting simple renderer script to max sdk plugin

Wow, thank you very much for your help! I am happy to check it out as soon as possible. The setting of the vertex colors inside max is actually only kind of a monitoring for the generated data which is good for debugging and nice to have but not the main goal as data will be displayed on a real object with leds behind each polygon. But I see that the c# code has all except the vertHits calculation inside already.
thanks again, i hope i can get it running, as i dont know yet what to do with the c# code. Do I have to compile it as .dll and put it in max root directory to access it from script? I didnt find any dll loading code in your maxscript.
Thanks again!
Flub

I use visual studio so for me it is easier to load it like this, but you can compile it on-the-fly right from the maxscript

(
  dllpath = @"C:\...\visual studio 2015\Projects\test\bin\Release\test.dll"
  assembly = (dotnetclass "System.Reflection.Assembly").Load ((dotnetclass "System.IO.File").ReadAllBytes dllpath)
  global mshops = (dotNetClass "System.Activator").CreateInstance (assembly.GetType("test.meshops"))
)

-- and then in maxscript call c# function like this
mshops.GetMeshTotalArea obj.inode.handle

-- or assign c# function to a global variable
GetMeshTotalArea = mshops.GetMeshTotalArea

Okay thats an option. I also have visual studio where i could create a dll. So how did you access it? because there is not dll path or something in your max script.

example

Ok, here is a last pure MXS attempt.

If I understood correctly, your setup is: 2 objects (188 verts each), 1 lite 1 matte and 2 light sources.

This runs at 3-4ms per iteration on my end, which is about 250-280 FPS, width very low memory footprint.

I get the same performance using IntersectRay() (to avoid the UI flickering), but there seems to be a bug in Max 2019 and the faces don’t get lit correctly. However in Max 2016 it works well.

(
	/* SETUP TEST SCENE ####################################################################################################### */
	
	gc()
	delete objects
	
	res = 11
	
	t1 = converttopoly(torus smooth:2 segs:(2*res) sides:res radius1:60 radius2:20 pos:[0,0,0] wirecolor:black name:"lit")
	rotate t1 (angleaxis 90 [0,1,0])
	
	t2 = converttopoly(torus smooth:2 segs:(2*res) sides:res radius1:60 radius2:20 pos:[-100,0,0] wirecolor:black name:"matte")
	rotate t2 (angleaxis 90 [0,1,0])
	
	l1 = omnilight rgb:(color 255 255 255) pos:[-200,0,0] multiplier:1.0 name:"light1"
	l2 = omnilight rgb:(color 255 255 255) pos:[ 200,0,0] multiplier:0.5 name:"light2"
	
	d1 = dummy()
	d2 = dummy()
	
	t2.parent = d1
	l1.parent = l2.parent = d2
	
	animationrange = (interval 0f 200f)
	
	with animate on
	(
		at time 200
		(
			rotate d1 (angleaxis 360 [0,0,1])
			rotate d2 (angleaxis 360 [0,1,0])
		)
	)
	
	/* END SETUP TEST SCENE ################################################################################################### */
	
	try destroydialog ::RO_DISPLAY_FACES_COLORS catch()
	
	rollout RO_DISPLAY_FACES_COLORS "Faces Colors" width:172 height:100
	(
		checkbutton bt_start "Start" pos:[8,8] width:154 height:32
		
		spinner sp_l1 "Light 1 Multiplier: " pos:[8,54] fieldwidth:48 range:[0,10,1.0] scale:0.01
		spinner sp_l2 "Light 2 Multiplier: " pos:[8,76] fieldwidth:48 range:[0,10,0.5] scale:0.01
		
		global RedrawCallback
		
		local node    = $lit
		local matte   = $matte
		local light_1 = $light1
		local light_2 = $light2
		
		local GetfaceNormal = polyop.getfacenormal
		local GetFaceCenter = polyop.getfacecenter
		local GetFaceVerts  = polyop.getfaceverts
		local GetVert       = polyop.getvert
		local GetMapFace    = polyop.getmapface
		local SetMapVert    = polyop.setmapvert
		
		local rm                  = RayMeshGridIntersect()
		local rm_Initialize       = rm.Initialize
		local rm_AddNode          = rm.addNode
		local rm_BuildGrid        = rm.buildGrid
		local rm_Free             = rm.free
		local rm_IntersectSegment = rm.intersectSegment
		
		local facesVerts  = #()
		local mapFaces    = #()
		local vertsHits   = #()
		local valuesArray = #()		-- Store the light values for each face (range 0.0-1.0)
		local colour      = [0,0,0]
		
		fn CalculateFacesColors = with undo off
		(
			rm_Initialize 5
			rm_AddNode matte
			rm_BuildGrid()
			
			light_1_pos        = light_1.center
			light_1_multiplier = light_1.multiplier
			
			light_2_pos        = light_2.center
			light_2_multiplier = light_2.multiplier
			
			mesh = snapshotasmesh node
			
			for j = 1 to node.numverts do
			(
				vnormal = GetNormal mesh j
				vpos    = GetVert node j
				
				dir_1 = normalize (light_1_pos - vpos)
				dir_2 = normalize (light_2_pos - vpos)
				
				if (dot dir_1 vnormal) >= 0 do vertsHits[j][1] = (rm_IntersectSegment vpos light_1_pos false) > 0
				if (dot dir_2 vnormal) >= 0 do vertsHits[j][2] = (rm_IntersectSegment vpos light_2_pos false) > 0
			)
			
			free mesh
			
			for f = 1 to node.numfaces do
			(
				faceNormal = GetfaceNormal node f
				faceCenter = GetFaceCenter node f
				
				dir_1 = normalize (light_1_pos - faceCenter)
				dir_2 = normalize (light_2_pos - faceCenter)
				
				ang_1 = dot dir_1 faceNormal
				ang_2 = dot dir_2 faceNormal
				
				value    = 0.0
				strength = 1.0/facesVerts[f].count	-- If all faces have 4 vertex this value is fixed 0.25
				
				illum = 0.0
				if ang_1 >= 0 do
				(
					for i in facesVerts[f] where not vertsHits[i][1] do illum += strength
					value += (amax (ang_1*light_1_multiplier) 0) * illum
				)
				
				illum = 0.0
				if ang_2 >= 0 do
				(
					for i in facesVerts[f] where not vertsHits[i][2] do illum += strength
					value += (amax (ang_2*light_2_multiplier) 0) * illum
				)
				
				value = amax 0.0 (amin value 1.0)
				
				valuesArray[f] = value
				
				colour.x = colour.y = colour.z = value
				
				for k in mapFaces[f] do SetMapVert node 0 k colour
			)
			
			update node
			
			rm_Free()	-- Prevent memory leaking
		)
		
		fn RedrawCallback =
		(
			clearlistener(); st = timestamp(); sh = heapfree
			
			CalculateFacesColors()
			
			format "time:% heap:%\n" (timestamp()-st) (sh-heapfree)
		)
		
		fn SetupScene =
		(
			polyop.applyuvwmap node #face channel:0
			
			mapFaces   = for j = 1 to node.numfaces collect GetMapFace node 0 j
			facesVerts = for j = 1 to node.numfaces collect GetFaceVerts node j
			vertsHits  = for j = 1 to node.numverts collect #(false, false)
			
			registerredrawviewscallback RedrawCallback
			
			node.vertexColorType  = 0
			node.showVertexColors = on
			
			completeredraw()
		)
		
		on bt_start changed arg do
		(
			unregisterredrawviewscallback RedrawCallback
			
			if arg then
			(
				SetupScene()
				playanimation()
				bt_start.text = "Stop"
			)else(
				bt_start.text = "Start"
				stopanimation()
			)
		)
		
		on RO_DISPLAY_FACES_COLORS open do
		(
			unregisterredrawviewscallback RedrawCallback
			gc()
		)
		
		on RO_DISPLAY_FACES_COLORS close do unregisterredrawviewscallback RedrawCallback
		
		on sp_l1 changed arg do
		(
			light_1.multiplier = arg
			redrawviews()
		)
		
		on sp_l2 changed arg do
		(
			light_2.multiplier = arg
			redrawviews()
		)
		
	)
	
	createdialog RO_DISPLAY_FACES_COLORS

)

Thank you very much! Okay i need some time to check out your versions now.

If I understood correctly, your setup is: 2 objects (188 verts each), 1 lite 1 matte and 2 light sources.

Actually my model has 188 planar quad faces and 237 vertices. the other objects are static. The the goal is to be able to play around with creation and positioning of light sources and different objects, and animations in real time. A standard scenario would be: You open the .max file with the master model and start the script. Then you orbit around the model and the script sends out the different brightness values (0-255) which change while orbitting because the default viewports lights are shining on the model and theyre connected to the viewport camera. Then you create a light source to change from the default ligth situation to specific one. you move it around to see how it looks. Then you animate the master model to fall down onto a plane and you see on the real model how the light on the virtual 3d object is changing while the static real object is displaying the light situation on that model. then you create some objects to throw shadows on the master object to see how that looks. Then you arrange or animate them. Then you create another light source which is animated like a sun moving around in a far distance and then you see how that looks on the real object which displays the data.

The next step is actually easily done when the first performs well i think. It would be to add another lowpoly master object and send out the brightness values of its faces also. Then there would be two real objects with leds which brightnesses are controlled by a virtual light scene. If you know what I mean.

sorry for the text getting so long but I hope that explains better what Id like to achieve.

I have modified the way you where calculating the shadows intensity. I think now it is working properly but you still need to test it and see if it is correct.

Also added the array to store the lighting values.

So, all the objects will be dynamic (animated or not), including the lights right?

Well, you need to do all the tests considering the worst case scenario (amount of models, faces, lights, animations, etc.) at least.

If possible make it 50-100% worst and try to be happy with the results. If you are not happy you will probably need to find a different approach to be on a “safe” side.

Hey there,
sorry for the late response, I had some problems with my laptop and so on.
It seems that it is fast enough now, I worked on the script by @PolyTools3D and I can get around 16fps in the example scene with two 244 faces torus objects and 2 omni lightsources. I am also trying to implement the dotnet (dll assembly) of @Serejah now to make monitoring even faster and to get a better understanding of how to work with dotnet in conjunction with maxscript / 3ds Max. The function to display faces colors is actually for meshes and uses ITriObject class. I will try to change it i IPolyObject as am focusing on quads.
rtViewportRender.cs (3.2 KB)
e.g. Line 28-29 (the filename is not right, because there is no rendering code yet)
Now, as I am finetuning the script for my needs I am facing some problems. I think that I am making some mistakes in the rendering calculation. I see a difference in color which I cannot understand between the images of viewport renderer and the monitoring of the script render results. My calculation per light source is
value += (theFillColor * lightColor(range 0-1) * dotLightDirFaceNormal(or diffusecolor) + lightColor * theSpecular) * illumination (depending on dropshadow) * lightmultiplier
But there are these strange darker faces. I thought they have to do with my calculation of dropShadows because i now also check for intersections between the object itself i.e. self shadowing is enabled.

This is the script:polytools3d_04_edit11.ms (8.8 KB)

Do you see where does differences are coming from? And I wondered I ve got another issue which is quite small but perhaps it is easy for you. I ve implemented the switching between monitoring and non monitoring mode using completeredraw() but this stops a playing animation. I already tried to save the animation play state beforehand and check if in the line after the redraw to decide if it is necessary to call playanimation() again, but it seems that completeredraw is somekind of asynchronous because it does not work like this.
Thank you for helping me so much.
Flub

If you want to turn the monitoring on/off without stopping the animation you can check if the animation is playing and only call completeredraw() if it is not playing.

if not (isanimplaying()) do completeredraw()

If the animation is playing, forcing a complete redraw won’t have any effect as everything will be redrawn on next frame anyway.

Regarding the illumination, as you have modified both the script and the scene substantially, it is to be expected a drop in performance as well as the introduction of new bugs.

Please consider what I mentioned earlier, you must have a clear understanding of what the scene will be and what you need to do with it. If you keep modifying this over the time, you might get to a point where you may realize this is not the best approach for your project. I am not saying it is not, but as the “conditions” are unclear, it is a possibility.

Any attempt to code a solution, needless to say to profile it, will most probably be a waste of time if the objective is unclear.

A scene with 2000 triangles with two lights and shadows is something that a very basic render engine should run in you cellphone without any issue, at very high frame rates.

Page 4 / 5