Viewport Drawing Methods

The methods documented in this topic provide low-level access to 3ds max's graphics system. These methods are available for scripts to do any graphics work not possible using the standard high-level graphics methods.

These methods are for use in the existing 3ds max viewports, and only work on the active viewport. Note that these methods typically are not for casual use, as they are not intended to be a high level graphics library. For example, many steps are required to display a single lit polygon. These methods are optimized for speed, and not at all for script programmer ease of use.

The methods described in this topic actually operate on graphic windows. While graphic windows are not the same as viewports, they are related to one another. Each viewport has its own graphics window, and the contents of the viewport display can be thought of as a snapshot of the graphics window contents. Since writing to display memory a relatively slow operation, 3ds max writes instead to the graphics window, and then when all the writes have been finished, it redraws the viewports with the graphics window contents.

Graphics Driver Configuration and Support Methods

gw.getDriverString()

This method returns a string identifying the graphics driver (and includes manufacturer info if available)

gw.querySupport <feature_name>

Determines if the driver supports the specified feature. The valid <feature_name> values are:

#txtCorrect

This is used to enable or gray-out the perspective correction right-click viewport menu option.

#geomAccel

This is used to indicate to 3ds max (and the mesh class in particular) that the driver wants to handle all of the 3D data natively. In this case, meshes are rendered by passing 3D world space data and letting the driver transform, clip, and light the vertices. If this returns false, then the mesh class handles all transforms, clipping and lighting calculations and then calls the hPolygon or hPolyline 2 1/2D calls for the driver to rasterize. (Primitives that are actually clipped are still sent to the polygon/polyline methods.)

Currently, only the OpenGL driver returns true to this query, but other drivers have been developed that return true, and the HEIDI and D3D drivers may change in the future.

#triStrips

If this returns true, then 3ds max will try to stripify meshes before calling the rendering methods. Currently, the drivers just return the user preference that is set in the driver configuration dialog. This preference defaults to true.

#dualPlanes

If a driver has dual-planes support it returns true. The standard 3ds max OpenGL display driver only returns true for this if the underlying display driver has implemented a custom OpenGL extension that allows 3ds max to handle this efficiently.

#swapModel

This returns true if 3ds max has to redraw the whole scene any time the viewports are exposed.

#incrUpdate

This returns true if the driver can update a rectangular subset of the viewport without trashing the image outside that rectangle. This is true for most drivers that blit the viewport region and false for those that do page-flipping in the hardware. For OpenGL, this is true if the display driver implements the Microsoft glSwapRectHintWIN extension.

#passDecal

This is true if the driver can handle decalling with only one pass. Currently, this is true for OpenGL, but false for HEIDI and D3D.

#driverConfig

This is true if the driver has a configuration dialog. This is true for all three of 3ds max's standard drivers.

#texturedBkg

This is true if the viewport background is implemented as a textured rectangle, and false if it is a blitted bitmap.

#virtualVpts

This is true if the driver allows viewports to be made larger than the physical window they are attached to. Currently, this is only true for OpenGL.

#paintDoesBlit

This is true if WM_PAINT messages result in a blit of the backbuffer (as opposed to a page-flipping swap). This allows 3ds max to do quick damage region repair, and works together with the #swapModel flag.

#wireframeStrips

This is true if the driver wants 3ds max to send down wireframe models using triangle strips instead of a bundle of 2-point segments. This is only used by the OpenGL driver, and it is there as a user-choosable performance-accuracy tradeoff (since the strips are faster and are back-culled, but they display hidden edges as though they are visible).

Window and Viewport Transformation Methods

gw.isPerspectiveView()

Returns true if the view is in perspective projection; otherwise false (orthographic projection).

gw.setTransform <matrix3>

Sets the active viewport's graphics window transformation matrix to the specified matrix3 value, and updates the modeling coordinates to normalized projection coordinates matrix. This routine also back-transforms each light and the eye point so that lighting can be done in modeling coordinates.

