CGTalk > Software > Autodesk 3ds max > 3dsMax SDK and MaxScript
Login register
Thread Closed share thread « Previous Thread | Next Thread »  
 
Thread Tools Search this Thread Display Modes
Old 07-13-2008, 08:02 PM   #1
seismograph
Veteran
portfolio
Christian Bauer
Austria
 
Join Date: Aug 2002
Posts: 35
Problems with Scripted simpleobject plugin + mapbutton + animated maptexture

Hi all!

I have a problem with a scripted "Simpleobject" plugin which generates Blinds for a complete fassade and which should control the grade of the opening with a maptexture.

I want to use a noise or gradient map instead a randomseed like in the script i wrote some years ago, which was basicaly based on jon seagull's blind object:
http://www.cgtechniques.com/goodies/blinds/

Here The Problems:
1. There is no drag'n'drop with the mapbutton possible, so i have to load it in the ME with "get material"
2. When i animate the map i don't get any feedback on the mesh, which is basicaly the reason why i want to invest time in rewriting this script. I found a workaround which only works in the viewport and not the renderer: just animate one pluginparam and you will get the animation when using the timeslider. ..dunno why.

I hope someone can help me fixing this errors. ..oh forgot to say it: it would be nice if the script is working also with max4, which i prefer over newer versions

