Documentation Papyrus make3do Script Language Reference

Note: This is an official document that was provided by Papyrus. It is available when you install Sandbox in the docs folder it generates. Admittedly its not well known and many users most likely missed it. It provides valuable info on scripting language make3do uses to write your own scripts with make3do psgs. The documentation has been reformatted for this article and organization. Any text below in blue is additional context I have added which is not included in the original document.
Note: This is not a tutorial but more documentation. We will go over in great detail in other articles how to use this scripting code effectively. This documentation is however useful for those able to jump in with minimal guidance and just needed to know the code states to use and a little context to go with them.
Papyrus Scene Graph (PSG) Script Language Reference (MAKE3DO.EXE)

General Info

A meshGroupIdentifier, linkedMeshIdentifier, or a meshIdentifier can be used wherever a sceneIdentifier can be used.

This means after you identify your imported mesh you can use the GROUP, LINK or MESH command

Stuff in [] is optional. Stuff in {} means pick one.

This mean any variables with in brackets doesn't need to be used in your script line. The brace brackets means you must choose one of the options in this document in your script or it won't resolve properly. You will see [] and {} in several example below.

Command Reference


The MESH_GROUP command is how you import a bunch of meshes into MAKE3DO. All of the mesh objects contained inside the specified PAS file will be imported into a “mesh group” with the specified name.

The LINK option causes any meshes that are part of a linked hierarchy to be linked together during import into a single “LINKED_MESH” identified by its root’s name – any unlinked meshes remain normal “MESH” objects identified by their MAX names. The USEPIVOTS option causes all of the MESH objects to be imported relative to their pivot points, otherwise it is as if all of the MESH objects have a pivot point of (0, 0, 0) inside MAX. The RENAME_MAT option is required to assign an Appearance node a name (so it can be accessed inside the game). The RENAME_MAT option can also be used to collapse several materials into one named material, by assigning them all the same name – however, this will only work if all the materials have the exact same settings (diffuse color, shininess, texture maps, etc.) See the car PSG file for example of RENAME_MAT usage. The ADDCHILDREN option allows you to link scene graphs onto certain meshes during import. This is useful for adding tools (controlled by state switches) onto animated pit crews hands. I made an example PSG file and e-mailed to tom faiano demonstrating how to use ADDCHILDREN.

meshGroupIdentifier: MESH_GROUP "filename.pas"


Using MESH_GROUP is the name of your PAS file where all the 3d models reside.

[RENAME_MAT (oldName1 newName1, ..., oldNameN newNameN)]

[ADDCHILDREN (objectName1 sceneIdentifier1, …, objectNameN sceneIdentifierN)]

After importing a bunch of MESH objects using the MESH_GROUP command, you may assign each MESH inside the group a name. This allows you to identify individual MESH objects in LOD commands, for instance. The MESH command assignes a MESH in the group the specified name (meshIdentifier). Also, if the mesh is animated in max, you can individually name each animation frame by optionally specifying the frame you want to name - the first frame (and default) is #0

meshIdentifier: MESH meshGroupIdentifier objectName [FRAME frameNum]

If the LINK option was used during the MESH_GROUP command, some of the MESHes inside the group may be linked hierarchies. In order to be “type safe”, you cannot name a LINKED HIERARCHY with the MESH command, instead you must use this LINKED_MESH command:

linkedMeshIdentifier: LINKED_MESH meshGroupIdentifier objectName

Some reason they never listed this, but the most common way to define your models in your script is this way:

meshIdentifier: MESH meshGroupIdentifier objectName

objectName refers to the EXACT name of the model in the 3ds max 8 file where you export said scene out as a PAS file.

For example if you have a 3DS Scene with a box called 'Good_Box' and the scene you exported out as a PSG was called 'Cool Scene' then to define your model in your script would go as follows with these 2 lines of code:

model: MESH_GROUP "cool scene.pas"

the_box: MESH model Good_Box