This method may be used to set a matrix that transforms the point passed to the drawing methods (like gw.text(), gw.marker(), gw.polyline() or gw.polygon()). Normally these methods expect world coordinates. However if this matrix is set to an object's transformation matrix you can pass coordinates in the object's space coordinates and they will be transformed into world space (and then put into screen space when they are drawn). If however this is set to the identity matrix, you would pass world space coordinates. You can set this matrix to the object's transform matrix using the following code:

gw.setTransform node.transform

For world-to-screen space conversions by the methods gw.text(), gw.marker(), gw.polyline() or gw.polygon(), etc, a developer must explicitly set this matrix to the identity matrix. This is because the graphics window may have a non-identity transform matrix already in place from a previous operation.

gw.getFlipped()

Returns true if the determinant of the current transform is positive, false if negative.

Position, Size, and Depth Clipping Methods

gw.setPos <x_integer> <y_integer> <w_integer> <h_integer>

Sets the size and position of the graphics window. The coordinates are all Windows coordinates in the space of the graphics windows' parent window. All coordinates are in Windows format, with the origin in the upper left.

x - Specifies the left graphics window origin; y - Specifies the top graphics window origin; w - Specifies the graphics window width; h - Specifies the graphics window height.

gw.getWinSizeX()

This method gets the current window size in X.

gw.getWinSizeY()

This method gets the current window size in Y.

gw.getWinDepth()

This method returns the z-buffer depth (in bits). Note: this method does not return the proper Z depth value in 3ds max and VIZ R3.

gw.getHitherCoord()

This method returns the largest device Z value.

gw.getYonCoord()

This method returns the smallest device Z value. Note: this method does not return the proper Z depth value in 3ds max and VIZ R3.

DIB (Device-Independent Bitmap) Methods

gw.getViewportDib()

This method returns the active viewport's graphics window image as a Bitmap value. The size of the bitmap is the same size as the viewport.

Example:

MacroScript GrabViewport category:"Tools" tooltip:"Grab Viewport"

(

---------------------------------------------------------------------

--GRABVIEWPORT MACROSCRIPT

--Created:3/23/99

--Edited:4/28/99

--by Borislav Petrov

--bobo@email.archlab.tuwien.ac.at

---------------------------------------------------------------------

--Snapshot Active Viewport to Bitmap

--Filename in format:

--VPGRAB_MaxFileName_ViewportName_CurentFrame.ImageFormatExtension

---------------------------------------------------------------------

--

--Init Variables

local grab_bmp          --bitmap to put the snapshot into

local bmp_name          --name of the bitmap to save

local get_viewport_name --viewport name

local gac,gct,mfn       --variables to hold ActiveCamera, CurrentTime, MaxFileName

--

--Start Macro

grab_bmp = gw.getViewportDib()          --get the viewport bitmap into variable

get_viewport_name = viewport.GetType()  --get viewport name

gvn = get_viewport_name as string       --convert viewport name to string

gvn = substring gvn 6 (gvn.count-5)     --cut the string

if gvn == "camera" then      --if the remaining string is "camera"

(  

gac = getActiveCamera() --get the camera

gvn = gac.name --get the name of the camera

)

mfn = MaxFileName            --get max file name

if mfn == "" then            --if there is no name

mfn = "Untitled" --use "Untitled"

else

mfn = getFileNameFile mfn --use the file name without .MAX extension

gct = SliderTime as string   --get current frame time

--

bmp_name = "VPGRAB_"+ mfn +"_" +gvn + "_" + gct --build the output file name

--

--Display file save dialog

bmp_name = getSaveFileName caption:"Save Viewport to:" filename:bmp_name \

types:"BMP(*.bmp)|*.bmp|TIFF(*.tif)|*.tif|JPG(*.jpg)|*.jpg|TGA(*.tga)|*.tga|"

--

if bmp_name != undefined then   --if user has confirmed / entered a valid name

(  

grab_bmp.filename = bmp_name --set output name to the one entered in the save file dialog

save grab_bmp --save the bitmap

display grab_bmp --display the bitmap in a VFB

)

--

)--end of script

Drawing Setup

gw.resetUpdateRect()

