MDL ASCII format files are text files of decompiled or uncompiled models. You can load these into the game but it is recommended in released models to compile models making it faster to load in the engine (especially on complex models).
|Table of Contents|
An Ascii MDL file consist of a header, a single geometry block holding all meshes and optional animations blocks.
Some model naming rules are documented here: https://neverwintervault.org/project/nwnee/other/mdl-naming-rules
Every line starting with a
# is considered a comment. Most mdl files start with a comment line indicating the date and the program used for its creation.
This line is a reference to the file used by the 3D modeling program which the model was created in. Unlike the name suggests it is irrelevant to the game engine and often times contains only the value
ambient 1.0 0.0 0.0
Colors in mdl file are always defined as RGB values in the range of [0.0, 1.0]
Whenever something is explicitly not existing, like a texture or the parent of a node it must be declared as NULL. Leaving an empty value is considered invalid.
parent NULL # No parent
bitmap NULL # No texture
A Dummy is a single point in space without any data - no geometry, no surface, no volume, nothing. Therefore it is never rendered. Dummies are used to group objects or indicate special locations to the engine like target coordinates for spells and projectiles.
Every model always has a dummy as its first node which:
- Has to have the same name as the file (minus the file extension)
- Mustn't have a parent
The name of this objects the parent node. NULL if none.
position <X> <Y> <Z>
Position of the node relative to its parent. This property can be animated.
orientation <X> <Y> <Z> <A>
The rotation of this node relative to its parent, in axis-angle representation. The first three values define the axis, the fourth defines an angle in radian. This property can be animated.
Uniform scale of this object. This property can be animated.
wirecolor <R> <G> <B>
This property seems to be present for all objects, but seems to be unused.
These nodes are used as targets for certain emitter types. They are always children of those emitters and indicate the target locations for Lightning or Bezier type emitters.
In addition to all properties mentioned here, reference nodes have all properties of a Dummy.
The name of another MDL file without file-extension.
All non-standard node types get defauled to dummy, this might have been something Bioware was planning but the Engine ignores now. Don't use them yourself.
They occur in some Bioware visual effects, but they behave exactly like normal dummy nodes and have the same properties.
Trimeshes inherit all attributes from Dummys in addition to their own.
Alpha value controlling transparency. Valid values are floating point numbers within [0.0, 1.0] - 0.0 being fully transparent and 1.0 fully opaque. This property can be animated.
ambient <R> <G> <B>
OpenGL GL_Ambient material parameter. Best leave this at 1.0 1.0 1.0. Default value when omitted: (1.0, 1.0, 1.0) - although compiled models done pre-patch 1.81.8193.17 will use 0.2 0.2 0.2 if you happen to see it and wonder why an old model looks weird.
For light-rays. Light rays cannot be made specifically for a tile, as tiles can rotate. Instead a different kind of shadow was created where the shadow volume is rendered at an alpha value and a specific color. To create shadow volumes, make a group of simple triangles above the camera level and enable this flag.
Specifies the texture to be used by this node. The value
NULL means to texture. Valid file formats include:
- MTR: Material file
DDS: Direct Draw Surface
TGA: Truevision TGA
PLT: Packed Layer Texture
MTR files take precedence over image textures and may overwrite textures specified in the MDL. If a txi file with same name is present it will be loaded as well.
center <X> <Y> <Z>
Unused, pivot point of the object.
diffuse <R> <G> <B>
Material diffuse color. Default value when omitted: (1.0, 1.0, 1.0) - although compiled models done pre-patch 1.81.8193.17 will use 0.8 0.8 0.8 if you happen to see it and wonder why an old model looks weird.
Enhanced Edition only. Specifies a material file (*.mtr), which allow the specification of shaders, additional textures and parameters.
selfillumcolor <R> <G> <B>
Makes the mesh glow without actually acting as a light source. It most likely corresponds to the GL_EMISSION material parameter in OpenGL. This property can be animated.
Note: In older mdl files this parameter might be spelled wrongly as
seTfillumcolor. (Pstemarie) The mispelling of this parameter is one of the primary reasons that older models fail to compile using nwnmdlcomp.
Controls whether this objects should cast a shadow. This attribute is frequently used in conjunction with the Render attribute to create a low-poly mesh solely for the purpose of casting shadows. Valid values are either 0/"false" (disabled) or 1/"true" (enabled).Note: A number of community made models use "True" and "False" as values.
TODO. Shininess requires a matching texture information file (*.txi).
specular <R> <G> <B>
Material specular color, however this has no effect. Default value when omitted: (0.2, 0.2, 0.2)
Controls whether this object should be rendered. The geometry will still be loaded to the memory and casts shadows, even if this is unchecked. This is frequently used in conjunction with the Shadow attribute to create a low-poly shadow volume object. Valid values are either 0/"false" (disabled) or 1/"true" (enabled).Note: A number of community made models use "True" and "False" as values.
Enhanced Edition only. Valid values:
NormalAndSpecMapped / NormalTangents (either of the two, can be used interchangeably)
Instructs the game to generate tangent and handedness vertex data for the associated model(s), provided that they are uncompiled and no tangent and handedness data exist. Also makes the game choose a shader with normal mapping enabled to render this content. Note: The renderhint can also be specified in a material file.
Only relevant for tilesets and controls ground mapping: When a model with this flag is rotated, the engine rotates the uv coordinates back to the original position. This avoids texture seams on tile edges.
This property is only relevant for tiles. It controls whether this mesh will turn invisible to offer a clear line of sight on the character. Possible values:
|0||Disabled||This object will never fade|
|1||Fade||This object will fade|
|3||Neighbor||This object will fade together with objects in neighboring tiles|
As transparency can be extremely costly to calculate from alpha values on a texture, this property gives the engine priority values of transparent objects. An object with a transparency hint of 1 will be rendered after an object that has a value of 0. This does not work on dynamic objects, it only affects static objects
Every lines defines a single vertex. The Faces section on a node references the indices of three vertices for every face.
Every lines defines a single texture vertex (tvert). They are 2-dimensional - the last value is always 0. The Faces section on a node references the indices of three tverts for every face.
Each line defines a triangle:
- The first three values
(v1 v2 v3) each reference a vertex from the vertex list. This is the index, not the actual coordinate.
- The 4th value
(s)defines the shading group the face belong to. All neighboring faces belonging to same group will be rendered as smooth.
- The next three values
(uv1 uv2 uv3)each reference a texture vertex (uv-vertex) from the tvert list. Again is the index, not the actual coordinate.
- The last value
(m)defines a material index. This is only relevant when creating walkmeshes for tiles and determines the surface type of this face.
Vertex colors. Each line contains the color for the corresponding vertex in the vertex list, as such the number of colors must match the number of vertices. Vertex colors are not used by the default shaders.
Per-vertex normals. Each line contains the normal for the corresponding vertex in the vertex list, as such the number of normals must match the number of vertices.
Per-vertex tangent data. Each line contains the value for the corresponding vertex in the vertex list, as such the number of tangents must match the number of vertices.
- The first three values defines the tangent vector
- The fourth value defines the binormal/ bitangent sign
Danglymeshes are Trimeshes which are "bouncy" or "dangly" and are affected by environmental effects like wind, character movement and visual effects.
In addition to their own set of attributes, Danglymeshes inherit all attributes from Trimeshes.
Determines how quickly the vertices move and how fast they come to a rest.
This value can be up to 59 before the mesh begins to jiggle incorrectly. At 59.5 the mesh may spasm sporadically. At 60 or above, the mesh will jiggle insanely for up to 1 second and then no longer jiggle, becoming locked at the starting position.
Tile grass (as created by walkmesh surface material 3) uses effects equivalent a hardcoded value greater than 60. It appears to be the only thing which can use this high a value.
The ’tension’ in the object, restricting movement.
A tight object is difficult to move by wind. Tile grass may have an approximate tightness of 50 or more.
Determines show far the vertices are allowed to move. This is further restricted by the constraints.
The value appears to be approximately 1/2 meter per unit.
Each line contains the constraints for the corresponding vertex in the vertex list, as such the number of constraints must match the number of vertices.
Constraints act as a multiplier for the displacement value and range from 0 to 255, with their value equating to the multiplier range of 0 to 1.
If the maximum displacement is set to 2, then the associated vertex painted with 255 will be allowed to move approximately 1 meter during maximum strength wind.
Skinmeshes are Trimeshes with additional weight parameters to support skeletal animations. As such they possess all properties of a Trimesh.
Every skinmesh must specify weights which influence its movements. Each line contains the weights for the corresponding vertex in the vertex list, as such the number of entries must match the number of vertices.
- Every line consists of
(name, weight)tuple, each defines the influence a node has over a single vertex.
- A line MUST have at least one
(name, weight)tuple. It mustn't be empty.
- A line MUSTN'T have more than four
- The weights are floating point numbers in the range of [0.0, 1.0]
- The sum of weights of must be 1.0 for each line.
Animeshes are Trimeshes with animated UV coordinates or vertices. In the geometry section of a model they have exactly the same properties as Trimeshes.
See subpage MDL ACSII Emitter Nodes.
This controls whether this light affects dynamic objects, i.e. characters. Disabling this will prevent this light from producing shadows with dynamic objects, but it will in turn improve performance. A less strict version of the Shadow setting. Default 1.
Controls if the light is only an ambient light source or if it is directional as well. Default 0.
Ambient lights do not cast shadows like diffuse lights.
color <R> <G> <B>
RGB light color.
Note: This value has no effect on tile lights (names ending with ml1, ml2) as that color is set by the module builder in the toolset.
Important for tile lights. Should be 0 for main lights and 1 for animated lights. Use this instead of isDynamic, which is outdated.
When a light is loaded or dropped from a scene, the light will flick on and off suddenly. If this check is on, the light will take a moment to fade on or off for a better overall feel. In some cases it’s best to have this un-checked, for things like spells where you want the light to be at it’s brightest as soon as it appears.
Note: This value has no effect on tile lights as they can't have lens-flares.
Note: The flare radius is specified in cm as opposed to meters for other distance values.
The flare radius is definitely not in cm. With a test light at 80m, a flareradius of 800.0 (cm?) is enough to show a faint lens flare.
The fade to the flare seems to be in the form of an inverse power curve. Possible math could be something like opacity = sqrt(flareradius)/distance, or even a cube root.
<R> <G> <B>
<R> <G> <B>
The position value modifies the offset from center where the lens flare will be drawn. The offset seems to be a square power curve relative to screen center and the angle from camera to light.
If we imagine a line from the light to the camera, a position of 0.0 will be drawn directly on that line. So if the light is at center screen, the flare will also be at center screen.
A position of 1.0 will be drawn such that the flare is put at an angle higher than the camera angle to the sun. So if the light is to the right of center screen, the flare will be even further right.
A position of -1.0 will be drawn such that the flare is put at an angle lower than the camera angle to the sun. So if the light is to the right of center screen, the flare will be drawn to the left of center.
Therefore this value represents the curvature of the lens as well as its concave/convex nature.
A list of sizes for lens-flares. The actual flare sizes depend on the texture size, these values will scale the texture. Their number must match the amount of Flare Colorshifts, Flare Positions and Texture Names. This property will have no effect if Flare Radius is set to zero.
Outdated, used nDynamicType instead. Important for tile lights. Should be 0 for main lights and 1 for animated lights.
This property helps the engine in preventing the use of too many lights that affect dynamic objects. In order to know which lights should be culled first a priority is assigned to each light, ranging from 1-5, with 1 being the highest priority. This property is related to the light count setting in the game.
This is the general scheme:
- Global Ambient Lighting, Sun & Moon lights
- Torches & light spells
- Spells, general lights
- Un-needed tile lights
- Other un-needed lights (e.g. weapons)
Note: There are additional priority caveats. One is that racial vision and worn gear realistically operate at "priority 0", and override all other lights in the scene if necessary in order to not exceed the client's light number limit. Also, only 1 (the newest created) light can exist on the player at a time; in this way, you can think of racial vision like a light placed on the skin item during chargen.
Additionally, racial vision is culled when close enough to practically any other light source. This is controlled by progfx.2da's Param5 for the vision lines (304-383), while worn light from gear cannot be modified and the newest light can exist on the player at any time, removing other lights from the scene in order to get within the client's light limit number setting.
Light intensity or brightness. Default value for Bioware SourceLights and MainTileLights is 1.
This multiplier does not affect shadow radius, so you can set radius to 10, which will automatically set shadow radius, then set multiplier to 5, making radius effectively 50, but shadow radius will stay 10.
Light radius in meters.
This is also used a default for shadow radius when not explicitly set using shadowRadius.
Determines if this light is capable of producing shadows at all. A stricter version of the Affect Dynamic property. Default 1.
Ambient-only lights cannot use this property
When shadows are cast by this light, the maximum radius (m) in which to draw shadows can bet set manually. By default, the shadowRadius matches the light radius value. The shadowRadius property can be animated separately from the radius value.
In NWN:EE, shadow opacity is faded over the distance from the source of the light to the shadowRadius value.
A standard torch with a light radius will cast shadows no more than 20m, so will not reach the bottom of overly deep pits. Such pit bottoms will appear illuminated relative to the nearby shadow. To remedy this, shadowRadius can be set higher than the light radius, at the expense of performance. The longer the shadowRadius, the more shadow processing time is required for all shadows and the more shadows will get created per redraw.
Tip: If your light source casts light in a 15m radius, try setting the shadowRadius = 0.71 x radius (about 10.65m) to make shadows merge better with the base lighting and other shadows in the area.
Note: Specifically, the distance this is set to, divided in half, is the max range at which the shadow will be visible. It can be considered "max opacity" at 25% of the distance and gradually fade to "fully transparent" at 50% of the distance set (which is in meters).
A list of texture names for lens-flares. Their number must match the amount of Flare Sizes, Flare Positions and Flare Colorshifts. Only TGA or DDS textures are allowed. This property will have no effect if Flare Radius is set to zero.
Default textures: fxpa_lensring or fxpa_lensflare
By default (0), no displacement will be added. If using Ctrl+Shift+F12 debug menu, having debug renderlights enabled should show you where the light is normally for the purposes of shadow-casting, and the vertical displacement value you set will move this light up or down from the default height (although you won't see any change in the debug light height shown). Having a value of 5 means the light will be high up in the sky and cast shadows nearly vertically down on everything around you, whereas -2 is likely to cast very long sweeping shadows on everything around you.
Axis Aligned Bounding Boxes in a tree structure for collision detection.
- The first three values define the minimum coordinates of the bounding box
- The next three values define the maximum coordinates of the bounding box
- The last value defines the face index, if the node is a leaf (-1 otherwise)
An mdl file may contain multiple animation blocks with the animation name determining its usage.
|newanim <animation_name> <model_name>|
node <node_type> <node_name>
node <node_type> <node_name>
Length of the animation in seconds.
Time in seconds where the animation overlaps with other animation to ensure a smooth transitions.
The animations entry point. This is useful for only animating part of the model and having the rest play a different animation. This used in the torch an shield holding animations to only influence the movement of a single arm.
event <time> <event_name>
event <time> <event_name>
Each animation may have a list of events. They typically trigger sounds or visual effects. The time value mustn't be larger then the animation length.
Trigger all Explosion type emitters.
Also produces a burst of wind using the blast properties of the emitter node. Those properties include radius and length, so a gust of wind can be managed.
|blur_end||End of Weapon Blur effect|
|blur_start||Start of Weapon Blur effect|
|cast||Spell activate effect (depends on spell)|
|draw_arrow||Sound: Draw Arrow|
|draw_weapon||Sound: Draw Weapon|
|hit||Weapon hit effect (depends on weapon)|
|snd_footstep||Sound: Footstep (depends on surface material)|
|snd_hitground||Sound: Creature hits ground|
Animation nodes reference nodes in the geometry block. The order and the name of the animation nodes must match the ones in the geometry block. However the node type may differ.
A node's properties are animated by specifying either a single value or a list of keys.
If the animation contains only a single state, they are specified the same way as in the geometry block, e.g.
position X Y Z. In this case the values will be held for the entire duration of the animation.
A list of keys, each specifying the value at a certain point in time. If a property is animateable, appending key to its name indicates the beginning of a key-list. Click here for a list of animateable properties.
The correct way to specify keyframes is enclosing them with
endlist. However some tools will instead use
positionkey <keycount> and omit the
Animates the position of a node. Each line specifies the position of the node at time
t. All values are floats and relative to the parent node. The time value mustn't be larger then the animation length.
Animates the position of a node as a bezier curve. Each line specifies the position of the node and up to two bezier curve handles/control points at time
t. All values are floats and relative to the parent node. The time value mustn't be larger then the animation length.
Animates the orientation of a node. Each line specifies the orientation of the node at time
t. The angles are in Axis-Angle format and relative to the parent node. The time value mustn't be larger then the animation length.
Animates the scale of a node. Each line specifies the scale of the node at time
t. The time value mustn't be larger then the animation length. You'll notice scalekey and radiuskey use the same formats.
Animates the color of a node. Each line specifies the color of the node at time
t in the form of a percentage from 0 to 1 directly translated into 0 to 256. Higher numbers increase the light's intensity, but as long as the ratio between the 3 values stays the same, then the color of the light remains the same too. The time value mustn't be larger then the animation length.
This a list of animateable properties for each node type.