[Closed] Converting simple renderer script to max sdk plugin
I tried that, but it does not work.
Using completeredraw() anytime the monitoring checkbutton is touched it works, but animations stop.
I think something else also gets reloaded when using completeredraw instead of the regular redraw. Perhaps it has something to do with: the SetupScene function where it says
polyop.applyuvwmap node #face channel:0
Anyways. More important is the correct illumination. Yeah I understand that this, actually I did not change the testscene I just tried to add some controls and features. It is still performing fast enough . (around 15ms)
But as I did not change the basics of the Rendering calculation. I wonder what I am missing in there, so the result is almost equivalent to the viewport renderers result. I am thinking about if it is connected to exposure control or gamma correction. Or if theres a mistake with selfshadowing as I added the object itself to the raymeshgridintersect object. Perhaps I should put the starting point of the ray which is checked for intersections with the object itself, alittle bit outside the object instead of on it, but on the other hand I think the rays which are pointing towards the same direction as the targets object face cannot intersect if the third parameter of the intersectSegment method is set to false:
rm.intersectSegment vpos lightPos false
I think the approach is good and working, I just want to reach some little goals:
– smaller difference between monitoring result and viewport rendering result
– finding out the type and position of the default light sources of a the viewport if there is no user added light in the scene. I think it is two lights with different multipliers linked to the View position. But i dont know how to find out the exactly same position.
– when that works I want to test things and add utility functions like saving the results for every frame in an animation into a file.
– when its not working, i try to find out if there is way with the dotnet assembly functions to put all the data into it and calculate everything in there. With the way serejah taught me.
I see wat you mean. There seems to be some differences between Max versions. While in Max 2016 you can turn the preview on/off works while playing an animation, in Max 2019 (and I guess others too), switching the vertex preview seems to break the visibility of the node. I don’t know why but someone may have some suggestions on how to work around this issue.
I’ve tried to implement the self-shadowing for the lit object as well as adding something to fix the preview in Max 2019, although it doesn’t work properly.
Here you have some more code to play with.
(
/* SETUP TEST SCENE ####################################################################################################### */
gc()
delete objects
res = 11
mtl1 = standard diffusecolor:white selfillumamount:100
mtl2 = standard diffusecolor:black
t1 = converttopoly(torus smooth:2 segs:(2*res) sides:res radius1:60 radius2:20 pos:[0,0,0] wirecolor:white material:mtl1 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 material:mtl2 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" castShadows:on
l2 = omnilight rgb:(color 255 255 255) pos:[ 200,0,0] multiplier:0.5 name:"light2" castShadows:on
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_TINY_RENDER catch()
rollout RO_TINY_RENDER "Tiny Render" width:172 height:168
(
checkbutton bt_enable "Enable" pos:[ 8, 8] width:154 height:32
checkbox chk_preview "Preview" pos:[ 8,50] checked:true
checkbox chk_shadows "Shadows" pos:[ 8,68] checked:true
checkbox chk_selfshadows "Self Shadows" pos:[80,68] checked:true
spinner sp_l1 "Light 1 Multiplier: " pos:[8, 94] fieldwidth:48 range:[0,10,1.0] scale:0.01
spinner sp_l2 "Light 2 Multiplier: " pos:[8,114] fieldwidth:48 range:[0,10,0.5] scale:0.01
label lb_statistics "" pos:[8,148] align:#left
global RedrawCallback
local DELTA = 0.00001
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 rm1 = RayMeshGridIntersect()
local rm1_Initialize = rm1.Initialize
local rm1_AddNode = rm1.addNode
local rm1_BuildGrid = rm1.buildGrid
local rm1_Free = rm1.free
local rm1_IntersectSegment = rm1.intersectSegment
local rm2 = RayMeshGridIntersect()
local rm2_Initialize = rm2.Initialize
local rm2_AddNode = rm2.addNode
local rm2_BuildGrid = rm2.buildGrid
local rm2_Free = rm2.free
local rm2_IntersectSegment = rm2.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]
local statistics = stringstream ""
local preview = true
local shadows = true
local selfShadows = true
fn RenderFaces = with undo off
(
rm1_Initialize 5
rm1_AddNode matte
rm1_BuildGrid()
rm2_Initialize 5
rm2_AddNode node
rm2_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
(
vpos_1 = vpos + (dir_1*DELTA)
hit = (rm1_IntersectSegment vpos_1 light_1_pos false) > 0
if selfShadows and not hit do hit = (rm2_IntersectSegment vpos_1 light_1_pos false) > 0
vertsHits[j][1] = hit
)
if (dot dir_2 vnormal) >= 0 do
(
vpos_2 = vpos + (dir_2*DELTA)
hit = (rm1_IntersectSegment vpos_2 light_2_pos false) > 0
if selfShadows and not hit do hit = (rm2_IntersectSegment vpos_2 light_2_pos false) > 0
vertsHits[j][2] = hit
)
)
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
if preview == true do
(
colour.x = colour.y = colour.z = value
for k in mapFaces[f] do SetMapVert node 0 k colour
)
)
update node
rm1_Free() -- Prevent memory leaking
rm2_Free() -- Prevent memory leaking
)
fn RenderFacesNoShadows = with undo off
(
light_1_pos = light_1.center
light_1_multiplier = light_1.multiplier
light_2_pos = light_2.center
light_2_multiplier = light_2.multiplier
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 = amax (ang_1*light_1_multiplier) 0
value += amax (ang_2*light_2_multiplier) 0
value = amax 0.0 (amin value 1.0)
valuesArray[f] = value
if preview == true do
(
colour.x = colour.y = colour.z = value
for k in mapFaces[f] do SetMapVert node 0 k colour
)
)
update node
)
local elapsedTime = 0
local elapsedCounter = 0
fn RedrawCallback =
(
st = timestamp(); sh = heapfree
if shadows == true then RenderFaces() else RenderFacesNoShadows()
elapsedTime += timestamp()-st
elapsedCounter += 1
if elapsedCounter >= 60 do
(
elapsedTime /= 60
fps = int ((1000.0/elapsedTime) + 0.5)
free statistics
format "% FPS | %ms | heap:%\n" fps elapsedTime (sh-heapfree) to:statistics
lb_statistics.text = statistics
elapsedTime = 0
elapsedCounter = 0
)
)
fn RedrawViewports =
(
for j = 1 to 2 do
(
if not (isanimplaying()) then completeredraw() else redrawviews()
)
)
fn SetupScene =
(
polyop.applyuvwmap node #face channel:0
for j = 1 to polyop.getnummapverts node 0 do SetMapVert node 0 j [0,0,0]
for j = 1 to node.numfaces do polyop.setfacesmoothgroup node j 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 = preview
node.vertexColorsShaded = on
RedrawViewports()
)
on bt_enable changed arg do
(
unregisterredrawviewscallback RedrawCallback
for j in RO_TINY_RENDER.controls where j != bt_enable do j.enabled = arg
if arg then
(
SetupScene()
bt_enable.text = "Disable"
chk_selfshadows.enabled = shadows
--playanimation()
)else(
bt_enable.text = "Enable"
--stopanimation()
)
)
on RO_TINY_RENDER open do
(
unregisterredrawviewscallback RedrawCallback
gc()
)
on RO_TINY_RENDER close do
(
unregisterredrawviewscallback RedrawCallback
stopanimation()
)
on sp_l1 changed arg do
(
light_1.multiplier = arg
RedrawViewports()
)
on sp_l2 changed arg do
(
light_2.multiplier = arg
RedrawViewports()
)
on chk_preview changed arg do
(
preview = node.showVertexColors = arg
if preview then node.mat.selfIllumAmount = 100 else node.mat.selfIllumAmount = 0
RedrawViewports()
)
on chk_shadows changed arg do
(
shadows = chk_selfshadows.enabled = node.receiveshadows = arg
RedrawViewports()
)
on chk_selfshadows changed arg do
(
selfShadows = node.castShadows = arg
RedrawViewports()
)
)
createdialog RO_TINY_RENDER
)
Thanks for that, i am still working on this.
At the moment I am trying to simulate the default lighting.
I created a testcene where i created the 2 default lights ( defaultKeyLight and defaultFillLight ) and created a camera from view before moving anything.
default_lighting_with_visibile.max (196 KB)
Now I found out how to get the viewport camera position, its direction and its rotation like this:
coordSysTM = inverse (getViewTM())
viewDir = -coordSysTM.row3 -- Get camera direction
viewPos = coordSysTM.row4 -- Get camera position
viewTarget = viewPos+(viewDir*gw.GetFocalDist())
viewRotation = (getViewTM()).rotation -- rotation around viewDir vector -> contains viewRotation.angle in degrees and viewRotation.axis
viewRotationVector = normalize [viewRotation.x, viewRotation.y, viewRotation.z]
Now If you look at the default_light_scene i just uploaded you can see all Rays as splines for a better understanding. My question is, how can i get the red vector only from the information about the viewport?
The plan is to measure out the angle(s) of the default situation and then somehow calculate the defaultLights position based on the viewport cameras position, its direction and its rotation.
My Math is not good enough at the moment. Can anyone help me out with this?
Thanks in advance!
EDIT:
By trial and erroring for hours i stumpled upon something which seems to work. But I dont know what s in viewTM().row2 …
clearListener()
fn drawLine pointA pointB aColor: = ( ss = SplineShape pos:pointA; if (aColor != unsupplied) then (ss.wirecolor = aColor); addNewSpline ss; addKnot ss 1 #corner #line PointA; addKnot ss 1 #corner #line PointB; updateShape ss; ss )
--coordSysTM = inverse (viewport.getTM())
viewTM = getViewTM()
coordSysTM = inverse (viewTM)
viewRow2 = -coordSysTM.row2 -- ? no clue what it is
viewDir = -coordSysTM.row3 -- Get camera direction
viewPos = coordSysTM.row4 -- Get camera position
viewTarget = viewPos+(viewDir*gw.GetFocalDist())
print "tee"
--default on startup 2014.94
/*keyLightDist = distance viewTarget $DefaultKeyLight.position
fillLightDist = distance viewTarget $DefaultFillLight.position
*/
defaultLightDist = 2014.94
v1 = normalize viewDir
v2 = normalize viewRow2
nv1 = normalize (cross v1 v2)
nv2 = normalize (cross nv1 v1)
tempMatrix = matrix3 v1 nv2 nv1 viewPos
/*
drawLine viewPos (viewPos + v1 * 150) aColor:red
drawLine viewPos (viewPos + nv2 * 150) aColor:green
drawLine viewPos (viewPos + nv1 * 150) aColor:blue
*/
--World Point * inverse yourObject.transform -> back to local
--Local Point * yourObject.transform -> world coordinates
--lightPosLocal = $DefaultKeyLight.position * inverse tempMatrix -- default [-1663.51,-627.287,-70.7103]
--print "lightPosLocal = " + (lightPosLocal as string)
defaultKeyLightPosLocal = [-1663.51,-627.287,-70.7103]
defaultKeyLightPos = defaultKeyLightPosLocal * tempMatrix -- word space value
print "lightPosWorld = " + (defaultKeyLightPos as string)
keyLightVector = normalize (defaultKeyLightPos - viewTarget)
drawLine viewTarget (keyLightVector * -defaultLightDist + viewTarget)
I try now to implement it to calculate the position of the default lights depending on the viewport camera pos. BUT, i dont know if the distance of the default light changes when i zoom out. Does anyone know?