This method resets the update rectangle. The update rectangle is the region of the screen that needs to be updated to reflect items that have changed. When the system is done rendering items, the goal is to only update the region of the viewport that has actually been altered. This method sets the update rectangle (the region that will be blitted to the display) to a special "empty" value. This way when gw.enlargeUpdateRect() is later called, the Box2 region passed will be used as the region.

gw.enlargeUpdateRect ( <Box2> | #whole )

This method enlarges the update rectangle to include the Box2 value passed. If #whole is specified, the whole viewport will later be updated

gw.getUpdateRect()

This method retrieves the current update rectangle as a Box2 value. Returns a special an Box2 "empty" value if no region of the viewport needs to be updated.

gw.setRndLimits <render_limits_name_array>

Sets the rendering limits used by primitive calls. Setting the rendering limits is used in communication between the various parts of 3ds max that handle the display of objects. For example, setting this limit to #polyEdges and then drawing a polygon won't result in a polygon drawn with edges. It only sets a flag that indicates the edge should be drawn.

What happens is as follows. Inside the upper level 3ds max, part of the code knows that polygon edges have been turned on. However this is not related through the object oriented architecture to the part of 3ds max that does the actual drawing. When 3ds max goes to draw objects it will see that the polygon edge flag is on. This tells it to do two drawing passes -- one to draw the polygon, then it calls outlinePass() call with true, draws a bunch of edges, then calls outlinePass() with false. Thus, the drawing routine is responsible for looking at the flags and drawing appropriately. This method is only responsible setting the limit which can later be checked.

<render_limits_name_array> specifies the rendering limits used by the viewport as an array of <render_limits_name> values. The valid <render_limits_name> values are:

#allEdges

All edges of the item are shown (including hidden ones).

#boxMode

Objects are shown using their bounding box.

#backcull

Backface culling is used. Entities whose surface normal face away from the view direction are not drawn.

#colorVerts

This turns on color-per-vertex display.

#flat

Flat (facet) shading mode.

#illum

This indicates that you have colors per vertex in your polygons and that they should be used. If you had colors per vertex but this flag was not set, the colors would be ignored.

#lighting

This is the same as setting #illum and #specular.

#noAtts

No attributes are specified.

#perspCorrect

In this mode textures are corrected for perspective display.

#pick

This indicates hit testing will be performed (not rendering).

#polyEdges

This mode causes polygon edges (Edged Faces) to be on.

#shadeCverts

This modifies #colorVerts. If set, lighting is enabled and the vertex colors are used to modulate the colors that result from lighting. If off, the colors on each vertex are used directly to shade the triangle. When 3ds max uses #shadeCverts mode, it puts a white diffuse-only material on the object so that it appears that the colors are shaded without distortion.

Described further, when #shadeCverts is OFF, then the vertex colors are used directly. This is equivalent to saying that they are modulated by a pure white self-illuminated material.

When #shadeCverts is ON, the diffuse white material is illuminated by the scene lighting, resulting in shades ranging from black to white, with most vertices being some shade of pure gray. When the vertex colors are modulated by the material color, they get multiplied (in general) by a number less than 1, which makes them appear darker.

The RGB components of the colors are modulated uniformly, so that there is no shift from, say, red to green. That would happen if the underlying material was not evenly weighted (that is, a pure gray lying between black and white). Said another way, only the intensity of the vertex colors is changed when shading is on, not luminance, chrominance, etc.

#specular

This enables specular highlight display.

#texture

This enables texture display.

#twoSided

Faces are displayed regardless of their surface normal orientation.

#vertTicks

This mode is really a pseudo-mode, in that it doesn't actually cause the graphics drivers to do anything differently, but rather is tested by the Mesh class, which sends down vertex markers (+) if the mode is on.

#wireframe

Wireframe rendering mode.

#zBuffer

When coordinates are specified for drawing primitives they have x, y, and z values. Sometimes when drawing entities in the viewports it is desirable to ignore the z values. For example in the 3ds max viewports the text that display the type of viewport (Front, Left, and so on) are drawn without z values. So are the arc-rotate circle control and the axis tripods. These items are drawn without this flag being set so they always show up in front.

gw.getRndLimits()

Retrieves the rendering limits used by primitive calls as an array of names. See gw.setRndLimits() for a list of the returned name values.

gw.getRndMode()

Returns the current rendering mode used by the viewport as an array of names. This is a subset of the rendering limit, in that any limits imposed by the rendering limit are forced onto the current mode. See gw.setRndLimits() for a list of the returned name values.

gw.setSkipCount <skip_count_integer>

Sets the number of triangles skipped when the viewport is set as a 'Fast View Display' viewport. To disable fastview, specify 1. Since triangles are handed down to graphics driver one at a time, it is up to the code that feeds triangles to the graphics driver to skip the specified number of triangles. The mesh rendering in 3ds max uses the skip count in this way.

<skip_count_integer> specifies that every 'n-th' triangle should be drawn. If set to 2, every other triangle should be drawn.

gw.getSkipCount()

Returns the current skip count setting.

The following is an example of using the above functions

-- Get the current rendering limits

lim = gw.getRndLimits()

-- Add another limit

append lim #polyEdges

-- Set the new rendering limits

gw.setRndLimits lim

-- Get back the rendering limits to check

gw.getRndLimits()

-- Get back the rendering mode to check

gw.getRndMode()

Light and Camera Methods

gw.getMaxLights()

Returns the maximum number of lights that may be used by the interactive renderer.

Coordinate Transformation Methods

The following methods map points from the graphic window's current transform to device space. If the graphic window's transform is set to the identity matrix then the mapping is done from points specified in world space. Otherwise the points given are transformed by the graphic window's transform, and are then considered to be in world space. Thus, to get a world-space to screen-space conversion, you need to set the graphic window's transform to the identity with:

gw.setTransform(Matrix3 1)

gw.hTransPoint <point3>

This method converts the point3 coordinate to a "h" format device coordinate. Each component of the return value is in integer format in the native device coordinates for the graphics driver. For HEIDI and OpenGL, the origin is at the lower left. For Direct3D the origin is at the upper left.

gw.wTransPoint <point3>

This method converts the point3 coordinate to a "w" format device coordinate. Each component of the return value is in integer format with the origin at the upper left.

gw.transPoint <point3>

This method converts the point3 coordinate to a "h" floating point coordinate. Each component of the return value is in float format with the origin at the upper left. This is just a helper routine to avoid building up round-off error. 3ds max uses it just for IK.

Drawing Methods

Methods that start with "h" take integer device coordinates with the origin at the lower-left. Methods that start with "w" in front take Windows device coordinates with the origin at the upper left. These "h" and "w" routines perform NO clipping unless otherwise noted. Drawing outside the allowable region is likely to cause 3ds max to crash. These coordinate systems are left-handed.

Methods that don't start with "h" or "w" map points from the graphic window's current transform to device space. This coordinate system is right-handed. If the graphic window's transform is set to the identity matrix then the mapping is done from points specified in world space. Otherwise the points given are transformed by the graphic window's transform, and are then considered to be in world space. Thus, to get a world-space to screen-space conversion, you need to set the graphic window's transform to the identity with:

gw.setTransform(Matrix3 1)

After completing any drawing to the graphics window with the methods described in this section, you need to call gw.updateScreen() to update the viewport display.

gw.updateScreen()

Updates the viewport display to display any text, markers, polylines, polygons, or tristrips written to the graphics window via the methods described below.

gw.text  <point3> <string> [ color:<color> ]

gw.hText <point3> <string> [ color:<color> ]

gw.wText <point3> <string> [ color:<color> ]

Draws 2D fixed font annotation string text to the specified location using the specified (optional) color. If the color is not specified, a default color of red is used.

Note: This routine DOES perform clipping of the text if it is off the screen.

gw.Marker  <point3> <marker_name> [ color:<color> ]

gw.hMarker <point3> <marker_name> [ color:<color> ]

gw.wMarker <point3> <marker_name> [ color:<color> ]

Draws a marker at the specified location. This is can be paired with pickpoint() to quickly show where the user has clicked. These markers are temporary and will be erased whenever the viewports are updated. If the color is not specified, a default color of red is used. The valid <marker_name> types are:

#point

#hollowBox

#plusSign

#asterisk

#xMarker

#bigBox

#circle

#triangle

#diamond

#smallHollowBox

#smallCircle

#smallTriangle

#smallDiamond

The following is an example that creates an instance of all types of markers, spaced equally:

arr = #("point","hollowBox","plusSign","asterisk","xMarker",

"bigBox","circle","triangle","diamond","smallHollowBox",

"smallCircle","smallTriangle","smallDiamond" )