And thats it. Now any time you need to call your 'Good_Box' model in your script up its now known as 'the_box'. You could literally give it the same name as well so it would read as:

Good_Box: MESH model Good_Box

But to avoid confusion I made a different name to show how you define it.


Loads an arbitrary Scene Graph already saved out as a 3DO. This is used to get the wheels out of wheels.3do (produced my makewheel.exe) and stick them onto the car model:

sceneIdentifier: SCENE "filename.3do"


Identifies a single node of a group as a new sceneIdentifier. Fails if source sceneIdentifier is not a GroupDescriptor (compile-time type check). ChildIndex is 0 based. Only used with the SCENE command, to pick out parts of a loaded 3DO scene graph:

sceneIdentifier: CHILD sceneIdentifier { childname | childIndex }


Defines a bounding box in axis aligned model space:

bboxIdentifier: BBOX MIN (minX, minY, minZ) MAX (maxX, maxY, maxZ)

Defines a bounding box that encloses the specified mesh object:

bboxIdentider: BBOX meshIdentifier

Bounding box BBOX code is commonly used in Car Mods for Flaps and Damage areas


Constructs a portal node. PortalName is the name of the portal for access at run-time by the application. meshIdentifier specifies the mesh that comprises the portal itself. Mainly used to create mirrors. The mesh must be a single shape (all of its faces must have the same material assigned to them, even though the material is not used during rendering):

SceneIdentifier: PORTAL portalName meshIdentifier

You see this code every time you race in NR2003. It is the mesh that is the rear view mirror you see. This code defines that mirror mesh as a portal which will 'reflect' whatever is behind it.


Constructs a transform node. The sceneIdentifer is the child of the transform. ROT is degrees:

sceneIdentifier: TRANSFORM [NAME nodeName] [bboxIdentifier] POS (x, y, z) ROT (yaw, pitch, roll) sceneIdentifier


Constructs a billboarding transform node. The sceneIdentifer is the child of the transform. AXIS is a unit vector that constrains the billboard to rotate only about that axis:

sceneIdentifier: BILLBOARD [bboxIdentifier] POS (x, y, z) [AXIS (i, j, k)] sceneIdentifier

Commonly used most often for the backfire effect on a car mod. Also this is what was used to create the Driver Display Interface effect on the ICR and GoKart mods.

Constructs a group node:

sceneIdentifier: GROUP [bboxIdentifier] (sceneIdentifier1, ..., sceneIdentifierN)

Used very often to group several of your models in the script together.


Constructs an LOD node - The specified POS is for computing the current distance at render time.

The distances are the minimum distance to draw the child at, dist1 should always be zero.

Sort them from closest to farthest.

sceneIdentifier: LOD [bboxIdentifier] [POS(x, y, z)] (dist1 sceneIdentifier1, ..., distN sceneIdentifierN)

Constructs a “static” LOD node – The difference between this node and “regular” LOD node is that

static LOD nodes will be rendered without adjusting lod distances by speed vs. quiity slider in graphics options. It could be useful for objects that are more sensitive to lod distance adjustments – e.g. top levels of hierarchical lods and animatied pit crew. Please, note that FOV and resolution will still affect lod selection of these nodes.

sceneIdentifier: STATIC_LOD [bboxIdentifier] [POS(x, y, z)] (dist1 sceneIdentifier1, ..., distN sceneIdentifierN)

Static LODs are used all the time for track 3do's and Car Models. Static is usually preferred because you can manually create your own level of detail mods and set the length in meters the LOD will transition.


Creates a point light at the center of the specified bounding box or position. The intensities are floating point values (usually set ambient to 0.0 and diffuse to a value around 400.0+). The color specifies the color of the light, each color component must be between 0.0 and 1.0 inclusive. In order to work correctly in the engine, POINTLIGHTS must only be used in trackside objects, and in the same reference frame as their trackside object (this means do not nest a point light underneath a transform node in the script!). By default, a POINTLIGHT will have a glow of 4m in diameter, this can be overridden with the GLOW option. Specifying a glow of 0.0 will cause the light to not emit a glow special effect in the engine.

