This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
assets:model [2019-09-13 11:07] – [Pass definition] skyjake | assets:model [2020-02-23 08:33] (current) – [Common metadata for models] skyjake | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== 3D model assets ====== | ||
+ | |||
+ | This page is about 3D model assets that can be rendered in the game world using the [[modding: | ||
+ | |||
+ | Doomsday 1.x supports only [[ded: | ||
+ | |||
+ | ===== Models representing things (model.thing.*) ===== | ||
+ | |||
+ | Example: | ||
+ | |||
+ | asset model.thing.possessed { | ||
+ | path = " | ||
+ | } | ||
+ | |||
+ | This asset definition should be understood as "3D model asset to be used for representing [[ded: | ||
+ | |||
+ | |||
+ | ===== Models representing player weapons (model.weapon.*) ===== | ||
+ | |||
+ | Example: | ||
+ | |||
+ | asset model.weapon.wand { | ||
+ | path = " | ||
+ | } | ||
+ | |||
+ | |||
+ | This asset is for a player weapon model that will be drawn instead of a 2D psprite. The weapon identifiers (e.g., " | ||
+ | |||
+ | You should scale weapon models so that they have an appropriate size in world units (compared to player height, for example). | ||
+ | |||
+ | |||
+ | ===== Common metadata for models ===== | ||
+ | |||
+ | |||
+ | ^ Variable ^ Description | | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | ===== Positioning ===== | ||
+ | |||
+ | |||
+ | The '' | ||
+ | |||
+ | After the model has been oriented to world space, the '' | ||
+ | |||
+ | In [[version: | ||
+ | |||
+ | |||
+ | ===== Materials ===== | ||
+ | |||
+ | |||
+ | In [[version: | ||
+ | |||
+ | The shader '' | ||
+ | |||
+ | By default, the materials found in the model file are used when finding the texture images for a model. For instance, in an MD5 model, the " | ||
+ | |||
+ | < | ||
+ | |||
+ | |||
+ | ==== Variants ==== | ||
+ | |||
+ | |||
+ | The material definition can specify multiple variants, i.e., alternative materials. Each material is a complete set of texture maps for all meshes. Any textures left unspecified are replaced with the default textures. | ||
+ | |||
+ | The simplest possible material definition simply assigns texture maps to a mesh. Meshes can be identified using the names assigned to them in the model file, or with a '' | ||
+ | |||
+ | < | ||
+ | material.@0 { | ||
+ | # ...textures maps... | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Example of multiple meshes, with only one material being used: | ||
+ | |||
+ | < | ||
+ | material { | ||
+ | @0 { | ||
+ | # ...textures maps... | ||
+ | } | ||
+ | @1 { | ||
+ | # ...textures maps... | ||
+ | } | ||
+ | mesh_with_a_name { | ||
+ | # ...textures maps... | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | In the above examples, the material being modified is the default one, called " | ||
+ | |||
+ | < | ||
+ | material { | ||
+ | variant " | ||
+ | @0 { | ||
+ | # ...textures maps... | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Variants can be defined by adding new '' | ||
+ | |||
+ | < | ||
+ | material { | ||
+ | variant " | ||
+ | @0 { | ||
+ | # ...textures maps... | ||
+ | } | ||
+ | } | ||
+ | variant " | ||
+ | @0 { | ||
+ | # ...textures maps... | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Use the '' | ||
+ | |||
+ | You can use one texture image in multiple meshes and materials — only one copy of it is stored for rendering. | ||
+ | |||
+ | **Tip about using inheritance.** If there are variants that are very similar to each other, you can use the '' | ||
+ | |||
+ | < | ||
+ | asset model.thing.ettin { | ||
+ | material { | ||
+ | @0 { | ||
+ | diffuseMap = " | ||
+ | normalMap = " | ||
+ | specularMap = " | ||
+ | emissiveMap = " | ||
+ | } | ||
+ | variant " | ||
+ | group @0 inherits model.thing.ettin.material.@0 { | ||
+ | diffuseMap = " | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | # ... | ||
+ | </ | ||
+ | |||
+ | The identifier " | ||
+ | |||
+ | |||
+ | ==== Texture maps ==== | ||
+ | |||
+ | |||
+ | The pixel size of the texture maps has no meaning: texture coordinates are always normalized in the range 0...1, and are mapped to the size of the texture being used. This enables having alternative texture maps (e.g., low and high resolution) and choosing one at package load time. | ||
+ | |||
+ | ^ Material metadata | ||
+ | ^ Variable^ Description | | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | |||
+ | Example: | ||
+ | |||
+ | < | ||
+ | asset model.thing.possessed { | ||
+ | path = " | ||
+ | material.@0 { | ||
+ | heightMap = " | ||
+ | emissiveMap = " | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Selecting a material for drawing ==== | ||
+ | |||
+ | |||
+ | The material to use for drawing is specified in the '' | ||
+ | |||
+ | render { | ||
+ | material = " | ||
+ | } | ||
+ | |||
+ | If omitted, the default material (called " | ||
+ | |||
+ | Additionally, | ||
+ | |||
+ | < | ||
+ | render { | ||
+ | pass " | ||
+ | material = " | ||
+ | # ...other pass parameters... | ||
+ | } | ||
+ | } | ||
+ | |||
+ | animation { | ||
+ | timeline " | ||
+ | # setting the main material | ||
+ | script at 0.0 { material = " | ||
+ | script at 1.0 { material = " | ||
+ | } | ||
+ | timeline " | ||
+ | # setting a pass-specific material | ||
+ | script at 0.5 { effects.material = " | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Animations ===== | ||
+ | |||
+ | |||
+ | Unlike in the past, 3D model animation is done in terms of animation sequences rather than individual keyframes or states. This means that the animation sequences defined in the model file are played as specified in the file; the model asset' | ||
+ | |||
+ | In [[version: | ||
+ | |||
+ | ^ Animation metadata | ||
+ | ^ Variable ^ Description | | ||
+ | | '' | ||
+ | |||
+ | |||
+ | The example below causes the state '' | ||
+ | |||
+ | |||
+ | animation { | ||
+ | state POSS_STND { | ||
+ | sequence @0 {} | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | ==== Sequences ==== | ||
+ | |||
+ | |||
+ | ^ Variable^ Description | | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | |||
+ | You may define any number of sequences for one state. Every sequence must have a unique name. The sequences are checked in the order they are found in the definitions. Below is an example of two variations of an animation sequence. Sequence #0 is started with a 50% probability; | ||
+ | |||
+ | |||
+ | state animation.POSS_STND { | ||
+ | sequence @0 { prob = 0.5 } | ||
+ | sequence @1 { prob = 1.0 } | ||
+ | } | ||
+ | |||
+ | |||
+ | The following shows a more complete example of player weapon animations: | ||
+ | |||
+ | < | ||
+ | asset model.weapon.wand { | ||
+ | path = " | ||
+ | | ||
+ | front <1, 0, 0> | ||
+ | up <0, 0, 1> | ||
+ | | ||
+ | animation { | ||
+ | state MWANDUP { | ||
+ | sequence " | ||
+ | } | ||
+ | state MWANDDOWN { | ||
+ | sequence " | ||
+ | } | ||
+ | state MWANDREADY { | ||
+ | sequence " | ||
+ | prob = 0.1 | ||
+ | looping = True | ||
+ | priority = 0 | ||
+ | } | ||
+ | sequence " | ||
+ | looping = True | ||
+ | priority = 0 | ||
+ | } | ||
+ | } | ||
+ | state MWANDATK_1 { | ||
+ | sequence " | ||
+ | sequence " | ||
+ | sequence " | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | There are a couple of things to note: | ||
+ | * The idle animations have priority 0, putting them below the default of 1. This means the idle sequences will not interrupt the (un)equip or attack animations. | ||
+ | * There are two looping variants of the idle animations. However, whenever any of them finishes a loop, it may trigger any of the variants. Therefore, " | ||
+ | * There are three different attack sequences, out of which one is randomly chosen for each attack. The probabilities are evenly distributed. | ||
+ | |||
+ | |||
+ | ==== Timelines ==== | ||
+ | |||
+ | |||
+ | Animation sequences can optionally define a timeline of scripted actions. The scripts are written using [[: | ||
+ | |||
+ | Timelines can be defined either inside '' | ||
+ | |||
+ | Example of a timeline inside a '' | ||
+ | |||
+ | |||
+ | state animation.MWANDUP { | ||
+ | sequence " | ||
+ | timeline { | ||
+ | script at 0 { print "Equip animation started" | ||
+ | script at 0.5 { print "Half a second gone" } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | Example of a shared timeline: | ||
+ | |||
+ | |||
+ | animation { | ||
+ | timeline " | ||
+ | script at 0 { | ||
+ | uEmission.setValueFrom(1, | ||
+ | } | ||
+ | } | ||
+ | | ||
+ | state MWANDATK_1 { | ||
+ | sequence " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | The '' | ||
+ | |||
+ | |||
+ | ==== Animation variables ==== | ||
+ | |||
+ | |||
+ | Variables can be declared inside the '' | ||
+ | |||
+ | |||
+ | animation { | ||
+ | variable gunRotation { | ||
+ | node = " | ||
+ | axis <0, 0, 1> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | Currently (starting with build 1844) these variables can be used for specifying additional rotation angles that affect parts of the model skeleton. | ||
+ | |||
+ | In the above example, the variable '' | ||
+ | |||
+ | This means that the variables support two, independent operating modes: you can control the rotation angle directly by using '' | ||
+ | |||
+ | ^ Animation variable declaration | ||
+ | ^ Variable^ Description | | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | |||
+ | |||
+ | ===== Shaders and variables ===== | ||
+ | |||
+ | |||
+ | The '' | ||
+ | |||
+ | |||
+ | render { | ||
+ | shader = " | ||
+ | } | ||
+ | |||
+ | |||
+ | The '' | ||
+ | |||
+ | |||
+ | render { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | ==== Shaders available in net.dengine.client.renderer ==== | ||
+ | |||
+ | |||
+ | See: [[https:// | ||
+ | |||
+ | |||
+ | ==== Variable definition ==== | ||
+ | |||
+ | |||
+ | ^ Variable^ Description | | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | |||
+ | |||
+ | ===== Rendering passes ===== | ||
+ | |||
+ | |||
+ | You can optionally specify exactly how the meshes of the model should be rendered. With the rendering pass definitions, | ||
+ | |||
+ | Each pass is either enabled or disabled. These flags are stored individually for each instance of the model, so they may be used during animations. | ||
+ | |||
+ | If no passes are specified, all meshes are drawn in a single pass with basic alpha blending (''< | ||
+ | |||
+ | Blending is specified using the OpenGL blending function factors and an operator: // | ||
+ | |||
+ | Here is an example of how rendering passes are defined: | ||
+ | |||
+ | |||
+ | render { | ||
+ | pass " | ||
+ | meshes <@2> | ||
+ | } | ||
+ | pass " | ||
+ | meshes <@0, @1, @3, @4> | ||
+ | blendFunc < | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | The names of the '' | ||
+ | |||
+ | |||
+ | render { | ||
+ | pass " | ||
+ | meshes <@2> | ||
+ | enabled = False | ||
+ | variable uEmission { value = 0.5 } | ||
+ | } | ||
+ | } | ||
+ | animation { | ||
+ | timeline " | ||
+ | script at 0 { | ||
+ | body.enabled = True | ||
+ | body.uEmission.setValueFrom(1, | ||
+ | } | ||
+ | script at 0.5 { | ||
+ | body.enabled = False | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | ==== Pass definition ==== | ||
+ | |||
+ | |||
+ | ^ Variable^ DScript^ Description | | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | |||
+ | |||
+ | ===== Scripting ===== | ||
+ | |||
+ | The state of 3D model assets can be manipulated via Doomsday Script. | ||
+ | |||
+ | * [[Scripting with StateAnimator]] | ||
+ | |||
+ | |||
+ | ===== Optimization tips ===== | ||
+ | |||
+ | |||
+ | The general rule of thumb for the best performance is to minimize the size of textures and the number of GL resources needed by the model. | ||
+ | |||
+ | * Each material variant reserves a separate copy of the vertex data of the model. | ||
+ | * Use as few enabled rendering passes at a time as possible. You may have a large number of disabled rendering passes, though. | ||
+ | * All textures used by a model are stored in a large atlas. This means there are no restrictions on texture sizes, i.e., they //do not// have to be powers of two (e.g., 1024x1024). However, because the atlas needs borders and other padding around the textures stored in it, it is recommended that the model' | ||
+ | |||
+ | |||
+ | |||
+ | |||