you can really use the orbit tool while running the script?
I also tried to send the same message to ViewportControlPanel but then i couldnt even select the orbit tool anymore…
My earlier script has the same problem that I have to select the object to start the render to texture script.
If I only could find a way to do it in the background or to render to texture without selecting the object everything would work out.
The new “All prepared” option in the render to texture rollout was a hope but I dont know how to select in the script.
clearListener()
global nodeName = "right_perfect"
global DotNetForm
-- Check variable and close window if it exists
try DotNetForm.close() catch()
(
unregisterRedrawViewsCallback printTime
global obj = getnodebyname nodeName
-- Create a form
global DotNetForm = DotNetObject "System.Windows.Forms.Form"
DotNetForm.Size = (DotNetObject "Drawing.Size" 200 80)
global ViewPanelHwnd = 0
fn GetViewportsPanelHWND = ( for j in (windows.getchildrenhwnd #max) where j[4] == "ViewPanel" do exit with j[1] )
ViewPanelHwnd = GetViewportsPanelHWND()
global ViewPanelHwnd2 = 0
fn GetViewportsPanelHWND2 = ( for j in (windows.getchildrenhwnd #max) where j[4] == "RollupPanel" do exit with j[1] )
ViewPanelHwnd2 = GetViewportsPanelHWND2()
-- create bakemap to use later
global monitor = bitmap 14 14 color:green
display monitor
-- set render environment background to black
execute "backgroundcolor = color 0 0 0"
if (obj != undefined AND obj.name == nodeName) then (
local bakeMap = diffusemap()
bakeMap.outputSzX = 16
bakeMap.outputSzY = 16
bakeMap.fileType = ".png"
bakeMap.filterOn = false
bakeMap.shadowsOn = on
bakeMap.lightingon = on
bakeMap.targetMapSlotName = "Diffuse"
bakeMap.enabled = true
obj.INodeBakeProperties.addBakeElement bakeMap
obj.INodeBakeProperties.bakeEnabled = on
obj.INodeBakeProperties.flags = 1
obj.INodeBakeProperties.bakeChannel = 1
obj.INodeBakeProperties.nDilations = 0
)
local myBitmap = render rendertype:#bakeSelected vfb:off progressBar:false outputSize:[16,16]
fn renderFrame = (
if (obj != undefined AND obj.name == nodeName) then (
faceValues = #()
selectedbefore = getCurrentSelection()
windows.sendmessage ViewPanelHwnd 11 0 0 -- redraw off
windows.sendmessage ViewPanelHwnd2 11 0 0 -- redraw off
select obj
myBitmap = render rendertype:#bakeSelected vfb:off progressBar:false outputSize:[16,16]
if (selectedbefore != undefined) then ( select selectedbefore )
windows.sendmessage ViewPanelHwnd 11 1 0 -- redraw on
windows.sendmessage ViewPanelHwnd2 11 1 0 -- redraw off
-- this is the first row which has only 6 fields
thisRow = getPixels myBitmap [1,2] 6
for color in thisRow do ( append faceValues color.r )
for row=3 to 16 do (
thisRow = getPixels myBitmap [1,row] 14
for color in thisRow do (
append faceValues color.r
)
)
for y = 1 to 14 do (
for x = 1 to 14 do (
index = (y*x)
if index <= 188 then (
colorValue = faceValues[index]
thisColor = color colorValue colorValue colorValue
-- append colors thisColor
colors = #(thisColor)
setPixels monitor [(x-1),(y-1)] colors
)
)
)
close myBitmap
-- copy myBitmap monitor
display monitor
-- return faceValues
)
)
local theTimer = dotNetObject "System.Windows.Forms.Timer"
fn printTime = (
values = renderFrame()
)
fn stopEverything = (
unregisterRedrawViewsCallback printTime
)
fn exitProgram = (
unregisterRedrawViewsCallback printTime
if (obj != undefined AND obj.name == nodeName) then (
obj.iNodeBakeProperties.removeAllBakeElements()
)
free myBitmap
undisplay monitor
)
-- Create a button
local DotNetButton = DotNetObject "System.Windows.Forms.Button"
DotNetButton.Text = "start"
DotNetButton.Location = (DotNetObject "Drawing.Point" 10 10)
-- Create another button
local DotNetButton2 = DotNetObject "System.Windows.Forms.Button"
DotNetButton2.Text = "stop"
DotNetButton2.Location = (DotNetObject "Drawing.Point" 100 10)
-- The "Click" event calls this function (see below)
fn OnButtonClick s e =(
if (s.Text == "start") then (
registerRedrawViewsCallback printTime
) else (
unregisterRedrawViewsCallback printTime
stopEverything()
)
)
-- Setup an event handler for both buttons
dotNet.addEventHandler DotNetButton "Click" OnButtonClick
dotNet.addEventHandler DotNetButton2 "Click" OnButtonClick
dotNet.addEventHandler DotNetForm "Closing" exitProgram
-- Add the buttons to the form
DotNetForm.Controls.AddRange #(DotNetButton, DotNetButton2)
DotNetForm.TopMost = true
-- Show the form
DotNetForm.Show()
)
this somehow works but If i add or remove a light while it is running it is crashing .
when adding a light it first crashes if I leave the mouse button so while dragging the light into the scene everything still works even with two lights but as soon as i move my finger up (on mouse up event) I get an application error and max crashes
I can use Max normally including any viewport navigation command, add/remove lights, modify the box, add/remove faces etc. and everything updates in real time, with no flickering, not even in the controls. Additionally, I can animate and play the animation while the tools is enabled and the values updates while playing.
The CPU usage keeps at 0 and the FPS does not slow down, which is surprising for a script that captures 100 images per second. I would expect at least a little drop in performance, but nothing changes on my end.
Using the render() function does cast a lot of notifications so I don’t think it is suitable for this task,
Using the Max .Net API should do it better. Here is all you need to implement it:
(
gbl = (dotnetclass "Autodesk.Max.GlobalInterface").Instance
ip = gbl.COREInterface
viewHwnd = dotnetobject "system.intptr" ip.ActiveViewExp.Hwnd
viewExp = ip.GetViewExp viewHwnd
GraphicsWindow = viewExp.Gw
dib = GraphicsWindow.DIB
)
Grab each viewport hwnd and the just get the bitmap of them and find the pixels values. From my last script you can remove all the stuff to switch the active viewport and the redraw messages. With this, you should see no flickering at all. Don’t forget to dispose the returning bitmap once you don’t need it.
cool thanks,
Ufff, I dont really know how to implement the code, did you write it or did you find it somewhere?
I tried to look at the dib data from the code like this but only got black screen:
gbl = (dotnetclass "Autodesk.Max.GlobalInterface").Instance
ip = gbl.COREInterface
viewHwnd = dotnetobject "system.intptr" ip.ActiveViewExp.Hwnd
viewExp = ip.GetViewExp viewHwnd
GraphicsWindow = viewExp.Gw
dib = GraphicsWindow.DIB
testbmp = bitmap 1920 1024
copy dib testbmp
display testbmp
I wrote the code. The bitmap you get is a .Net bitmap so it won’t display with the MXS display() function. You can either copy it to the clipboard (using .Net) and then getting the clipboard image from MXS and display it or use a .Net control to see it.
(
gbl = (dotnetclass "Autodesk.Max.GlobalInterface").Instance
ip = gbl.COREInterface
viewHwnd = dotnetobject "system.intptr" ip.ActiveViewExp.Hwnd
viewExp = ip.GetViewExp viewHwnd
GraphicsWindow = viewExp.Gw
dib = GraphicsWindow.DIB
rollout RO_VIEWER ""
(
dotNetControl dnc_img "PictureBox" pos:[8,8] width:dib.width height:dib.height
on RO_VIEWER open do dnc_img.image = dib
)
createdialog RO_VIEWER width:(dib.width+16) height:(dib.height+16)
)
okay I did the following to see all properties of gbl.COREInterface
local props = getPropNames gbl:COREInterface
print props
but there is nothing else than ActiveViewExp in the list
I dont get how to grab a Viewport handle mh, if I find it I will report it here
___ EDIT
I can somehow get the top view with the following, but I have no idea how to get the other views.
Do you know how to do it?
gbl = (dotnetclass "Autodesk.Max.GlobalInterface").Instance
ip = gbl.COREInterface
viewHwnd = dotnetobject "system.intptr" ip.MAXHWnd -- this here returns the top view, I dont know why.
Thanks! I could somehow manage to get them like this now by try and error
clearListener()
gbl = (dotnetclass "Autodesk.Max.GlobalInterface").Instance
ip = gbl.COREInterface
fn GetLeftCamHWND = ( for j in (windows.getchildrenhwnd #max) where j[5] == "camera_left" do exit with j[1] )
fn GetRightCamHWND = ( for j in (windows.getchildrenhwnd #max) where j[5] == "camera_right" do exit with j[1] )
camLeft = GetLeftCamHWND()
camRight = GetRightCamHWND()
viewHwnd = dotnetobject "system.intptr" camLeft
viewExp = ip.GetViewExp viewHwnd
GraphicsWindow = viewExp.Gw
dib = GraphicsWindow.DIB
rollout RO_VIEWER ""
(
dotNetControl dnc_img "PictureBox" pos:[8,8] width:dib.width height:dib.height
on RO_VIEWER open do dnc_img.image = dib
)
createdialog RO_VIEWER width:(dib.width+16) height:(dib.height+16)
Unfortunately not all SDK is implemented in the .Net API, although it gets better in later Max versions. But in Max 2014 many things do not work.
There are several methods and properties implemented in different interfaces, but I couldn’t get most of them to work.
GetCOREInterface7()->getViewport()
Was deprecated in Max 2013 and getViewExp() doesn’t seem to work in Max 2014.
Here is something I could get to work if you want to stick to the SDK, but there might be other ways.
(
gbl = (dotnetclass "Autodesk.Max.GlobalInterface").Instance
ip = gbl.COREInterface
fn GetCameraViewExp name: =
(
viewPanel = gbl.ViewPanelManager
activePanel = viewPanel.ActiveViewPanel
numViewports = activePanel.NumberOfViewports
for j = 1 to numViewports do
(
ViewExp = activePanel.GetViewExpByIndex (j-1)
ViewType = ViewExp.ViewType.ToString()
if ViewType == "Camera" do
(
if ViewExp.ViewCamera.Name == name do return ViewExp
)
)
return undefined
)
GetCameraViewExp name:"camera_left"
)
Here is a working code using the .net API. You still need to debug and improve it.
The Logarithmic Exposure Control seems to work consistently in viewport and render, but not all the others, so this solution is a partial workaround for what you need. If you switch to another Exposure algorithm, it might not give the values you need.
A better solution would be to implement the Exposure algorithm in the code, or perhaps you can adjust the exposure externally and use something different. After all, it is just a color curve.
(
global GW_DisplayFacesColors
local data_front = #()
local data_back = #()
local data_type = 1
local showSamples = false
fn GW_DisplayFacesColors =
(
for j in data_front do
(
val = case data_type of
(
1: j[2] as point3
2: int j[2].h
3: int j[2].s
4: int j[2].v
)
gw.text j[1] (val as string) color:red
if showSamples do for k in j[3] do gw.Marker k #asterisk color:red
)
for j in data_back do
(
val = case data_type of
(
1: j[2] as point3
2: int j[2].h
3: int j[2].s
4: int j[2].v
)
gw.text j[1] (val as string) color:black
if showSamples do for k in j[3] do gw.Marker k #asterisk color:black
)
gw.enlargeUpdateRect #whole
)
try destroydialog ::RO_DISPLAY_FACES_COLORS catch()
rollout RO_DISPLAY_FACES_COLORS "Faces Colors" width:172 height:172
(
button bt_vp1 "Set Font View" pos:[ 8, 8] width:72 height:32
button bt_vp2 "Set Back View" pos:[88, 8] width:72 height:32
spinner sp_sampleDist "Samples Distance" pos:[ 8,48] range:[0,100,50] fieldwidth:56
checkbox chk_showSamples "Show Smaple Points" pos:[ 8,72]
radioButtons rdo1 "Display:" pos:[8, 96] labels:#("RGB","H","S","B") columns:4
checkbutton btn1 "Enable" pos:[8,136] width:154 height:28
timer clock interval:20 active:false
local GetFaceVerts = polyop.getfaceverts
local GetfaceNormal = polyop.getfaceNormal
local GetFaceCenter = polyop.getFaceCenter
local GetVert = polyop.getvert
local node
local vp_front = undefined
local vp_back = undefined
local gw_front = undefined
local gw_back = undefined
local gbl = (dotnetclass "Autodesk.Max.GlobalInterface").Instance
local ip = gbl.COREInterface
local identityTM = gbl.IdentityTM.Create()
fn ConvertMatrix tm =
(
r1 = tm.GetRow 0
r2 = tm.GetRow 1
r3 = tm.GetRow 2
return (matrix3 [r1.x, r1.y, r1.z] [r2.x, r2.y, r2.z] [r3.x, r3.y, r3.z] [0,0,0])
)
fn CalculateFacesColors viewGW viewTM =
(
dib = viewGW.DIB
tm = viewTM.AffineTM
viewTM = ConvertMatrix tm
viewGW.transform = identityTM
result = for f = 1 to node.numfaces collect
(
faceNormal = normalize (GetfaceNormal node f)
if (faceNormal*viewTM).z > 0 then
(
faceCenter = GetFaceCenter node f
fverts = GetFaceVerts node f
numverts = fverts.count
faceColor = black
samplePoints = #()
for j in fverts do
(
d = distance faceCenter (GetVert node j)
n = normalize (faceCenter - (GetVert node j))
d = sp_sampleDist.value * d / 100.0
pt = faceCenter + (d*n)
pt = gbl.Point3.Create pt.x pt.y pt.z
px = gbl.Point3.Create 0 0 0
viewGW.TransPoint pt px
if px.x > 0 and px.x < dib.width and px.y > 0 and px.y < dib.height do
(
pcolor = dib.GetPixel px.x px.y
if pcolor != undefined then
(
faceColor.r += pcolor.r
faceColor.g += pcolor.g
faceColor.b += pcolor.b
)else(
numverts -= 1
)
)
append samplePoints [pt.x, pt.y, pt.z]
px.Dispose()
pt.Dispose()
)
faceColor.r = int (faceColor.r/numverts)
faceColor.g = int (faceColor.g/numverts)
faceColor.b = int (faceColor.b/numverts)
#(faceCenter, faceColor, samplePoints)
)else(
dontcollect
)
)
tm.Dispose()
dib.Dispose()
return result
)
fn GetDisplayData =
(
data_front = CalculateFacesColors gw_front vp_front
data_back = CalculateFacesColors gw_back vp_back
)
fn GetViewExp =
(
viewHwnd = dotnetobject "system.intptr" ip.ActiveViewExp.Hwnd
return ip.GetViewExp viewHwnd
)
on clock tick do
(
GetDisplayData()
)
on RO_DISPLAY_FACES_COLORS close do
(
unregisterRedrawViewsCallback GW_DisplayFacesColors
completeredraw()
)
on btn1 changed arg do
(
if vp_front == undefined or vp_back == undefined do
(
btn1.checked = false
return messagebox "Select Front and Back Views"
)
unregisterRedrawViewsCallback GW_DisplayFacesColors
if arg then
(
if selection.count == 1 and iskindof selection[1] editable_poly then
(
node = selection[1]
clock.tick()
clock.active = true
registerRedrawViewsCallback GW_DisplayFacesColors
)else(
node = undefined
btn1.checked = false
clock.active = false
messagebox "Select an object"
)
)else(
clock.active = false
)
completeredraw()
)
on rdo1 changed arg do
(
data_type = arg
completeredraw()
)
on bt_vp1 pressed do
(
vp_front = GetViewExp()
gw_front = vp_front.Gw
bt_vp1.text = viewport.activeViewport as string
)
on bt_vp2 pressed do
(
vp_back = GetViewExp()
gw_back = vp_back.Gw
bt_vp2.text = viewport.activeViewport as string
)
on chk_showSamples changed arg do
(
showSamples = arg
GetDisplayData()
completeredraw()
)
on sp_sampleDist changed arg do
(
GetDisplayData()
completeredraw()
)
)
createdialog RO_DISPLAY_FACES_COLORS
)
Merry Christmas everyone!
With the incredible help here I could find a solution with a very small UV map and texture baking -> analyzing which allows me to interact with the viewport, rotate & and move the objects,
getting the default light which changes on the object while rotating around it and so on .
this is the sketch:
clearListener()
global nodeName = "right_perfect"
global obj = getnodebyname nodeName
global DotNetForm
-- Check variable and close window if it exists
try DotNetForm.close() catch()
(
-- Create a form
global DotNetForm = DotNetObject "System.Windows.Forms.Form"
DotNetForm.Size = (DotNetObject "Drawing.Size" 200 80)
global ViewPanelHwnd = 0
fn GetViewportsPanelHWND = ( for j in (windows.getchildrenhwnd #max) where j[4] == "ViewPanel" do exit with j[1] )
ViewPanelHwnd = GetViewportsPanelHWND()
global ViewPanelHwnd2 = 0
fn GetViewportsPanelHWND2 = ( for j in (windows.getchildrenhwnd #max) where j[4] == "RollupPanel" do exit with j[1] )
ViewPanelHwnd2 = GetViewportsPanelHWND2()
global monitor = bitmap 14 14 color:green
display monitor
-- set render environment background to black
execute "backgroundcolor = color 0 0 0"
-- create bakemap to use later
if (obj != undefined AND obj.name == nodeName) then (
local bakeMap = diffusemap()
bakeMap.outputSzX = 16
bakeMap.outputSzY = 16
bakeMap.fileType = ".png"
bakeMap.filterOn = false
bakeMap.shadowsOn = on
bakeMap.lightingon = on
bakeMap.targetMapSlotName = "Diffuse"
bakeMap.enabled = true
obj.INodeBakeProperties.addBakeElement bakeMap
obj.INodeBakeProperties.bakeEnabled = on
obj.INodeBakeProperties.flags = 1
obj.INodeBakeProperties.bakeChannel = 1
obj.INodeBakeProperties.nDilations = 0
)
local myBitmap = render rendertype:#bakeSelected vfb:off progressBar:false outputSize:[16,16]
fn renderFrame = (
if (obj != undefined AND obj.name == nodeName) then (
faceValues = #()
selectedbefore = getCurrentSelection()
windows.sendmessage ViewPanelHwnd 11 0 0 -- redraw off
windows.sendmessage ViewPanelHwnd2 11 0 0 -- redraw off
select obj
myBitmap = render rendertype:#bakeSelected vfb:off progressBar:false outputSize:[16,16]
if (selectedbefore != undefined) then ( select selectedbefore )
windows.sendmessage ViewPanelHwnd 11 1 0 -- redraw on
windows.sendmessage ViewPanelHwnd2 11 1 0 -- redraw off
-- this is the first row which has only 6 fields
thisRow = getPixels myBitmap [1,2] 6
for color in thisRow do ( append faceValues color.r )
for row=3 to 16 do (
thisRow = getPixels myBitmap [1,row] 14
for color in thisRow do (
append faceValues color.r
)
)
for y = 1 to 14 do (
for x = 1 to 14 do (
index = (y*x)
if index <= 188 then (
colorValue = faceValues[index]
thisColor = color colorValue colorValue colorValue
-- append colors thisColor
colors = #(thisColor)
setPixels monitor [(x-1),(y-1)] colors
)
)
)
close myBitmap
-- copy myBitmap monitor
display monitor
)
)
global dnc_control, mouseButtonStates, lButton, mButton, rButton
global mouseState
global numObjects = objects.count -- to store last object count before mouse clicking to check if object is created later
fn printTime = (
mouseButtonStates = dnc_control.MouseButtons.value__
theButtonStr = ""
if (dotnet.CompareEnums mouseButtonStates lButton) then (
--theButtonStr += "Left "
mouseState = "clicked";
if (objects.count > numObjects) then (
-- print "create object"
) else (
values = renderFrame()
)
) else (
mouseState = "not clicked"
values = renderFrame()
numObjects = objects.count
)
--print mouseState
)
fn stopEverything = (
unregisterRedrawViewsCallback printTime
print("stop")
)
fn exitProgram = (
unregisterRedrawViewsCallback printTime
print("exit")
if (obj != undefined AND obj.name == nodeName) then (
obj.iNodeBakeProperties.removeAllBakeElements()
)
free myBitmap
undisplay monitor
)
-- Create a button
local DotNetButton = DotNetObject "System.Windows.Forms.Button"
DotNetButton.Text = "start"
DotNetButton.Location = (DotNetObject "Drawing.Point" 10 10)
-- Create another button
local DotNetButton2 = DotNetObject "System.Windows.Forms.Button"
DotNetButton2.Text = "stop"
DotNetButton2.Location = (DotNetObject "Drawing.Point" 100 10)
-- The "Click" event calls this function (see below)
fn OnButtonClick s e =(
if (s.Text == "start") then (
print("start everything")
registerRedrawViewsCallback printTime
dnc_control = dotNetClass "Control"
mouseButtons = dnc_control.MouseButtons
lButton = mouseButtons.Left.value__
--theTimer.start()
) else (
unregisterRedrawViewsCallback printTime
print("stop everything")
stopEverything()
)
)
-- Setup an event handler for both buttons
dotNet.addEventHandler DotNetButton "Click" OnButtonClick
dotNet.addEventHandler DotNetButton2 "Click" OnButtonClick
dotNet.addEventHandler DotNetForm "Closing" exitProgram
-- Add the buttons to the form
DotNetForm.Controls.AddRange #(DotNetButton, DotNetButton2)
DotNetForm.TopMost = true
-- Show the form
DotNetForm.Show()
)
the last problem to solve is to check avoid rendering while objects are created I could manage to do it for lights like this
global numObjects = objects.count -- to store last object count before mouse clicking to check if object is created later
fn printTime = (
mouseButtonStates = dnc_control.MouseButtons.value__
theButtonStr = ""
if (dotnet.CompareEnums mouseButtonStates lButton) then (
--theButtonStr += "Left "
mouseState = "clicked";
if (objects.count > numObjects) then (
-- print "create object"
) else (
values = renderFrame()
)
) else (
mouseState = "not clicked"
values = renderFrame()
numObjects = objects.count
)
--print mouseState
)
But if i Create a box it does not work because the box creation process is not finished after leaving the mouse.
Does anyone know a property or a way how to find out if a object is in the process of being created?
Thanks so much for the help so far. I hope perhaps the collection of scripts can be helpful to others too.
best wishes!
One little thing is still not working perhaps someone can help me out.
When I create a light while the script is running on every viewport change callback 3ds Max crash as soon as I leave the mouse button…
It seems this happens because leaving the mouse button after dragging in a target spot leads Max to deselect the target/ reselect the light base only. I think if the light creation selecting parts are executed it crashes with the asynchron rendering progress which depends on the selection.
If there just would be a solution to bake a texture be script without selecting it. Everything would be so easy…
__
EDIT :
this is what happens if I leave the left mouse after creating this target spot. I dont understand why the script crashes.
fn printTime = (
mouseButtonStates = dnc_control.MouseButtons.value__
theButtonStr = ""
if (dotnet.CompareEnums mouseButtonStates lButton) then (
mouseState = "clicked";
if (objects.count > numObjects) then (
-- values = renderFrame() <<<<<<<<< if i uncomment this i get the ERROR
) else (
values = renderFrame()
)
) else (
mouseState = "not clicked"
values = renderFrame()
numObjects = objects.count
)
)