Source

dark-hammer / src / h3dimport / anim-import.c

The branch '0.4.7' does not exist.
/***********************************************************************************
 * Copyright (c) 2013, Sepehr Taghdisian
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, 
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation 
 *   and/or other materials provided with the distribution.
 *
 ***********************************************************************************/

#include <stdio.h>
#include "core/core.h"
#include "model-import.h"
#include "engine/h3d-types.h"

#include "assimp/cimport.h"
#include "assimp/postprocess.h"
#include "assimp/scene.h"
#include "assimp/config.h"
#include "assimp/anim.h"

#define DEFAULT_FPS 30

/*************************************************************************************************
 * types
 */
struct anim_channel_ext
{
    struct h3d_anim_channel c;
    struct vec4f* pos_scale;    /* w = uniform scale */
    struct quat4f* rot;
};

struct anim_ext
{
    struct h3d_anim a;
    struct anim_channel_ext* channels;
};

/*************************************************************************************************
 * fwd declarations
 */
bool_t import_writeanim(const char* filepath, const struct anim_ext* anim);

/*************************************************************************************************/
bool_t import_anim(const struct import_params* params)
{
    const struct aiScene* scene = aiImportFileEx(params->in_filepath, 0/*aiProcess_MakeLeftHanded*/, 
        NULL);

    if (scene == NULL)  {
        printf(TERM_BOLDRED "Error: (assimp) %s\n" TERM_RESET, aiGetErrorString());
        return FALSE;
    }

    if (scene->mNumAnimations == 0) {
        printf(TERM_BOLDRED "Error: no animation exist in the file '%s'" TERM_RESET,
            params->in_filepath);
        return FALSE;
    }

    uint32 fps = params->anim_fps != 0 ? params->anim_fps : DEFAULT_FPS;
    uint32 channel_cnt = 0;
    uint32 frame_cnt = 0;
    bool_t has_scale = FALSE;

    /* channel count is the sum of all animation channels */
    /* frame_cnt is the maximum of all animation channel key counts */
    for (uint32 i = 0; i < scene->mNumAnimations; i++)  {
        const struct aiAnimation* anim = scene->mAnimations[i];
        channel_cnt += anim->mNumChannels;
        for (uint32 k = 0; k < anim->mNumChannels; k++) {
            frame_cnt = maxun(frame_cnt, maxun(anim->mChannels[k]->mNumPositionKeys, 
                maxun(anim->mChannels[k]->mNumRotationKeys, anim->mChannels[k]->mNumScalingKeys)));
        }
    }

    if (channel_cnt == 0)   {
        printf(TERM_BOLDRED "Error: no animation channels exist in the file '%s'" TERM_RESET,
            params->in_filepath);
        return FALSE;
    }

    /* parse my data from assimp data */
    struct anim_ext h3danim;
    memset(&h3danim, 0x00, sizeof(h3danim));

    /* channels */
    struct anim_channel_ext* h3dchannels = ALLOC(sizeof(struct anim_channel_ext)*channel_cnt, 0);
    ASSERT(h3dchannels != NULL);
    memset(h3dchannels, 0x00, sizeof(struct anim_channel_ext)*channel_cnt);

    uint32 channel_offset = 0;
    for (uint32 i = 0; i < scene->mNumAnimations; i++)  {
        struct aiAnimation* anim = scene->mAnimations[i];

        for (uint32 k = 0; k < anim->mNumChannels; k++) {
            struct aiNodeAnim* channel = anim->mChannels[k];
            struct anim_channel_ext* h3dchannel = &h3dchannels[k + channel_offset];

            str_safecpy(h3dchannel->c.bindto, sizeof(h3dchannel->c.bindto), channel->mNodeName.data);
            h3dchannel->pos_scale = ALLOC(sizeof(struct vec4f)*frame_cnt, 0);
            h3dchannel->rot = ALLOC(sizeof(struct quat4f)*frame_cnt, 0);
            ASSERT(h3dchannel->pos_scale && h3dchannel->rot);

            struct vec3f pos;
            struct quat4f quat;
            fl32 scale = 1.0f;

            /* fill channel data */
            for (uint32 f = 0; f < frame_cnt; f++)  {
                if (f < channel->mNumPositionKeys)  {
                    vec3_setf(&pos, 
                        channel->mPositionKeys[f].mValue.x,
                        channel->mPositionKeys[f].mValue.y,
                        channel->mPositionKeys[f].mValue.z);
                }

                if (f < channel->mNumRotationKeys)  {
                    quat_setf(&quat, 
                        channel->mRotationKeys[f].mValue.x,
                        channel->mRotationKeys[f].mValue.y,
                        channel->mRotationKeys[f].mValue.z,
                        channel->mRotationKeys[f].mValue.w);
                }

                if (f < channel->mNumScalingKeys)   {
                    scale = (channel->mScalingKeys[f].mValue.x + 
                        channel->mScalingKeys[f].mValue.y + 
                        channel->mScalingKeys[f].mValue.z) / 3.0f;
                    has_scale = !math_isequal(scale, 1.0f);
                }

                if (params->zup)    {
                    swapf(&pos.y, &pos.z);
                    swapf(&quat.y, &quat.z);
                }   else    {
                    pos.z = -pos.z;
                    quat.z = -quat.z;   
                }
                quat_inv(&quat, &quat);

                vec4_setf(&h3dchannel->pos_scale[f], pos.x, pos.y, pos.z, scale);
                quat_setf(&h3dchannel->rot[f], quat.x, quat.y, quat.z, quat.w);
            }
        }

        channel_offset += anim->mNumChannels;
    }

    /* write to file */
    h3danim.a.channel_cnt = channel_cnt;
    h3danim.a.frame_cnt = frame_cnt;
    h3danim.a.has_scale = has_scale;
    h3danim.a.fps = fps;
    h3danim.channels = h3dchannels;

    bool_t r = import_writeanim(params->out_filepath, &h3danim);

    /* report */
    if (r)  {
        printf(TERM_BOLDGREEN "ok, saved: \"%s\".\n" TERM_RESET, params->out_filepath);
        
        if (params->verbose)    {
            printf(TERM_WHITE);
            printf("Animation report:\n"
                "  animation count: %d\n"
                "  frame count: %d\n"
                "  channel count: %d\n"
                "  has_scale: %s\n"
                "  fps: %d\n",
                scene->mNumAnimations,
                frame_cnt,
                channel_cnt,
                has_scale ? "yes" : "no",
                fps);
            printf(TERM_RESET);
        }
    }

    /* cleanup */
    for (uint32 i = 0; i < h3danim.a.channel_cnt; i++)    {
        if (h3danim.channels[i].pos_scale != NULL)
            FREE(h3danim.channels[i].pos_scale);
        if (h3danim.channels[i].rot != NULL)
            FREE(h3danim.channels[i].rot);
    }
    FREE(h3danim.channels);
    aiReleaseImport(scene);

    return r;
}

