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).
Introduction
An Ascii MDL file consist of a header, a single geometry block holding all meshes and optional animations blocks.
Remarks
Some model naming rules are documented here: https://neverwintervault.org/project/nwnee/other/mdl-naming-rules
Comments
#
A comment
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.
File Dependancy
filedependancy <filename>
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 UNKNOWN
.
Colors
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]
Null
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
Model Header
Model Geometry
Dummy
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
Parent ¶
parent <node_name>
The name of this objects the parent node. NULL if none.
Position ¶
position <X> <Y> <Z>
Position of the node relative to its parent. This property can be animated.
Orientation ¶
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.
Scale ¶
scale <S>
Uniform scale of this object. This property can be animated.
Wirecolor ¶
wirecolor <R> <G> <B>
This property seems to be present for all objects, but seems to be unused.
Reference
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.
Reference Model ¶
refmodel <mdl_filename>
The name of another MDL file without file-extension.
Note this should always be set. A dummy one commonly used is fx_ref (fx_ref.mdl) which is otherwise a blank effect MDL.
Reattachable ¶
reattachable 0|1
Seems to do with Beams and Projectiles attaching to parts of a target of the VFX (eg beams attaching to the targets chest node, eg Lightning Bolt - vdu_beam000.mdl). Alternatively it's set to 0 in VFX such as Evards Black Tentacles (vps_tentacle.mdl).
Note this should always be set to 0 or 1.
Patch
Unknown
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.
Trimesh
Trimeshes inherit all attributes from Dummys in addition to their own.
Alpha ¶
alpha <A>
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 Color ¶
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.
Beaming ¶
beaming 0|1
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.
Bitmap ¶
bitmap
<texture_filename>
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 SurfaceTGA
: Truevision TGAPLT
: 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 ¶
center <X> <Y> <Z>
Unused, pivot point of the object.
Diffuse Color ¶
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.
Inheritcolor ¶
inheritcolor 0|1
Unused.
Materialname ¶
materialname <mtr_filename>
Enhanced Edition only. Specifies a material file (*.mtr), which allow the specification of shaders, additional textures and parameters.
Self-Illumination Color ¶
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.
Shadow ¶
shadow 0|1
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).
Shininess ¶
shininess <int>
TODO. Shininess requires a matching texture information file (*.txi).
Specular Color ¶
specular <R> <G> <B>
Material specular color, however this has no effect. Default value when omitted: (0.2, 0.2, 0.2)
Render ¶
render 0|1
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).
Renderhint ¶
renderhint NormalAndSpecMapped
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.
Rotate Texture ¶
rotatetexture 0|1
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.
Tilefade ¶
tilefade <int>
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 |
2 | Base | ??? |
3 | Neighbor | This object will fade together with objects in neighboring tiles |
Transparencyhint ¶
transparencyhint <int>
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
Vertices ¶
Every lines defines a single vertex. The Faces section on a node references the indices of three vertices for every face.
verts <vert_count> | |
|
Texture Vertices (tverts) ¶
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.
tverts <tvert_count> | |||
|
Faces ¶
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.
faces <face_count> | ||||
|
|
|
|
Colors ¶
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.
colors <vertex_count> | |
|
Normals
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.
normals <vertex_count> | |
|
Tangents
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
tangents <vertex_count> | ||
|
|
Danglymesh
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.
Period
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.
Tightness
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.
Displacement
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.
Constraints
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.
Skin(mesh)
Skinmeshes are Trimeshes with additional weight parameters to support skeletal animations. As such they possess all properties of a Trimesh.
Weights
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
(name, weight)
tuples - 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.
weights <vertex_count> | |
|
Animesh
Animeshes are Trimeshes with animated UV coordinates or vertices. In the geometry section of a model they have exactly the same properties as Trimeshes.
Emitter
See subpage MDL ASCII Emitter Nodes.
Light
Affect Dynamic ¶
affectdynamic 0|1
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.
Ambient Only ¶
ambientonly 0|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 ¶
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.
Dynamic Type ¶
nDynamicType 0|1
Important for tile lights. Should be 0 for main lights and 1 for animated lights. Use this instead of isDynamic, which is outdated.
Fading ¶
fadinglight 0|1
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. This takes 1.5 seconds. 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 when the lights are at their maximum amount in the Enhanced Lighting setup in 8193.34 the game will turn off an existing light to play a nearer one. This appears to have a 1.5 second delay turning a light off and a 1.5 second fade in time for the light using fadinglight 1.
Generateflare ¶
generateflare 0|1
Whether or not to use lens flares. Used in conjunction with Flare Radius, Flare Colorshifts, Flare Positions, Flare Sizes and Texture Names
Note: This value has no effect on tile lights as they can't have lens-flares.
Flare Radius ¶
flareradius <float>
Radius in centimeters for all lens-flares cast by this light. Default value of 0. Used in conjunction with Flare Colorshifts, Flare Positions, Flare Sizes and Texture Names
Note: The flare radius is specified in cm as opposed to meters for other distance values.
From Merricksdad:
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.
Flare Colorshifts ¶
flarecolorshifts <flare_count>
<R> <G> <B>
...
<R> <G> <B>
A list of color shifts for lens-flares. Their number must match the amount of Flare Positions, Flare Sizes and Texture Names. This property will have no effect if Flare Radius is set to zero.
Flare Positions ¶
flarepositions <flare_count>
<float>
...
<float>
A list of positions for lens-flares. Their count must match the amount of Flare Colorshifts, Flare Sizes and Texture Names. This property will have no effect if Flare Radius is set to zero.
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.
Flare Sizes ¶
flaresizes <flare_count>
<float>
...
<float>
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.
Is Dynamic ¶
isdynamic 0|1
Outdated, used nDynamicType instead. Important for tile lights. Should be 0 for main lights and 1 for animated lights.
Light Priority ¶
lightpriority <int>
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.
Multiplier ¶
multiplier <float>
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.
Radius ¶
radius <float>
Light radius in meters.
This is also used a default for shadow radius when not explicitly set using shadowRadius.
Shadow ¶
shadow 0|1
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
ShadowRadius ¶
shadowRadius <float>
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).
Texture Names ¶
texturenames <flare_count>
<texture_filename>
...
<texture_filename>
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
Vertical Displacement ¶
verticaldisplacement <float>
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.
AABB
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)
aabb | |||
|
|
|
Animations
An mdl file may contain multiple animation blocks with the animation name determining its usage.
newanim <animation_name> <model_name> | |||
length <float> transtime <float> animroot <node_name> | |||
node <node_type> <node_name> ... endnode | |||
node <node_type> <node_name> ... endnode | |||
doneanim <animation_name> |
Animation Header
Length ¶
length <float>
Length of the animation in seconds.
Transition Time ¶
transtime <float>
Time in seconds where the animation overlaps with other animation to ensure a smooth transitions.
Animation Root ¶
animroot <node_name>
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.
Events ¶
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.
Name | Purpose |
---|---|
detonate | 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) |
parry | Parry effect |
snd_footstep | Sound: Footstep (depends on surface material) |
snd_hitground | Sound: Creature hits ground |
Animation Nodes
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.
Single Values ¶
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.
Keys ¶
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 positionkey
and endlist
. However some tools will instead use positionkey <keycount>
and omit the endlist
.
Examples
Positionkey
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.
positionkey | ||
|
| |
endlist |
PositionBezierKey
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.
positionkey | ||
|
| |
endlist |
Orientationkey
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.
orientationkey | ||
|
| |
endlist |
Scalekey
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.
scalekey | ||
|
| |
endlist |
Radiuskey
Animates the radius of a node. Each line specifies the radius of the node at time t
. The time value mustn't be larger then the animation length.
radiuskey | ||
|
| |
endlist |
Colorkey
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.
radiuskey | ||
|
| |
endlist |
Animateable Properties ¶
This a list of animateable properties for each node type.
Dummy 1 | Mesh 2 | Emitter | Light |
---|---|---|---|
TODO | |||
|
2 Comments
Tarot Redhand
If you can write this so I can understand it (and you're doing OK so far) I'll be in your debt.
TR
symmetric
Yeah, I'm trying my best. This is heavily WIP and so far mostly a copy of my personal documentation which I've accumulated over the past 5-6 years. Unfortunately a lot of resources/documentation for the mdl format disappeared over the past few years.
Formatting is barely adequate and formulations tends to be very technical. But I have a couple of ideas to visualize and explain the file structure a bit better.