GothicVRProject / GothicVR

Fan project recreating the classic Gothic I and II experience in VR.
GNU General Public License v3.0
26 stars 2 forks source link

NPCs - animations #10

Closed JaXt0r closed 9 months ago

JaXt0r commented 1 year ago

Goal is to have NPCs behave right: i.e. have idle and walking animations.

Some hints from lmichaelis about how to achieve it with Gothic data:


Animations (and animated models generally) work like this (as far as I can remember at least):

  1. The model is requested to be loaded by, for example, a VOb
  2. The engine replaces the .3DS-extension of the indicated model with a .MDS or .MSB extension (depends on the game: G1 uses .MDS and G2 uses .MSB; solve this with a priority lookup. First try .MSB, then try .MDS then you can try extensions for non-animated models: .MDL, .MDH, .MDM, .MRM).
  3. The Model script (either MSB or MDS) is parsed with phoenix::model_script::parse
  4. From the model script, the name of the mesh and skeleton files can be retrieved (see model_script::skeleton::name). This file is looked up with priority like above: .MDL, .MDH, .MDM, .MRM
  5. The different animations and effects defined in the model script are enumerated and loaded by name (with the correct extensions; .MAN for animations, .WAV for sound effects and so on)
  6. Choosing which animation to play is a bit more finicky as far as I remember since you have to figure out which animation to play for which action. Consult OpenGothic for this.

The animations loaded in step 5. will contain a set of skeleton node indices which are animated as well as a set of samples that make up that animation. Each animation has a total of nodeCount * frameCount samples, so one sample per frame per node. Each animation also contains the name of the next animation to play, a frame-rate, a layer and a bounding box. I don't know what the bounding box represents though.

You can take a look at the Gothic ModKit for additional information: https://github.com/dzieje-khorinis/Gothic-2-Mod-Development-Kit

JaXt0r commented 1 year ago

Animations are a tricky one. ;-)

Some links on how to create them at runtime in Unity:

Animator

(dead end. No "runtime" animation features):

Playables API

(Low level. Plugs into Animator. Add animations at runtime.):

Bones:

JaXt0r commented 1 year ago

Some hints from lmichaelis about how to load the proper animation data from Phoenix: Walk animation load:

  1. load ModelScript (with array Animations[].name)
  2. Use this name, add MAN and load it as PxAnimationData
  3. Use the PxAnimationData.samples for animation
  4. PxAnimationData.nodeIndices --> Example: a skeleton with 10 bones (nodes) and 20 frames has
    • 10 node_indices and 10*20samples (i.e. 10 samples == 10 frames for bone/node1, 11...20 samples == 10 frames for node 2)
  5. PxModelHierarchy (mdh) == skeleton/bones.

Hint: Check checksums for corresponding object (mrm+mdl+...; Should match!).

JaXt0r commented 1 year ago

From OpenGothic (resources.cpp::implLoadMeshMain()):

  1. Check file ending of requested object (e.g. humans.mds)
  2. If .mds, load ModelScript
  3. check mds.skeleton.disable_mesh
    1. == false then load mesh with this name
    2. == true then skip this information and load requested object's .mdh (replace .mds with .mdh -> e.g. humans.mdh)
JaXt0r commented 1 year ago

How did Romeyyy import Animations back in the PoC days:

  1. Some tool (from modding tools) to transform animations (.mds) to .asc
  2. KeraxImpEx for .asc import (moesh+animation files are inside Modding Kit) --> transform to .fbx
  3. Unity import fbx -> Animation clips are there (hierarchical)
  4. Rig (bones weights, ...?) needed to be generated via Unity ("Create from Model")

image

Reverse engineering pro tip:

JaXt0r commented 9 months ago

Animations are imported and working since ~Oct 2023. We now need to optimize them for Unity usage (e.g. collision usage and grounding on the floor).