nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2018, assimp team nuclear@0: nuclear@0: nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** nuclear@0: * @file anim.h nuclear@0: * @brief Defines the data structures in which the imported animations nuclear@0: * are returned. nuclear@0: */ nuclear@0: #pragma once nuclear@0: #ifndef AI_ANIM_H_INC nuclear@0: #define AI_ANIM_H_INC nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: extern "C" { nuclear@0: #endif nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** A time-value pair specifying a certain 3D vector for the given time. */ nuclear@0: struct aiVectorKey nuclear@0: { nuclear@0: /** The time of this key */ nuclear@0: double mTime; nuclear@0: nuclear@0: /** The value of this key */ nuclear@0: C_STRUCT aiVector3D mValue; nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: nuclear@0: /// @brief The default constructor. nuclear@0: aiVectorKey() AI_NO_EXCEPT nuclear@0: : mTime( 0.0 ) nuclear@0: , mValue() { nuclear@0: // empty nuclear@0: } nuclear@0: nuclear@0: /// @brief Construction from a given time and key value. nuclear@0: nuclear@0: aiVectorKey(double time, const aiVector3D& value) nuclear@0: : mTime( time ) nuclear@0: , mValue( value ) { nuclear@0: // empty nuclear@0: } nuclear@0: nuclear@0: typedef aiVector3D elem_type; nuclear@0: nuclear@0: // Comparison operators. For use with std::find(); nuclear@0: bool operator == (const aiVectorKey& rhs) const { nuclear@0: return rhs.mValue == this->mValue; nuclear@0: } nuclear@0: bool operator != (const aiVectorKey& rhs ) const { nuclear@0: return rhs.mValue != this->mValue; nuclear@0: } nuclear@0: nuclear@0: // Relational operators. For use with std::sort(); nuclear@0: bool operator < (const aiVectorKey& rhs ) const { nuclear@0: return mTime < rhs.mTime; nuclear@0: } nuclear@0: bool operator > (const aiVectorKey& rhs ) const { nuclear@0: return mTime > rhs.mTime; nuclear@0: } nuclear@0: #endif // __cplusplus nuclear@0: }; nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** A time-value pair specifying a rotation for the given time. nuclear@0: * Rotations are expressed with quaternions. */ nuclear@0: struct aiQuatKey nuclear@0: { nuclear@0: /** The time of this key */ nuclear@0: double mTime; nuclear@0: nuclear@0: /** The value of this key */ nuclear@0: C_STRUCT aiQuaternion mValue; nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: aiQuatKey() AI_NO_EXCEPT nuclear@0: : mTime( 0.0 ) nuclear@0: , mValue() { nuclear@0: // empty nuclear@0: } nuclear@0: nuclear@0: /** Construction from a given time and key value */ nuclear@0: aiQuatKey(double time, const aiQuaternion& value) nuclear@0: : mTime (time) nuclear@0: , mValue (value) nuclear@0: {} nuclear@0: nuclear@0: typedef aiQuaternion elem_type; nuclear@0: nuclear@0: // Comparison operators. For use with std::find(); nuclear@0: bool operator == (const aiQuatKey& rhs ) const { nuclear@0: return rhs.mValue == this->mValue; nuclear@0: } nuclear@0: bool operator != (const aiQuatKey& rhs ) const { nuclear@0: return rhs.mValue != this->mValue; nuclear@0: } nuclear@0: nuclear@0: // Relational operators. For use with std::sort(); nuclear@0: bool operator < (const aiQuatKey& rhs ) const { nuclear@0: return mTime < rhs.mTime; nuclear@0: } nuclear@0: bool operator > (const aiQuatKey& rhs ) const { nuclear@0: return mTime > rhs.mTime; nuclear@0: } nuclear@0: #endif nuclear@0: }; nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** Binds a anim-mesh to a specific point in time. */ nuclear@0: struct aiMeshKey nuclear@0: { nuclear@0: /** The time of this key */ nuclear@0: double mTime; nuclear@0: nuclear@0: /** Index into the aiMesh::mAnimMeshes array of the nuclear@0: * mesh corresponding to the #aiMeshAnim hosting this nuclear@0: * key frame. The referenced anim mesh is evaluated nuclear@0: * according to the rules defined in the docs for #aiAnimMesh.*/ nuclear@0: unsigned int mValue; nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: nuclear@0: aiMeshKey() AI_NO_EXCEPT nuclear@0: : mTime(0.0) nuclear@0: , mValue(0) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: /** Construction from a given time and key value */ nuclear@0: aiMeshKey(double time, const unsigned int value) nuclear@0: : mTime (time) nuclear@0: , mValue (value) nuclear@0: {} nuclear@0: nuclear@0: typedef unsigned int elem_type; nuclear@0: nuclear@0: // Comparison operators. For use with std::find(); nuclear@0: bool operator == (const aiMeshKey& o) const { nuclear@0: return o.mValue == this->mValue; nuclear@0: } nuclear@0: bool operator != (const aiMeshKey& o) const { nuclear@0: return o.mValue != this->mValue; nuclear@0: } nuclear@0: nuclear@0: // Relational operators. For use with std::sort(); nuclear@0: bool operator < (const aiMeshKey& o) const { nuclear@0: return mTime < o.mTime; nuclear@0: } nuclear@0: bool operator > (const aiMeshKey& o) const { nuclear@0: return mTime > o.mTime; nuclear@0: } nuclear@0: nuclear@0: #endif nuclear@0: }; nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** Binds a morph anim mesh to a specific point in time. */ nuclear@0: struct aiMeshMorphKey nuclear@0: { nuclear@0: /** The time of this key */ nuclear@0: double mTime; nuclear@0: nuclear@0: /** The values and weights at the time of this key */ nuclear@0: unsigned int *mValues; nuclear@0: double *mWeights; nuclear@0: nuclear@0: /** The number of values and weights */ nuclear@0: unsigned int mNumValuesAndWeights; nuclear@0: #ifdef __cplusplus nuclear@0: aiMeshMorphKey() AI_NO_EXCEPT nuclear@0: : mTime(0.0) nuclear@0: , mValues(0) nuclear@0: , mWeights(0) nuclear@0: , mNumValuesAndWeights(0) nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: ~aiMeshMorphKey() nuclear@0: { nuclear@0: if (mNumValuesAndWeights && mValues && mWeights) { nuclear@0: delete [] mValues; nuclear@0: delete [] mWeights; nuclear@0: } nuclear@0: } nuclear@0: #endif nuclear@0: }; nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** Defines how an animation channel behaves outside the defined time nuclear@0: * range. This corresponds to aiNodeAnim::mPreState and nuclear@0: * aiNodeAnim::mPostState.*/ nuclear@0: enum aiAnimBehaviour nuclear@0: { nuclear@0: /** The value from the default node transformation is taken*/ nuclear@0: aiAnimBehaviour_DEFAULT = 0x0, nuclear@0: nuclear@0: /** The nearest key value is used without interpolation */ nuclear@0: aiAnimBehaviour_CONSTANT = 0x1, nuclear@0: nuclear@0: /** The value of the nearest two keys is linearly nuclear@0: * extrapolated for the current time value.*/ nuclear@0: aiAnimBehaviour_LINEAR = 0x2, nuclear@0: nuclear@0: /** The animation is repeated. nuclear@0: * nuclear@0: * If the animation key go from n to m and the current nuclear@0: * time is t, use the value at (t-n) % (|m-n|).*/ nuclear@0: aiAnimBehaviour_REPEAT = 0x3, nuclear@0: nuclear@0: /** This value is not used, it is just here to force the nuclear@0: * the compiler to map this enum to a 32 Bit integer */ nuclear@0: #ifndef SWIG nuclear@0: _aiAnimBehaviour_Force32Bit = INT_MAX nuclear@0: #endif nuclear@0: }; nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** Describes the animation of a single node. The name specifies the nuclear@0: * bone/node which is affected by this animation channel. The keyframes nuclear@0: * are given in three separate series of values, one each for position, nuclear@0: * rotation and scaling. The transformation matrix computed from these nuclear@0: * values replaces the node's original transformation matrix at a nuclear@0: * specific time. nuclear@0: * This means all keys are absolute and not relative to the bone default pose. nuclear@0: * The order in which the transformations are applied is nuclear@0: * - as usual - scaling, rotation, translation. nuclear@0: * nuclear@0: * @note All keys are returned in their correct, chronological order. nuclear@0: * Duplicate keys don't pass the validation step. Most likely there nuclear@0: * will be no negative time values, but they are not forbidden also ( so nuclear@0: * implementations need to cope with them! ) */ nuclear@0: struct aiNodeAnim { nuclear@0: /** The name of the node affected by this animation. The node nuclear@0: * must exist and it must be unique.*/ nuclear@0: C_STRUCT aiString mNodeName; nuclear@0: nuclear@0: /** The number of position keys */ nuclear@0: unsigned int mNumPositionKeys; nuclear@0: nuclear@0: /** The position keys of this animation channel. Positions are nuclear@0: * specified as 3D vector. The array is mNumPositionKeys in size. nuclear@0: * nuclear@0: * If there are position keys, there will also be at least one nuclear@0: * scaling and one rotation key.*/ nuclear@0: C_STRUCT aiVectorKey* mPositionKeys; nuclear@0: nuclear@0: /** The number of rotation keys */ nuclear@0: unsigned int mNumRotationKeys; nuclear@0: nuclear@0: /** The rotation keys of this animation channel. Rotations are nuclear@0: * given as quaternions, which are 4D vectors. The array is nuclear@0: * mNumRotationKeys in size. nuclear@0: * nuclear@0: * If there are rotation keys, there will also be at least one nuclear@0: * scaling and one position key. */ nuclear@0: C_STRUCT aiQuatKey* mRotationKeys; nuclear@0: nuclear@0: /** The number of scaling keys */ nuclear@0: unsigned int mNumScalingKeys; nuclear@0: nuclear@0: /** The scaling keys of this animation channel. Scalings are nuclear@0: * specified as 3D vector. The array is mNumScalingKeys in size. nuclear@0: * nuclear@0: * If there are scaling keys, there will also be at least one nuclear@0: * position and one rotation key.*/ nuclear@0: C_STRUCT aiVectorKey* mScalingKeys; nuclear@0: nuclear@0: /** Defines how the animation behaves before the first nuclear@0: * key is encountered. nuclear@0: * nuclear@0: * The default value is aiAnimBehaviour_DEFAULT (the original nuclear@0: * transformation matrix of the affected node is used).*/ nuclear@0: C_ENUM aiAnimBehaviour mPreState; nuclear@0: nuclear@0: /** Defines how the animation behaves after the last nuclear@0: * key was processed. nuclear@0: * nuclear@0: * The default value is aiAnimBehaviour_DEFAULT (the original nuclear@0: * transformation matrix of the affected node is taken).*/ nuclear@0: C_ENUM aiAnimBehaviour mPostState; nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: aiNodeAnim() AI_NO_EXCEPT nuclear@0: : mNumPositionKeys( 0 ) nuclear@0: , mPositionKeys( 0 ) nuclear@0: , mNumRotationKeys( 0 ) nuclear@0: , mRotationKeys( 0 ) nuclear@0: , mNumScalingKeys( 0 ) nuclear@0: , mScalingKeys( 0 ) nuclear@0: , mPreState( aiAnimBehaviour_DEFAULT ) nuclear@0: , mPostState( aiAnimBehaviour_DEFAULT ) { nuclear@0: // empty nuclear@0: } nuclear@0: nuclear@0: ~aiNodeAnim() { nuclear@0: delete [] mPositionKeys; nuclear@0: delete [] mRotationKeys; nuclear@0: delete [] mScalingKeys; nuclear@0: } nuclear@0: #endif // __cplusplus nuclear@0: }; nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** Describes vertex-based animations for a single mesh or a group of nuclear@0: * meshes. Meshes carry the animation data for each frame in their nuclear@0: * aiMesh::mAnimMeshes array. The purpose of aiMeshAnim is to nuclear@0: * define keyframes linking each mesh attachment to a particular nuclear@0: * point in time. */ nuclear@0: struct aiMeshAnim nuclear@0: { nuclear@0: /** Name of the mesh to be animated. An empty string is not allowed, nuclear@0: * animated meshes need to be named (not necessarily uniquely, nuclear@0: * the name can basically serve as wild-card to select a group nuclear@0: * of meshes with similar animation setup)*/ nuclear@0: C_STRUCT aiString mName; nuclear@0: nuclear@0: /** Size of the #mKeys array. Must be 1, at least. */ nuclear@0: unsigned int mNumKeys; nuclear@0: nuclear@0: /** Key frames of the animation. May not be NULL. */ nuclear@0: C_STRUCT aiMeshKey* mKeys; nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: nuclear@0: aiMeshAnim() AI_NO_EXCEPT nuclear@0: : mNumKeys() nuclear@0: , mKeys() nuclear@0: {} nuclear@0: nuclear@0: ~aiMeshAnim() nuclear@0: { nuclear@0: delete[] mKeys; nuclear@0: } nuclear@0: nuclear@0: #endif nuclear@0: }; nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** Describes a morphing animation of a given mesh. */ nuclear@0: struct aiMeshMorphAnim nuclear@0: { nuclear@0: /** Name of the mesh to be animated. An empty string is not allowed, nuclear@0: * animated meshes need to be named (not necessarily uniquely, nuclear@0: * the name can basically serve as wildcard to select a group nuclear@0: * of meshes with similar animation setup)*/ nuclear@0: C_STRUCT aiString mName; nuclear@0: nuclear@0: /** Size of the #mKeys array. Must be 1, at least. */ nuclear@0: unsigned int mNumKeys; nuclear@0: nuclear@0: /** Key frames of the animation. May not be NULL. */ nuclear@0: C_STRUCT aiMeshMorphKey* mKeys; nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: nuclear@0: aiMeshMorphAnim() AI_NO_EXCEPT nuclear@0: : mNumKeys() nuclear@0: , mKeys() nuclear@0: {} nuclear@0: nuclear@0: ~aiMeshMorphAnim() nuclear@0: { nuclear@0: delete[] mKeys; nuclear@0: } nuclear@0: nuclear@0: #endif nuclear@0: }; nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** An animation consists of key-frame data for a number of nodes. For nuclear@0: * each node affected by the animation a separate series of data is given.*/ nuclear@0: struct aiAnimation { nuclear@0: /** The name of the animation. If the modeling package this data was nuclear@0: * exported from does support only a single animation channel, this nuclear@0: * name is usually empty (length is zero). */ nuclear@0: C_STRUCT aiString mName; nuclear@0: nuclear@0: /** Duration of the animation in ticks. */ nuclear@0: double mDuration; nuclear@0: nuclear@0: /** Ticks per second. 0 if not specified in the imported file */ nuclear@0: double mTicksPerSecond; nuclear@0: nuclear@0: /** The number of bone animation channels. Each channel affects nuclear@0: * a single node. */ nuclear@0: unsigned int mNumChannels; nuclear@0: nuclear@0: /** The node animation channels. Each channel affects a single node. nuclear@0: * The array is mNumChannels in size. */ nuclear@0: C_STRUCT aiNodeAnim** mChannels; nuclear@0: nuclear@0: nuclear@0: /** The number of mesh animation channels. Each channel affects nuclear@0: * a single mesh and defines vertex-based animation. */ nuclear@0: unsigned int mNumMeshChannels; nuclear@0: nuclear@0: /** The mesh animation channels. Each channel affects a single mesh. nuclear@0: * The array is mNumMeshChannels in size. */ nuclear@0: C_STRUCT aiMeshAnim** mMeshChannels; nuclear@0: nuclear@0: /** The number of mesh animation channels. Each channel affects nuclear@0: * a single mesh and defines morphing animation. */ nuclear@0: unsigned int mNumMorphMeshChannels; nuclear@0: nuclear@0: /** The morph mesh animation channels. Each channel affects a single mesh. nuclear@0: * The array is mNumMorphMeshChannels in size. */ nuclear@0: C_STRUCT aiMeshMorphAnim **mMorphMeshChannels; nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: aiAnimation() AI_NO_EXCEPT nuclear@0: : mDuration(-1.) nuclear@0: , mTicksPerSecond(0.) nuclear@0: , mNumChannels(0) nuclear@0: , mChannels(0) nuclear@0: , mNumMeshChannels(0) nuclear@0: , mMeshChannels(0) nuclear@0: , mNumMorphMeshChannels(0) nuclear@0: , mMorphMeshChannels(0) { nuclear@0: // empty nuclear@0: } nuclear@0: nuclear@0: ~aiAnimation() { nuclear@0: // DO NOT REMOVE THIS ADDITIONAL CHECK nuclear@0: if ( mNumChannels && mChannels ) { nuclear@0: for( unsigned int a = 0; a < mNumChannels; a++) { nuclear@0: delete mChannels[ a ]; nuclear@0: } nuclear@0: nuclear@0: delete [] mChannels; nuclear@0: } nuclear@0: if (mNumMeshChannels && mMeshChannels) { nuclear@0: for( unsigned int a = 0; a < mNumMeshChannels; a++) { nuclear@0: delete mMeshChannels[a]; nuclear@0: } nuclear@0: nuclear@0: delete [] mMeshChannels; nuclear@0: } nuclear@0: if (mNumMorphMeshChannels && mMorphMeshChannels) { nuclear@0: for( unsigned int a = 0; a < mNumMorphMeshChannels; a++) { nuclear@0: delete mMorphMeshChannels[a]; nuclear@0: } nuclear@0: nuclear@0: delete [] mMorphMeshChannels; nuclear@0: } nuclear@0: } nuclear@0: #endif // __cplusplus nuclear@0: }; nuclear@0: nuclear@0: #ifdef __cplusplus nuclear@0: nuclear@0: } nuclear@0: nuclear@0: /// @brief Some C++ utilities for inter- and extrapolation nuclear@0: namespace Assimp { nuclear@0: nuclear@0: // --------------------------------------------------------------------------- nuclear@0: /** nuclear@0: * @brief CPP-API: Utility class to simplify interpolations of various data types. nuclear@0: * nuclear@0: * The type of interpolation is chosen automatically depending on the nuclear@0: * types of the arguments. nuclear@0: */ nuclear@0: template nuclear@0: struct Interpolator nuclear@0: { nuclear@0: // ------------------------------------------------------------------ nuclear@0: /** @brief Get the result of the interpolation between a,b. nuclear@0: * nuclear@0: * The interpolation algorithm depends on the type of the operands. nuclear@0: * aiQuaternion's and aiQuatKey's SLERP, the rest does a simple nuclear@0: * linear interpolation. */ nuclear@0: void operator () (T& out,const T& a, const T& b, ai_real d) const { nuclear@0: out = a + (b-a)*d; nuclear@0: } nuclear@0: }; // ! Interpolator nuclear@0: nuclear@0: //! @cond Never nuclear@0: nuclear@0: template <> nuclear@0: struct Interpolator { nuclear@0: void operator () (aiQuaternion& out,const aiQuaternion& a, nuclear@0: const aiQuaternion& b, ai_real d) const nuclear@0: { nuclear@0: aiQuaternion::Interpolate(out,a,b,d); nuclear@0: } nuclear@0: }; // ! Interpolator nuclear@0: nuclear@0: template <> nuclear@0: struct Interpolator { nuclear@0: void operator () (unsigned int& out,unsigned int a, nuclear@0: unsigned int b, ai_real d) const nuclear@0: { nuclear@0: out = d>0.5f ? b : a; nuclear@0: } nuclear@0: }; // ! Interpolator nuclear@0: nuclear@0: template <> nuclear@0: struct Interpolator { nuclear@0: void operator () (aiVector3D& out,const aiVectorKey& a, nuclear@0: const aiVectorKey& b, ai_real d) const nuclear@0: { nuclear@0: Interpolator ipl; nuclear@0: ipl(out,a.mValue,b.mValue,d); nuclear@0: } nuclear@0: }; // ! Interpolator nuclear@0: nuclear@0: template <> nuclear@0: struct Interpolator { nuclear@0: void operator () (aiQuaternion& out, const aiQuatKey& a, nuclear@0: const aiQuatKey& b, ai_real d) const nuclear@0: { nuclear@0: Interpolator ipl; nuclear@0: ipl(out,a.mValue,b.mValue,d); nuclear@0: } nuclear@0: }; // ! Interpolator nuclear@0: nuclear@0: template <> nuclear@0: struct Interpolator { nuclear@0: void operator () (unsigned int& out, const aiMeshKey& a, nuclear@0: const aiMeshKey& b, ai_real d) const nuclear@0: { nuclear@0: Interpolator ipl; nuclear@0: ipl(out,a.mValue,b.mValue,d); nuclear@0: } nuclear@0: }; // ! Interpolator nuclear@0: nuclear@0: //! @endcond nuclear@0: nuclear@0: } // ! end namespace Assimp nuclear@0: nuclear@0: #endif // __cplusplus nuclear@0: nuclear@0: #endif // AI_ANIM_H_INC