for i=1 to arr.count do

gw.hMarker [100, (50 + i*10), 50](arr[i] as name)

gw.enlargeUpdateRect #whole

gw.updateScreen()

gw.Polyline <vertex_point3_array> <isClosed_boolean> \

            [ rgb:<color_array> ]

gw.hPolyline <vertex_point3_array> <isClosed_boolean> \

             [ rgb:<color_array> ]

gw.wPolyline <vertex_point3_array> <isClosed_boolean> \

             [ rgb:<color_array> ]

This method draws a multi-segment polyline. Each value in <vertex_point3_array> is a vertex on the polyline. If <isClosed_boolean> is true, the first point is connected to the last point, that is, the polyline is closed. If false, the polyline is left open. If the optional rgb color array is specified, and shade mode is set to smooth, the polyline will be drawn Gourand shaded. This is how 3ds max draws lit wireframes for instance. If the optional rgb color array is not specified, the line is drawn with the line color specified via gw.setColor(). The number of elements in <color_array> must be the same as in <vertex_point3_array>.

gw.Polygon <vertex_point3_array> <color_array> \

           <uvw_point3_array>

gw.hPolygon <vertex_point3_array> <color_array> \

            <uvw_point3_array>

gw.wPolygon <vertex_point3_array> <color_array> \

            <uvw_point3_array>