here the code ( i tried to clean it a bit, but it's a script which grows with the time and maybe becomes not easy to read for others ..sorry)

Code:
plugin simpleobject Blinds_test name:"Blinds_test" classID:#(0x6cd7715b, 0x44d977bf) version:1 category:"AEC Extended" ( parameters main rollout:params ( open_map type:#TextureMap ui:ui_open_map open_treshold type:#percent ui:ui_open_treshold default:0 blinds type:#integer ui:ui_blinds default:24 width type:#worldunits ui:ui_width floors type:#integer ui:ui_floors default:6 roomheight type:#worldunits ui:ui_roomheight default:3.2 ) rollout params "Blinds Opening" width:162 height:309 ( mapbutton ui_open_map "<<none>>" width:150 tooltip:"Select Opening Map" spinner ui_open_treshold "treshold: " type:#float scale:1 range:[0,99,0] spinner ui_blinds "# of Blinds: " fieldwidth:45 type:#integer range:[2,60,24] spinner ui_width "Fassade Width:" fieldwidth:45 type:#worldunits range:[0,99999,width] spinner ui_floors "# of floors: " fieldwidth:45 type:#integer range:[2,20,10] spinner ui_roomheight "Height betw. floors:" fieldwidth:45 type:#worldunits range:[0,9999,roomheight] ) on buildmesh do ( local element_width = width/blinds -- this is the width of ONE Element verts=#() faces=#() if open_map != undefined then ( global open_bitmap = renderMap open_map size:[blinds,floors] scale:10.0 --display:true --here i generate a bitmap to read the pixelvalues for each blind ) else ( open_map = gradient() global open_bitmap = renderMap open_map size:[blinds,floors] scale:10.0 --display:true ) -- vertical for e in 1 to floors do ( -- horizontal for i in 1 to blinds do ( rnd = 100-((getpixels open_bitmap [(i-1),(floors)-e] 1)[1].v/255)*100 -- *offset_blind if rnd >= (99-open_treshold) then rnd = 99 ctr = (roomheight*rnd)/100 if ctr <= 0 then ctr = 0 local blindVerts = #( [element_width*(i-1) , 0 , roomheight*(e-1)+ctr], --right bottom xyz [element_width*i , 0 , roomheight*(e-1)+ctr], --left bottom [element_width*(i-1) , 0 , roomheight*e], --rechts top [element_width*i , 0 , roomheight*e]) --left top local fi = verts.count+1 for v in 1 to blindVerts.count do append verts blindVerts[v] local blindfaces = #([fi,(fi+1),(fi+2)],[(fi+3),(fi+2),(fi+1)]) for f in 1 to blindfaces.count do append faces blindfaces[f] )--end for e )--end for i setMesh mesh verts:verts faces:faces meshop.autoedge mesh mesh.edges 3 --autoedge )--end buildmesh tool create ( -- Need to declare ST as local local ST local my_euler = eulerangles 0 0 0 local gdx local gdy on mousePoint click do case click of ( -- Initialize ST variable to record the initial point clicked 1: ( nodeTM.translation = st = gridPoint nodeTM.rotation = quat 0 0 1 0 ) 3:#stop ) -- end MousePoint on mouseMove click do case click of ( 2: ( -- width ------------------------- width = (abs(griddist.x * cos(gridangle.z))) + (abs(griddist.y * sin(gridangle.z))) --rotation ------------------------- my_euler = eulerangles 0 0 gridangle.z if shiftKey then --constrain to cardinal directions ( local grid_z = gridangle.z case of ( (grid_z<=45 and -45<grid_z): my_euler = eulerangles 0 0 0 (45<grid_z and grid_z<=135): my_euler = eulerangles 0 0 90 (135<grid_z or grid_z<=-135): my_euler = eulerangles 0 0 180 (-135<grid_z and grid_z<=-45): my_euler = eulerangles 0 0 270 )--end case )--end if if ctrlKey then --constrains to 15 degree increments ( local index, sign, the_angle local angle_array = #(0,15,30,45,60,75,90,105,120,135,150,165,180) if gridangle.z >= 0 then sign=1 else sign=2 index = floor(((abs(gridangle.z)+15)/15)+.5) the_angle = angle_array[index] case sign of ( 1: my_euler = eulerangles 0 0 the_angle 2: my_euler = eulerangles 0 0 -the_angle )--end case )--end if nodeTM.rotation = my_euler as quat -- fix position ------------------ nodeTM.translation = st ) 3: ( height = abs(distance gridDist [0,0,0]) roomheight = height/floors -- floors = (distance gridDist [0,0,0]) / roomheight ) ) -- end MouseMove ) -- end tool create ) -- end
__________________
[see.]

Last edited by seismograph : 07-13-2008 at 08:14 PM.
 
Old 07-13-2008, 08:13 PM   #2
ZeBoxx2
Lord of the posts
 
ZeBoxx2's Avatar
Richard Annema
Caustic Graphics / SplutterFish
Groningen, Netherlands
 
Join Date: Jul 2006
Posts: 1,461
Quote:
Originally Posted by seismograph
1. There is no drag'n'drop with the mapbutton possible, so i have to load it in the ME with "get material"

That's a MaxScript limitation through to 3ds Max 2009 at least. Can only drag&drop to/from map/material buttons inside scripted materials/maps. It's quite annoying: suggest you add a button to let the user use the currently selected material in the material editor; still not pretty but probably easier than always hunting for it in the material/map browser.
Code:
-- activemeditslot should be new in Max 4 theMat = meditmaterials[activemeditslot]


Quote:
Originally Posted by seismograph
2. When i animate the map i don't get any feedback on the mesh, which is basicaly the reason why i want to invest time in rewriting this script. I found a workaround which only works in the viewport and not the renderer: just animate one pluginparam and you will get the animation when using the timeslider. ..dunno why.

There may be other issues at play, but at least one problem is that rendermap() will always render at the current sliderTime, regardless of currentTime. Using "at time ( rendermap() )" doesn't work, and there's no "renderMap <map> time:N" either. I wrote the following function for a scripted render effect that had the same problem:
Code:
fn bmpRenderMapAtTime map into: size: filename: scale: filter: display: time:currentTime = ( -- change time so that rendermap takes the correct time into account -- store the original settings originalSliderTime = sliderTime originalAnimationRange = animationRange -- and change the time animationRange = interval time (time + 1) sliderTime = time local result = renderMap map into:into size:size filename:filename scale:scale filter:filter display:display -- restore time animationRange = originalAnimationRange sliderTime = originalSliderTime -- return the result result )


Not pretty, but should do the trick. Again, though, there may be other problems with your script as well; can't run it at the moment. I think you need to supply a bitmap for into: (seeing the above code); looks like I never added a check for "if (into == unsupplied) then ( )" Basically the into: is there so that the calling script feeds it a bitmap to renderMap into... that way it doesn't allocate memory for a new bitmap every time it gets called.
 
Old 07-13-2008, 08:27 PM   #3
seismograph
Veteran
portfolio
Christian Bauer
Austria
 
Join Date: Aug 2002
Posts: 35
Hi Richard,

thanks for the info. I read about the limitations of the mapbutton, but thought there is maybe a workaround (there is always one ) . ..it don't really hurts, at least IMHO.

I will try your function and will tell you if it works.
The problem is that i don't understand that i can actually animate the blinds in the Viewport, so the rendermap does work when i change the currenttime, but it doesn't work when i render from frame 0-100 , because max doesn't render the bitmap before it renders the Frame itself.

well, i'm also not happy with the way i access the colorvalue of the map - a bit dirty (also in terms of memory usage).
__________________
[see.]
 
Old 07-13-2008, 09:19 PM   #4
seismograph
Veteran
portfolio
Christian Bauer
Austria
 
Join Date: Aug 2002
Posts: 35
After testing your function i can say that the object still needs to have a key to be set on one of the script params, but it renders at least fine! (why do the script do not run on your machine?)
http://www.cgtechniques.com/~upload...blinds_test.avi
http://www.cgtechniques.com/~upload...linds_test2.avi
http://www.cgtechniques.com/~upload...linds_test3.avi

..BUT there is now another issue when using for example a MaterialByElement modifier, because it seems that the mesh then becomes corrupted and dissapear after some animated frames... and crashes max with endless viewport refreshes.

Is there a way to run parts of the code only while rendering?
__________________
[see.]

Last edited by seismograph : 07-13-2008 at 10:07 PM.
 
Old 07-14-2008, 01:11 PM   #5
PEN
R&D/Technical Director
 
PEN's Avatar
portfolio
Paul Neale
PEN Productions
Canada
 
Join Date: Jun 2002
Posts: 7,416
I"m going to suggest that you bite the bullet and upgrade as Max 4 has many limitations and bugs in systems that have been corrected over the years. Also with the addition of dotNet you could easily create your own drag and drop buttons and tools.
__________________
Paul Neale
http://paulneale.com
 
Old 07-14-2008, 02:03 PM   #6
seismograph
Veteran
portfolio
Christian Bauer
Austria
 
Join Date: Aug 2002
Posts: 35
the max version is not the problem and to be honest, if i would need dotnet for a script i wouldn't waste my time on writing a script - i would instead write a plugin.
anyway..

I not really have fixed all issues, but i'm now able to render the animation. ...still with the workaround of setting an animationkey (which seems to force the script to rebuild the mesh with every frame)

The problem with crashing max was maybe the sliderTime behave of max, so i disabled the screenrefresh.

I attached a scene for you to have a look at it, because i still hope that someone can fix this stupid animated maptexture bug.
(it's btw a max2008 scene)



Code:
plugin simpleobject Blinds_test name:"Blinds_test" classID:#(0x6cd7715b, 0x44d977bf) version:1 category:"AEC Extended" ( parameters main rollout:params ( open_map type:#TextureMap ui:ui_open_map animation:true open_treshold type:#percent ui:ui_open_treshold default:0 animation:true blinds type:#integer ui:ui_blinds default:24 width type:#worldunits ui:ui_width floors type:#integer ui:ui_floors default:6 roomheight type:#worldunits ui:ui_roomheight default:3.2 ) rollout params "Blinds Opening" width:162 height:309 ( mapbutton ui_open_map "<<none>>" width:150 tooltip:"Select Opening Map" spinner ui_open_treshold "treshold: " type:#float scale:1 range:[0,99,0] spinner ui_blinds "# of Blinds: " fieldwidth:45 type:#integer range:[2,60,24] spinner ui_width "Fassade Width:" fieldwidth:45 type:#worldunits range:[0,99999,width] spinner ui_floors "# of floors: " fieldwidth:45 type:#integer range:[2,20,10] spinner ui_roomheight "Height betw. floors:" fieldwidth:45 type:#worldunits range:[0,9999,roomheight] ) on buildmesh do ( local element_width = width/blinds -- this is the width of ONE Element verts=#() faces=#() if open_map == undefined then ( open_map = gradient() ) with redraw off ( --richard originalSliderTime = sliderTime originalAnimationRange = animationRange -- and change the time animationRange = interval currentTime (currentTime + 1) sliderTime = currentTime local open_bitmap = renderMap open_map size:[blinds,floors] scale:10.0 --display:true -- restore time animationRange = originalAnimationRange sliderTime = originalSliderTime ) -- vertical for e in 1 to floors do ( -- horizontal for i in 1 to blinds do ( rnd = 100-((getpixels open_bitmap [(i-1),(floors)-e] 1)[1].v/255)*100 -- *offset_blind if rnd >= (99-open_treshold) then rnd = 99 ctr = (roomheight*rnd)/100 if ctr <= 0 then ctr = 0 /* --classic blinds : up down local blindVerts = #( [element_width*(i-1) , 0 , roomheight*(e-1)+ctr], --right bottom xyz [element_width*i , 0 , roomheight*(e-1)+ctr], --left bottom [element_width*(i-1) , 0 , roomheight*e], --rechts top [element_width*i , 0 , roomheight*e]) --left top */ -- windows which rotates local blindVerts = #( [element_width*(i-1) , 0 , roomheight*(e-1)], --right bottom xyz [element_width*i , 0 , roomheight*(e-1)], --left bottom [element_width*(i-1) , (ctr/2) , roomheight*e], --rechts top [element_width*i , (ctr/2) , roomheight*e]) --left top local fi = verts.count+1 for v in 1 to blindVerts.count do append verts blindVerts[v] local blindfaces = #([fi,(fi+1),(fi+2)],[(fi+3),(fi+2),(fi+1)]) for f in 1 to blindfaces.count do append faces blindfaces[f] )--end for e )--end for i setMesh mesh verts:verts faces:faces meshop.autoedge mesh mesh.edges 3 --autoedge )--end buildmesh tool create ( -- Need to declare ST as local local ST local my_euler = eulerangles 0 0 0 local gdx local gdy on mousePoint click do case click of ( -- Initialize ST variable to record the initial point clicked 1: ( nodeTM.translation = st = gridPoint nodeTM.rotation = quat 0 0 1 0 ) 3:#stop ) -- end MousePoint on mouseMove click do case click of ( 2: ( -- width ------------------------- width = (abs(griddist.x * cos(gridangle.z))) + (abs(griddist.y * sin(gridangle.z))) --rotation ------------------------- my_euler = eulerangles 0 0 gridangle.z if shiftKey then --constrain to cardinal directions ( local grid_z = gridangle.z case of ( (grid_z<=45 and -45<grid_z): my_euler = eulerangles 0 0 0 (45<grid_z and grid_z<=135): my_euler = eulerangles 0 0 90 (135<grid_z or grid_z<=-135): my_euler = eulerangles 0 0 180 (-135<grid_z and grid_z<=-45): my_euler = eulerangles 0 0 270 )--end case )--end if if ctrlKey then --constrains to 15 degree increments ( local index, sign, the_angle local angle_array = #(0,15,30,45,60,75,90,105,120,135,150,165,180) if gridangle.z >= 0 then sign=1 else sign=2 index = floor(((abs(gridangle.z)+15)/15)+.5) the_angle = angle_array[index] case sign of ( 1: my_euler = eulerangles 0 0 the_angle 2: my_euler = eulerangles 0 0 -the_angle )--end case )--end if nodeTM.rotation = my_euler as quat -- fix position ------------------ nodeTM.translation = st ) 3: ( height = abs(distance gridDist [0,0,0]) roomheight = height/floors -- floors = (distance gridDist [0,0,0]) / roomheight ) ) -- end MouseMove ) -- end tool create ) -- end
Attached Images
File Type: png blinds.png (16.7 KB, 21 views)
Attached Files
File Type: zip blinds_test.zip (28.1 KB, 4 views)
__________________
[see.]
 
Old 07-14-2008, 04:38 PM   #7
seismograph
Veteran
portfolio
Christian Bauer
Austria
 
Join Date: Aug 2002
Posts: 35
Problems Solved!

Hi all!

I finaly found another workaround: i added a dummy param which gets an float_script controller. Now max knows that it have to update the mesh with every Frame. Together with the sliderTime + refresh off workaround it works pretty nice.

Thanks All!

There are many other ways how a "dynamic fassade" can be created, but i think this is now a pretty easy way.
Here the basic Script, which can switch between blinds and tilted windows.


Code:
plugin simpleobject Blinds_test name:"Blinds_test" classID:#(0x6cd7715b, 0x44d977bf) version:1 category:"AEC Extended" ( parameters main rollout:params ( type type:#integer ui:ui_type default:1 open_map type:#TextureMap ui:ui_open_map animation:true subanim:true open_treshold type:#percent ui:ui_open_treshold default:0 animation:true blinds type:#integer ui:ui_blinds default:24 width type:#worldunits ui:ui_width floors type:#integer ui:ui_floors default:6 roomheight type:#worldunits ui:ui_roomheight default:3.2 -- part1 for the animating texture workaround : we add a controller to a dummyparameter anim_helper type:#float animation:true ) rollout params "Blinds Opening" width:162 height:309 ( radiobuttons ui_type labels:#("Blinds", "Tilted Windows") default:1 mapbutton ui_open_map "<<none>>" width:110 tooltip:"Select Opening Map" across:2 offset:[15,0] button ui_send_map ">>ME" width:30 tooltip:"Send material to active Material editor Slot" offset:[20,0] spinner ui_open_treshold "treshold: " type:#float scale:1 range:[0,99,0] spinner ui_blinds "# of Blinds: " fieldwidth:45 type:#integer range:[2,60,24] spinner ui_width "Fassade Width:" fieldwidth:45 type:#worldunits range:[0,99999,width] spinner ui_floors "# of floors: " fieldwidth:45 type:#integer range:[2,20,10] spinner ui_roomheight "Height betw. floors:" fieldwidth:45 type:#worldunits range:[0,9999,roomheight] on ui_send_map pressed do meditmaterials[activemeditslot] = open_map ) on buildmesh do ( local element_width = width/blinds -- this is the width of ONE Element verts=#() faces=#() if open_map == undefined then ( open_map = gradient() ) with redraw off ( --richard originalSliderTime = sliderTime originalAnimationRange = animationRange -- and change the time animationRange = interval currentTime (currentTime + 1) sliderTime = currentTime local open_bitmap = renderMap open_map size:[blinds,floors] scale:10.0 --display:true -- restore time animationRange = originalAnimationRange sliderTime = originalSliderTime ) -- vertical for e in 1 to floors do ( -- horizontal for i in 1 to blinds do ( rnd = 100-((getpixels open_bitmap [(i-1),(floors)-e] 1)[1].v/255)*100 -- *offset_blind if rnd >= (99-open_treshold) then rnd = 99 ctr = (roomheight*rnd)/100 if ctr <= 0 then ctr = 0 if type == 1 then ( --classic blinds : up down local blindVerts = #( [element_width*(i-1) , 0 , roomheight*(e-1)+ctr], --right bottom xyz [element_width*i , 0 , roomheight*(e-1)+ctr], --left bottom [element_width*(i-1) , 0 , roomheight*e], --rechts top [element_width*i , 0 , roomheight*e]) --left top ) else ( -- windows which rotates local blindVerts = #( [element_width*(i-1) , 0 , roomheight*(e-1)], --right bottom xyz [element_width*i , 0 , roomheight*(e-1)], --left bottom [element_width*(i-1) , (ctr/2) , roomheight*e], --rechts top [element_width*i , (ctr/2) , roomheight*e]) --left top ) local fi = verts.count+1 for v in 1 to blindVerts.count do append verts blindVerts[v] local blindfaces = #([fi,(fi+1),(fi+2)],[(fi+3),(fi+2),(fi+1)]) for f in 1 to blindfaces.count do append faces blindfaces[f] )--end for e )--end for i setMesh mesh verts:verts faces:faces meshop.autoedge mesh mesh.edges 3 --autoedge )--end buildmesh -- part2 for the animating texture workaround on Create do anim_helper.controller=float_script() tool create ( -- Need to declare ST as local local ST local my_euler = eulerangles 0 0 0 local gdx local gdy on mousePoint click do case click of ( -- Initialize ST variable to record the initial point clicked 1: ( nodeTM.translation = st = gridPoint nodeTM.rotation = quat 0 0 1 0 ) 3:#stop ) -- end MousePoint on mouseMove click do case click of ( 2: ( -- width ------------------------- width = (abs(griddist.x * cos(gridangle.z))) + (abs(griddist.y * sin(gridangle.z))) --rotation ------------------------- my_euler = eulerangles 0 0 gridangle.z if shiftKey then --constrain to cardinal directions ( local grid_z = gridangle.z case of ( (grid_z<=45 and -45<grid_z): my_euler = eulerangles 0 0 0 (45<grid_z and grid_z<=135): my_euler = eulerangles 0 0 90 (135<grid_z or grid_z<=-135): my_euler = eulerangles 0 0 180 (-135<grid_z and grid_z<=-45): my_euler = eulerangles 0 0 270 )--end case )--end if if ctrlKey then --constrains to 15 degree increments ( local index, sign, the_angle local angle_array = #(0,15,30,45,60,75,90,105,120,135,150,165,180) if gridangle.z >= 0 then sign=1 else sign=2 index = floor(((abs(gridangle.z)+15)/15)+.5) the_angle = angle_array[index] case sign of ( 1: my_euler = eulerangles 0 0 the_angle 2: my_euler = eulerangles 0 0 -the_angle )--end case )--end if nodeTM.rotation = my_euler as quat -- fix position ------------------ nodeTM.translation = st ) 3: ( height = abs(distance gridDist [0,0,0]) roomheight = height/floors -- floors = (distance gridDist [0,0,0]) / roomheight ) ) -- end MouseMove ) -- end tool create ) -- end
Attached Images
File Type: png blinds_ok.png (16.9 KB, 15 views)
__________________
[see.]
 
Old 07-14-2008, 04:49 PM   #8
PEN
R&D/Technical Director
 
PEN's Avatar
portfolio
Paul Neale
PEN Productions
Canada
 
Join Date: Jun 2002
Posts: 7,416
You can also go with a callback system or you can use the parameter event handlers. So in the param block you can have on theParam get val do. Be careful with this as it is called when the file loads, if there are no changes to make it is still making them. To get around this I implemented a and oldParam and test that against the param that I'm changing. If they are different it updates.
__________________
Paul Neale
http://paulneale.com
 
Old 07-14-2008, 04:51 PM   #9
ZeBoxx2
Lord of the posts
 
ZeBoxx2's Avatar
Richard Annema
Caustic Graphics / SplutterFish
Groningen, Netherlands
 
Join Date: Jul 2006
Posts: 1,461
Quote:
Originally Posted by seismograph
I finaly found another workaround: i added a dummy param which gets an float_script controller. Now max knows that it have to update the mesh with every Frame.

Back late to the party, but yep... that's what I've got in one of our scripts as well;

Code:
parameters ... ( _autorefresh_donotmodify type:#float ) on attachedToNode n do ( n._autorefresh_donotmodify.controller = float_script() n._autorefresh_donotmodify.controller.script = "-- script controller to force mesh refresh. Do not remove.\n1.0" )


Then a lot of the actual code on the simple object is handled in the "on _autorefresh_donotmodify get" handler.

Maybe this thread will spawn a cleaner method
 
Old 07-14-2008, 04:51 PM   #10
CGTalk Moderation
Lord of the posts
CGTalk Forum Leader
 
Join Date: Sep 2003
Posts: 1,066,481
Thread automatically closed

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
__________________
CGTalk Policy/Legalities
Note that as CGTalk Members, you agree to the terms and conditions of using this website.
 
Thread Closed share thread


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 11:21 PM.


Powered by vBulletin
Copyright ©2000 - 2016, Jelsoft Enterprises Ltd.