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.

# A comment
filedependancy <filename>
newmodel <model_name>

setsupermodel <model_name> <filename>

classification <model_classification>

setanimationscale <float>


Model Header


beginmodelgeom <model_name>




Geometry Block






node <node_type> <node_name>

endnode



...


node <node_type> <node_name>

endnode


endmodelgeom




newanim <animation_name> <model_name>





Animation Block






length <float>

transtime <float>

animroot <node_name>



node <node_type> <node_name>

endnode



...


node <node_type> <node_name>

endnode


doneanim <animation_name>
donemodel

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


The model header contains data about the model itself.

Classification

classification <model_classification>

Determines the type of model and influences the way objects are rendered. Valid options are:

  • Item: Any inventory item.
  • GUI: TODO
  • Effect: Visual effects
  • Door: Generic or tilesets doors
  • Character: Creature or placeables
  • Tile: Tilesets
  • Unknown: Avoid using this

Supermodel

setsupermodel <model_name> <supermodel_name>

This command references other model files. A model may have a (super)model from which it inherits animations. If there is no supermodel the second parameter has to be NULL. The model may overwrite any animation from its supermodel. The structure of the two models must to match, i.e. the order of the objects in both files must be the same.

Animationscale

setanimationscale <model_animationscale>

Used in conjunction with supermodels this value modifies (scales) animations to avoid shearing effects which may appear when the size of objects in this model does not match the ones in the supermodel.



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:

  1. Has to have the same name as the file (minus the file extension)
  2. 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 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

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:

0DisabledThis object will never fade
1FadeThis object will fade
2Base???
3NeighborThis 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>

x0   y0   z0

x1   y1   z1

...

xn   yn   zn


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>

u0     v0     0

u1     v1     0

...

un     vn     0

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>

v1   v2   v3

v1   v2   v3

...

v1   v2   v3

s

s


s

uv1   uv2   uv3

uv1   uv2   uv3


uv1   uv2   uv3

m

m


m

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>

r0     g0     b0

r1     g1     b1

...

rn     gn     bn

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>

x0     y0     z0

x1     y1     z1

...

xn     yn     zn

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>

x0     y0     z0  

x1     y1     z1  

...

xn     yn     zn  

s0

s1


sn


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>

name1 weight1  name2 weight2

name1 weight1

...

name1 weight1  name2 weight2  name3 weight3  name4 weight4

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:

  1. Global Ambient Lighting, Sun & Moon lights
  2. Torches & light spells
  3. Spells, general lights
  4. Un-needed tile lights
  5. 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

minx   miny   minz

minx   miny   minz


minx   miny   minz

maxx   maxy   maxz

maxx   maxy   maxz


maxx   maxy   maxz

idx

idx


idx



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>


Animation Header


node <node_type> <node_name>

...

endnode


Animation Node





node <node_type> <node_name>

...

endnode


Animation Node

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.

NamePurpose
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_endEnd of Weapon Blur effect
blur_startStart of Weapon Blur effect
castSpell activate effect (depends on spell)
draw_arrowSound: Draw Arrow
draw_weaponSound: Draw Weapon
hitWeapon hit effect (depends on weapon)
parryParry effect
snd_footstepSound: Footstep (depends on surface material)
snd_hitgroundSound: 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

t0


tn

x0   y0   z0

...

xn   yn   zn

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

t0


tn

x0   y0   z0  x1   y1   z1 x2   y2   z2

...

xn   yn   zn  xn   yn   zn  xn   yn   zn

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

t0


tn

x0   y0   z0   a0

...

xn   yn   zn   an

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

t0


tn

s0

...

sn

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

t0


tn

r0

...

rn

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

t0


tn

r0          g0          b0

      ...

r0          g0          b0

endlist

Animateable Properties

This a list of animateable properties for each node type.

Dummy 1

Mesh 2

Emitter

Light

  • Birthrate

TODO

  1. And all other node types.
  2. Includes Trimesh, Danglymesh and Skin(mesh)
  • No labels

2 Comments

  1. If you can write this so I can understand it (and you're doing OK so far) I'll be in your debt.

    TR

    1. 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.