bool_t import_writeanim(const char* filepath, const struct anim_ext* anim)
{
    /* write to temp file and move it later */
    char filepath_tmp[DH_PATH_MAX];
    strcat(strcpy(filepath_tmp, filepath), ".tmp");
    FILE* f = fopen(filepath_tmp, "wb");
    if (f == NULL)  {
        printf(TERM_BOLDRED "Error: failed to open file '%s' for writing\n" TERM_RESET, filepath);
        return FALSE;
    }

    /* header */
    struct h3d_header header;
    header.sign = H3D_SIGN;
    header.type = H3D_ANIM;
    header.version = H3D_VERSION_11;
    header.data_offset = sizeof(struct h3d_header);
    fwrite(&header, sizeof(header), 1, f);

    /* anim descriptor */
    fwrite(&anim->a, sizeof(struct h3d_anim), 1, f);

    /* channels */
    for (uint32 i = 0; i < anim->a.channel_cnt; i++)    {
        /* channel descriptor */
        fwrite(&anim->channels[i].c, sizeof(struct h3d_anim_channel), 1, f);

        /* channel data */
        fwrite(anim->channels[i].pos_scale, sizeof(struct vec4f), anim->a.frame_cnt, f);
        fwrite(anim->channels[i].rot, sizeof(struct quat4f), anim->a.frame_cnt, f);
    }

    fclose(f);
    
    /* move back the file */
    return util_movefile(filepath, filepath_tmp);
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.