sceneIdentifier: POINTLIGHT bboxIdentifier | POS(x,y,z) diffuseIntensity ambientIntensity [COLOR (red, green, blue)] [GLOW(sizeInMeters)]

Commonly used on night tracks with light poles


Constructs a STATE_SWITCH node. Note that there is one more state val than sceneIdentifiers;

this is to support State Morph children, which require that each state has a range. Sort them from smallest state val to largest state val.

sceneIdentifier: STATE_SWITCH [bboxIdentifier] VAR stateVarName [PERIOD value] (stateVal1 sceneIdentifier1, ..., stateValN sceneIdentifierN, stateValN+1)

State switches is where all the magic happens. It takes the meshes you defined in your script and makes them do 'stuff'. What that 'stuff' is ranges from making them act like a flap on the car, a wheel spinning, damage morphs, an animating billboard on the side of your track, caution lights that trigger on your track when a wreck occurs, etc

Note: The state switches that can be used for some reason they didn't actually list them all in this document. They can however be found in another file Papyrus text file provided called papy.txt


Constructs a REGION MORPH node. The two meshes must have the same number of vertices and faces, and the vertices and faces must be in the same order for it to work correctly. Ideally, make the two meshes in 3DS Max by creating an animated mesh, and then use two frames of the animation for this construct (insuring the vertices and faces remain in same order) - see MESH statement, above. Each region can be controlled by its own state variable, or regions can be setup to share a variable. Optionally, (and optimally) the state variable can be broken into bit fields (up to 32 bits total) where each field controls a region of the mesh. In each region, the bbox defines which vertices are considered part of the region, vertices can be included in more than one region and weights will be calculated automatically.

sceneIdentifier: REGION_MORPH meshIdentifier DESTINATION meshIdentifier

REGIONS (bboxIdentifier stateVarName [numBits firstBit],

bboxIdentifier stateVarName [numBits firstBit], ... )

This is the code that creates all the glorious car damage you see on mods.


Creates a new spanning mesh object from a normal mesh object. A spanning mesh has vertices controlled by multiple reference frames (such as a suspension strut that is attached to a car chassis at one end, and is attached to the nested moving wheel hub at the other end). The vertices in the nested ref-frame are identified by an axis-aligned bounding box around those vertices of the source mesh (put a box around the nested verts in 3DS, and use the BBOX command to get the bounding box in the script file). The nested ref-frame (transform) is identified by its sceneIdentifier.

[ICODE]sceneIdentifier: SPANNING meshIdentifier NESTED (bboxIdentifier transformSceneIdentifier)[/ICODE]


Makes a copy of a mesh object (presumably so it can be modified using the MODIFY command).

meshIdentifier: COPY meshIdentifier

Very useful command when you have a mesh in your script and need to duplicate it. Sure, you can duplicate the mesh int he 3DS scene before exporting but sometimes (like when you just need to retexture the same model) copying is more efficient.


Identifies a texture for the MODIFY TEXTURE command.

mipIdentifier: MIP "filename.mip"

Used commonly in tandem with COPY command to copy a mesh and then use the MODIFY TEXTURE command to give it a different MIP file

Replaces a texture in all appearances used by a Mesh Object that refer to the specified original texture.


meshIdentifier origMipIdentifier newMipIdentifier


Defines an ‘empty’ object that can be used for a lod or in a state switch, for example.

emptyObjectIdentifier: EMPTY_MESH

Commonly used command to great an invisible mesh. Used often for a LOD level where you want nothing to render, or need a mesh to 'disappear' in certain states.


Outputs a 3DO file

OUTPUT sceneIdentifer ["filename.3do"]

The grand finale command at the end of every script. This is where you give your 3do a filename
Last edited:
Who read this thread (Total readers: 0)