Notifications
Clear all

[Closed] Converting simple renderer script to max sdk plugin

Hello there,
i am working on a kind of long term project right now. I ve reached my goal ( with help of the user @PolyTools3D ) using a maxscript. That maxscript is based on the svg renderer tutorial and is modified so it outputs the brightness of all faces of one specific object in the scene. I also implemented very simple dropshadows using RayMeshGridIntersect to check how many of a faces vertices cross and object if you draw a line from it to a light source.
Now to my problem. Actually the performance without dropshadows is quite okay (32ms per frame) But after implementing the dropshadows it is around 370ms per frame. Thats very sad because I am trying to do some realtime experiments. Now I thought that I have to somehow do it with max sdk to make it faster. But it seems to me that it would be more efficient to p- ay someone to convert it for me . Can someone help me ? or offer me how much it would c – ost? I am a poor art student, so i cannot p – ay huge amounts… I ask also because I have no clue if it is much work for some experienced person.
Thanks in advance,
Flub
thats the code :
`
(

clearListener()
-- Generals
global myNodeName = "kopfmodell"
-- face mapping position in uv map from left top to hardware order
global faceMap = #(159, 154, 153, 60, 134, 59, 79, 19, 43, 94, 56, 7, 12, 20, 40, 27, 65, 82, 121, 176, 1, 77, 90, 30, 91, 89, 78, 81, 85, 57, 76, 31, 44, 13, 11, 10, 83, 49, 50, 64, 93, 87, 88, 5, 4, 3, 6, 92, 75, 66, 34, 22, 58, 24, 23, 33, 21, 80, 41, 28, 39, 26, 47, 61, 9, 46, 32, 45, 2, 8, 14, 16, 15, 35, 36, 51, 73, 69, 72, 67, 74, 52, 37, 62, 18, 17, 86, 84, 63, 48, 150, 101, 106, 114, 38, 68, 70, 42, 25, 71, 29, 55, 54, 53, 185, 183, 172, 175, 179, 151, 170, 125, 138, 107, 173, 113, 137, 188, 144, 158, 187, 181, 182, 99, 98, 97, 100, 186, 95, 171, 184, 124, 152, 118, 117, 127, 115, 174, 135, 122, 133, 120, 105, 104, 177, 143, 126, 139, 96, 102, 108, 110, 109, 129, 130, 145, 169, 160, 128, 116, 168, 146, 131, 156, 112, 111, 180, 178, 157, 142, 141, 155, 103, 140, 132, 162, 164, 136, 119, 165, 123, 149, 148, 147, 167, 163, 166, 161);
-- face mpping face ids to hardware order of leds
global faceMap2 = #(132,162,164,136,119,165,123,149,148,147,167,163,166,161,168,146,131,156,112,111,180,178,157,142,141,155,103,140,126,139,96,102,108,110,109,129,130,145,169,160,128,116,152,118,117,127,115,174,135,122,133,120,105,104,177,143,144,158,187,181,182,99,98,97,100,186,95,171,184,124,185,183,172,175,179,151,170,125,138,107,173,113,137,188,150,101,106,114,38,68,70,42,25,71,29,55,54,53,73,69,72,67,74,52,37,62,18,17,86,84,63,48,47,61,9,46,32,45,2,8,14,16,15,35,36,51,75,66,34,22,58,24,23,33,21,80,41,28,39,26,11,10,83,49,50,64,93,87,88,5,4,3,6,92,1,77,90,30,91,89,78,81,85,57,76,31,44,13,79,19,43,94,56,7,12,20,40,27,65,82,121,176,159,154,153,60,134,59);
global mappedOutput = #(); -- output array
mappedOutput = (for i=1 to 188 collect (if i == 1 then 255 else 0)) --debugging values

try(
	global telnetClient  = DotNetObject "System.Net.Sockets.TcpClient" "192.168.4.1" 23
	global telnetStream =  telnetClient.GetStream()
) catch (
	print "Konnte die Verbindung nicht aufbauen."
)
fn telnetClose = (
	if (telnetClient != undefined) then (
		telnetStream.Close()
		telnetClient.Close()
	)
)
fn telnetWrite msg = (
	telnetStream =  telnetClient.GetStream()
	data = (dotNetClass "System.Text.Encoding").ASCII.GetBytes (msg + "\r\n")
	for b in data do telnetStream.WriteByte b
	telnetStream.Flush()
)
fn telnetWriteBytes  byteArray = (
	telnetStream.Write byteArray 0 188
	telnetStream.Flush()
)

try destroydialog ::RO_DISPLAY_FACES_COLORS catch()

rollout RO_DISPLAY_FACES_COLORS "Faces Colors" width:172 height:250
(
	label lab1 "Live Mode - Serial Transfer"
	checkbutton btnStartStop "Start" pos:[8,100] width:154 height:28
	
	timer clock interval:2000 active:false
    global GW_DisplayFacesColors
	
	local node = getNodeByName (myNodeName as string)
    local GetfaceNormal = polyop.getfaceNormal
    local GetFaceCenter = polyop.getFaceCenter

    fn CalculateFacesColors obj =
    (
		meshIntersections  = #()
		for otherObject in Geometry where (otherObject.name != obj.name) do (
			rm = RayMeshGridIntersect () --create an instance of the Reference Target
			rm.Initialize 10 --init. the voxel grid size to 10x10x10
			rm.addNode intersectionObject 
			rm.buildGrid ()
			append meshIntersections rm
		)
        for faceIndex = 1 to obj.numfaces do (
            value = 0
			faceNormal = normalize (GetfaceNormal obj faceIndex)
			faceCenter = GetFaceCenter obj faceIndex
			
			lightIndex = 0
			for k in lights where classof k != targetobject do (
			--for k in lights where (matchPattern k.name pattern:"*.Target" != true) do (
				lightIndex += 1
				lightDir = normalize (k.pos - faceCenter)
				
				-- calculate drop shadow
				faceVerts = polyop.getFaceVerts obj faceIndex
				dropShadowStrength = 0
				for vertexIndex in faceVerts do (
					thePos = polyop.getVert obj  vertexIndex
					theVector = k.pos - thePos
					for rm in meshIntersections do (
						theHitsCount = rm.intersectRay thePos theVector false --intersect the ray with the theObj
						if theHitsCount > 0 then --if have hit anything...
						(
							dropShadowStrength += (1.0/theFaceVerts.count)
						)
					)
				)
				-- end drop shadow calculation
				
				diffuse = amax ((dot lightDir faceNormal)*k.multiplier) 0
				value += k.color.r * diffuse * (1-dropShadowStrength)
			)
			
			value = amin (int value) 255
			mappedOutput[faceMap2[faceIndex]] = value
		)
    )
    
    fn GW_DisplayFacesColors =
    (
		st = timestamp()
		CalculateFacesColors(node)
		--telnetWriteBytes(mappedOutput)
		format "% ms\n" (timestamp()-st)
    )
	
	on clock tick do
    (
        GW_DisplayFacesColors()
		--print "tick"
	)

	fn exitProgram = (
		print "Exit program"
		telnetClose()
		clock.active = false
		unregisterRedrawViewsCallback GW_DisplayFacesColors
		completeredraw()
	)
	
    on RO_DISPLAY_FACES_COLORS open do
    (
        unregisterRedrawViewsCallback GW_DisplayFacesColors
        completeredraw()
    )

    on RO_DISPLAY_FACES_COLORS close do
    (
		exitProgram()
    )
	on btnStartStop changed arg do
	(
		unregisterRedrawViewsCallback GW_DisplayFacesColors
		if arg then -- start program
        (
			--telnetWrite("q") -- serial command to enter menu 
			--telnetWrite("livemode") -- serial command to enter live mode
			--registerRedrawViewsCallback GW_DisplayFacesColors
			clock.active = true
			if lights.count == 0 then
			(
				node = undefined
				btnStartStop.checked = false
				messagebox "There must be at least one light source in the scene"
			) else (
				if classof node.mat == standardmaterial do node.mat.diffuse = cp1.color
			)		
			
		) else (
			clock.active = false
			unregisterRedrawViewsCallback GW_DisplayFacesColors
		)
		if btnStartStop.checked then (
			btnStartStop.Text = "Stop"
		) else (
			btnStartStop.Text = "Start"
		)
		completeredraw()
	)

)
createdialog RO_DISPLAY_FACES_COLORS

44 Replies

Before moving to anything more complex (e.g., a c++ implementation), you should set a goal.
You say that the script is not fast enough … well … but maybe there is a way to make it works faster.
(but… if it was originally written by @PolyTools3D, the chance is little).

anyway… what performance is good enough for you?

It may take a working week. This means it can cost ~ $1000, by given, that’s a piece goods

You asked, I answered. (Honestly, it never makes sense to order a small script (tool) for someone else’s commercial development)

Hello, thanks for your answers. The goal is to have it around 15 ms per frame with drop shadows . My obj to get those face values about has only 188 faces. But a maximum of 40 ms would be acaptable too . The other low poly objects for the play with shadows would be maximum two. If I can manage to get a plug-in skeleton from the sdks plugin wizard I imagine it to be quite possible to write those calculations there but I don’t know if it will be so much faster. If not I am not skilled enough to implement it better than just plain in one function which gets all lights and objects and returns those values to send them via telnet or serially

Continuing the script I would need to find way for faster Ray intersection calculations . I don’t know how but i think instead of creating a ray intersection grid obj for every other object and than looping through them for every vertex I should some how snapshot and attach all other objects to one big shadow object (but how?) and then create an array for all 188 vertices
Which stores if they are in a drop shadow or not.
Then I I wouldn’t check vertices multiple times but I would need two light loops:
Now i am at around 90ms per frame with 1 light source and one extra object with 200 faces for this function, do you see any possible performance improvements for it:
if i put in a second light source execution time doubles.
´

fn CalculateFacesColors obj =
(
	-- collect lights
	theLights = #()
	for k in lights where classof k != targetobject do (
		append theLights #(k.pos, k.multiplier, k.color.r)
	)
		
	-- calculate shadow information
	vertexShadows = #()
	for otherObject in Geometry where (otherObject.name != obj.name) do (
		rm = RayMeshGridIntersect () --create an instance of the Reference Target
		rm.Initialize 10 --init. the voxel grid size to 10x10x10
		rm.addNode otherObject 
		rm.buildGrid ()
		for i=1 to obj.numVerts do (
			v = polyop.getVert obj i
			for lightIndex = 1 to theLights.count do (
				shadows = #()
				theVector = theLights[lightIndex][1] - v
				theHitsCount = rm.intersectRay v theVector true --intersect the ray with otherObject
				if theHitsCount > 0 then shadows[i] = true else shadows[i] = false --if have hit anything...
				vertexShadows[lightIndex] = shadows	
			)
                )
        )
		
	for faceIndex = 1 to obj.numfaces do (
		value = 0
		faceNormal = normalize (GetfaceNormal obj faceIndex)
		faceCenter = GetFaceCenter obj faceIndex
		faceVerts = polyop.getFaceVerts obj faceIndex
		
		for lightIndex = 1 to theLights.count do (
			lightDir = normalize (theLights[lightIndex][1] - faceCenter)
			
			-- calculate drop shadow
			dropShadowStrength = 0
			for v in faceVerts do (
				if vertexShadows[v] == true then dropShadowStrength += (1.0/theFaceVerts.count)
			)
			-- end drop shadow calculation
				
			diffuse = amax ((dot lightDir faceNormal)*theLights[lightIndex][2]) 0
			value += theLights[lightIndex][3] * diffuse * (1-dropShadowStrength)
		)
		
		value = amin (int value) 255
		mappedOutput[faceMap2[faceIndex]] = value
	)

)

´

if none of the objects are moving or animated you can cache out faceVerts, verts poisitions, faceCenters … etc
rIafZOL1C9
here’re my numbers (nothing is cached except functions, btw)

if I turn my laptop to performance mode total calc time is under 20ms including SetFaceColors

wow thats cool ! can you share your code? what do you mean by caching? saving it to a variable ?
I want to move all objects and lights. What do you mean by caching functions?
i am running win 10 on lenovo w510 i7 q820 1.7ghz quadcore , gpu quadro fx 880m and 16gb ram and ssd drive and am at 90ms on “turbo mode” = /

ps . how did you make that gif? is there a tool for these kind of gifs?

If you want to move everything then caching will be mostly useless
caching functions is something that you already did with GetFaceCenter

I use ShareX for gifs

Hello @Flub,

Could you provide a full description of the task or a test scene?

Amount of objects, faces, lights, animated parameters of any involving node?

I’ll see if I can help you with this.

example_scene01.max (732 KB)

Hello,
I created a test scene which shows the scenario.
in the scene you see a white object . it consists mainly out of planar quad faces. I have a real object with the same structure with lights installed in a way that i am able to control the brightness of every face of it. The goal is to realtime render/calculate how bright every of its faces is in different constellations of objects and light sources. For now only the angle of every face and a drop shadow are implemented in my script.
I want to be able to play around with animations and different objects in max and realtime transfer the look of the object in the max viewpor to the real object via telnet or serial port.
The red objects in the testscene are just examples to see the shadow.
The yellow squares with 6×6 faces are a future idea to be implemented. actually it is just the idea to select one or more objects which faces brightness are calculated and sent out in realtime. Than it would also be perhaps necessary to define which of them throws shadows and which are excluded from this.
Because of your help there is no problem for me to build it with the script now. But it is actually too slow. It takes 200 ms per frame for the test scene at the moment. light_transfer_script_edit09.ms (7.2 KB)

Thank you!!!
Flub

Page 1 / 5