This method draws a multi-point polygon. Each value in <vertex_point3_array> is a vertex on the polygon. <color_array> specifies the color at each vertex. The rendering mode (set via gw.setRndLimits()) must include #illum for the color values to be used. <uvw_point3_array> specifies the UVW coordinates at each. The rendering mode must include #texture for the UVW coordinates to be used. The number of elements in each array must be identical.

gw.hTriStrip <vertex_point3_array> <color_array> \

             <uvw_point3_array>

gw.wTriStrip <vertex_point3_array> <color_array> \

             <uvw_point3_array>

This method is used for drawing a series of triangles specified as 'strips'. It takes a count of 3 or more, and builds triangles in a strip. This sends a lot less data and the underlying graphics library has to set up a lot less data since it can use the previous information to start the rasterization. This results in a significant speed increase. This routine does no clipping so all the vertices passed must be within view. The parameters are the same as for the gw.Polygon(). However, how the <vertex_point3_array> is handled is different. After the first two vertices, each new vertex is used to create a new triangle. For instance, to draw a quad, the first three vertices specify the first triangle and the next one is combined with the previous two to complete the square

The following is an example of using the above functions:

Example:

-- Draw some primitives

gw.hPolyline #([300,50,16], [300,200,8], [450,250,4]) true

--

gw.hPolygon #([200,100,16], [280,100,8], [250,200,4]) \

            #(red, blue, green) \

            #([1.0,.5,0], [0.5,0.5,0], [0,0,0.5])

--

gw.hTriStrip #([50,50,0], [175,100,0], [25,100,0], [150,250,0]) \

             #(red, blue, green, white) \

             #([1.0,.5,0], [0.5,0.5,0], [0,0,0.5], [0.5,1,0])

--

-- Update the viewports

gw.enlargeUpdateRect #whole

gw.updateScreen()

gw.setColor <type_name> <color_value>

Sets the RGB color used for the specified drawing type. The valid <type_name> values are:

#line -- line drawing color

#fill -- polygon fill color

#text -- text drawing color

#clear -- The color that the viewport is cleared to

when you call gw.clearScreen()

gw.clearScreen <Box2> [ useBkg:<boolean> ]

Clears the specified rectangular region of the screen. If the optional useBkg parameter is set to false, the region is set to the "clear" color (see gw.setColor() above). If true, the background should be used to fill the cleared area. The default useBkg value is false.