Home > other >  Trying to get only the animations from a GLTF file
Trying to get only the animations from a GLTF file

Time:01-25

I would like to obtain from every kind of gltfAsset only the animations in it to create an animation file. Someone know if a library can help me or if there is a easiest way to do it ?

private async createAnimationAssets(props: Props, animations: Animation[]): Promise<CreateMetaAssetResponseDto[]> {
    const animationAssets = await Promise.all(
      animations.map(async (animation) => {
        return new Promise(async (resolve, reject) => {
          try {
            const asset = await this.assetRepository.create(
              new MetaBuilderAssetListItem({
                createdAt: this.dateService.now(),
                modifiedAt: this.dateService.now(),
                id: undefined,
                projectId: props.projectId,
                name: animation.name,
                type: "animation",
                exclude: false,
              })
            );
            if (!asset.id) return reject();
            const assetData: MetaAsset = MetaBuilderAssetMapper.defaultAsset({
              ...props,
              name: animation.name || "",
              assetId: asset.id,
              source: false,
              type: "animation",
            });
            if (props.parent) await this.insertParentPath(assetData, props.parent);
            // créer le fichier glb ui ne contient que l'animation
            const gltfAsset = fsExtra.readFileSync(`temp/${assetData.file?.filename}`);

            // const gltfAnimations = 

            gltfPipeline.gltfToGlb(gltfAnimations).then(function (results: any) {
              fsExtra.writeFileSync(`${animation.name}.glb`, results.glb);
            });
            console.log(`gltfAsset: ${gltfAsset}`);
            // lire les meta-données de mon fichier
            assetData.file = {
              filename: animation.name,
              size: 0,
              hash: assetData.file?.hash,
            };
            // console.log(assetData);
            await this.sharedbSocket.insertDoc(`assets`, asset.id, assetData);
            const response: CreateMetaAssetResponseDto = {
              ...assetData,
              id:  asset.id,
              uniqueId:  asset.id,
              createdAt: asset.createdAt,
              modifiedAt: asset.modifiedAt,
            };
            console.log(response);
            this.sharedbSocket.emitAssetCreated(response);
            return resolve(response);
          } catch (err) {
            console.error(err);
            return reject(err);
          }
        });
      })
    );
    return animationAssets.filter((asset) => asset) as CreateMetaAssetResponseDto[];
  }

if you need any additional information to help me do not hesitate to say me !

CodePudding user response:

I'd suggest using glTF Transform to extract data from, or modify, glTF files in a Node.js environment.

Start by reading the file with NodeIO:

import { NodeIO } from '@gltf-transform/core';
import { KHRONOS_EXTENSIONS } from '@gltf-transform/extensions';

const io = new NodeIO().registerExtensions( KHRONOS_EXTENSIONS );

// (a) read from disk
const document = await io.read('path/to/scene.glb');

// (b) read from memory
const document = await io.readBinary(glb); // glb is Uint8Array or Buffer

Next we'll make type definitions to hold the new animation data, if you're using TypeScript.

interface Clip {
    name: string,
    tracks: Track[],
}

interface Track {
    targetObject: string,
    targetProperty: string, // 'translation', 'rotation', 'scale', 'weights'
    interpolation: string, // 'LINEAR', 'CUBICSPLINE', 'STEP'
    times: number[],
    values: number[]
}

Then, parse the animation data from the glTF Animations, into these structures:

const clips = [] as Clip[];

for (const animation of document.getRoot().listAnimations()) {
    const tracks = [];

    for (const channel of animation.listChannels()) {
        const sampler = channel.getSampler();
        tracks.push({
            targetObject: channel.getTargetNode().getName(),
            targetProperty: channel.getTargetPath(),
            interpolation: sampler.getInterpolation(),
            times: Array.from(sampler.getInput().getArray()),
            values: Array.from(sampler.getOutput().getArray())
        } as Track)
    }

    clips.push({ name: animation.getName(), tracks: tracks } as Clip);
}

console.log(clips);

You can do whatever you want with that clips data - it represents the raw keyframes, with each track identifying the object it affects by name. This does require that your glTF file have unique names for animated objects.

If you'd rather not write raw data, but just have a new glTF file containing nothing but the animation, you can also do that instead:

import { prune } from '@gltf-transform/functions';

// remove all mesh data
for (const mesh of document.getRoot().listMeshes()) {
    for (const prim of mesh.listPrimitives()) {
        prim.dispose();
    }
}

// clean up unused materials, textures, ...
await document.transform(prune());

// (a) write to disk
await io.write('path/to/output.glb', document);

// (b) write to bytes
const bytes = await io.writeBinary(document);

CodePudding user response:

There are many approaches, but I tell you my workflow

you are trying to extract the animation data from a GLTF file, convert it to a GLB format, and then save it as a new file. The library 'gltf-pipeline' can be used to convert GLTF to GLB. The gltfToGlb function from the library takes a GLTF object and returns a promise that resolves with the GLB data.

Library link here https://github.com/CesiumGS/gltf-pipeline

  • Related