m3g/m3gcore11/src/m3g_object.c
changeset 0 5d03bc08d59c
child 26 15986eb6c500
equal deleted inserted replaced
-1:000000000000 0:5d03bc08d59c
       
     1 /*
       
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of the License "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: Base object class implementation
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 /*!
       
    20  * \internal
       
    21  * \file
       
    22  * \brief Base object class implementation
       
    23  *
       
    24  */
       
    25 
       
    26 #ifndef M3G_CORE_INCLUDE
       
    27 #   error included by m3g_core.c; do not compile separately.
       
    28 #endif
       
    29 
       
    30 /*----------------------------------------------------------------------
       
    31  * Constructor & destructor
       
    32  *--------------------------------------------------------------------*/
       
    33 
       
    34 /*!
       
    35  * \internal
       
    36  * \brief Constructor for all Objects
       
    37  *
       
    38  * The reference count for new objects is initialized to zero; the
       
    39  * object pointer must be stored using \c m3gSetRef, or m3gAddRef
       
    40  * called explicitly, to increase this to one.
       
    41  */
       
    42 static void m3gInitObject(Object *obj,
       
    43                           Interface *interface,
       
    44                           M3GClass classID)
       
    45 {
       
    46     M3G_ASSERT_PTR(obj);
       
    47     M3G_VALIDATE_INTERFACE(interface);
       
    48 
       
    49     M3G_ASSERT(m3gInRange(classID,
       
    50                           M3G_CLASS_ANIMATION_CONTROLLER, M3G_CLASS_WORLD));
       
    51     
       
    52     obj->classID = (M3Guint) classID;
       
    53     obj->interface = interface;
       
    54     obj->animTracks = NULL;
       
    55     obj->refCount = 0u;
       
    56     
       
    57     M3G_VALIDATE_OBJECT(obj);
       
    58 
       
    59     m3gAddChildObject(interface);
       
    60     m3gMarkObject(obj);
       
    61     
       
    62     m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_OBJECTS, 1);
       
    63     M3G_LOG2(M3G_LOG_OBJECTS, "New %s 0x%08X\n",
       
    64              m3gClassName((M3GClass) obj->classID),
       
    65              (unsigned) obj);
       
    66 }
       
    67 
       
    68 /*!
       
    69  * \internal
       
    70  * \brief Destructor for all Objects
       
    71  */
       
    72 static void m3gDestroyObject(Object *obj)
       
    73 {
       
    74     M3G_VALIDATE_OBJECT(obj);
       
    75     M3G_ASSERT(m3gIsObject(obj));
       
    76 
       
    77     if (obj->animTracks != NULL) {
       
    78         int n = m3gArraySize(obj->animTracks);
       
    79         int i;
       
    80 
       
    81         for (i = 0; i < n; ++i) {
       
    82             M3GObject hTrk = (M3GObject) m3gGetArrayElement(obj->animTracks, i);
       
    83             m3gDeleteRef(hTrk);
       
    84         }
       
    85         m3gDestroyArray(obj->animTracks, M3G_INTERFACE(obj));
       
    86         m3gFree(obj->interface, obj->animTracks);
       
    87     }
       
    88 
       
    89     m3gDelChildObject(obj->interface);
       
    90     m3gUnmarkObject(obj);
       
    91     
       
    92     m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_OBJECTS, -1);
       
    93     M3G_LOG2(M3G_LOG_OBJECTS, "Destroyed %s 0x%08X\n",
       
    94              m3gClassName((M3GClass) obj->classID),
       
    95              (unsigned) obj);
       
    96 }
       
    97 
       
    98 /*----------------------------------------------------------------------
       
    99  * Internal functions
       
   100  *--------------------------------------------------------------------*/
       
   101 
       
   102 /*!
       
   103  * \internal
       
   104  * \brief Sets an object reference to a new value and updates the
       
   105  * reference count accordingly
       
   106  *
       
   107  * Note that this may lead to the originally referenced object being
       
   108  * destroyed.
       
   109  *
       
   110  * \param ref Pointer to the reference (pointer) to set to a new value
       
   111  * \param obj New value of the reference
       
   112  */
       
   113 static void m3gSetRef(Object **ref, Object *obj)
       
   114 {
       
   115     M3G_ASSERT_PTR(ref);
       
   116 
       
   117     if (*ref != obj) {
       
   118         if (obj != NULL) {
       
   119             m3gAddRef((M3GObject) obj);
       
   120         }
       
   121         if (*ref != NULL) {
       
   122             m3gDeleteRef((M3GObject) *ref);
       
   123         }
       
   124         *ref = obj;
       
   125     }
       
   126 }
       
   127 
       
   128 #if defined(M3G_DEBUG)
       
   129 /*!
       
   130  * \internal
       
   131  * \brief Checks the integrity of an Object-derived object
       
   132  */
       
   133 static void m3gValidateObject(const void *pObj)
       
   134 {
       
   135     const Object *obj = (const Object *) pObj;
       
   136     M3G_VALIDATE_MEMBLOCK(obj);
       
   137     M3G_VALIDATE_MEMBLOCK(obj->interface);
       
   138     M3G_ASSERT(m3gInRange(obj->classID,
       
   139                           M3G_CLASS_ANIMATION_CONTROLLER, M3G_CLASS_WORLD));
       
   140     M3G_ASSERT_PTR(m3gGetVFTable(obj));
       
   141 }
       
   142 #endif /* M3G_DEBUG */
       
   143 
       
   144 
       
   145 /* ---------------- Internal Object3D functions ---------------- */
       
   146 
       
   147 /*!
       
   148  * \internal
       
   149  * \brief Default \c applyAnimation function implementation
       
   150  */
       
   151 static M3Gint m3gObjectApplyAnimation(Object *self, M3Gint time)
       
   152 {
       
   153     Interface *m3g = M3G_INTERFACE(self);
       
   154     M3Gint validity = 0x7FFFFFFF;
       
   155     M3Gint trackIndex, numTracks;
       
   156     M3Gfloat stackSampleVector[4];
       
   157     const PointerArray *tracks = self->animTracks;
       
   158 
       
   159     /* Quick exit if no animation tracks */
       
   160 
       
   161     if (tracks == NULL) {
       
   162         return validity;
       
   163     }
       
   164 
       
   165     /* Loop through the tracks. Note that the tracks are ordered so
       
   166      * that tracks targeting the same property are adjacent in the
       
   167      * array; this makes animation blending easier. */
       
   168 
       
   169     numTracks = m3gArraySize(tracks);
       
   170 
       
   171     for (trackIndex = 0; trackIndex < numTracks; ) {
       
   172         const AnimationTrack *track = (const AnimationTrack *)
       
   173             m3gGetArrayElement(tracks, trackIndex);
       
   174         const KeyframeSequence *sequence = track->sequence;
       
   175 
       
   176         M3Gint components = sequence->numComponents;
       
   177         M3Gint property = track->property;
       
   178         M3Gint nextProperty;
       
   179 
       
   180         M3Gfloat sumWeights = 0;
       
   181         M3Gfloat *sumValues;
       
   182 
       
   183         /* Collect the contributions from all the tracks targeting the
       
   184          * same property */
       
   185 
       
   186         if (components <= 4) {
       
   187             sumValues = stackSampleVector;
       
   188         }
       
   189         else {
       
   190             sumValues = (M3Gfloat *)
       
   191                 m3gAlloc(m3g, components * sizeof(M3Gfloat));
       
   192             if (sumValues == NULL) {
       
   193                 return 0;
       
   194             }
       
   195         }
       
   196 
       
   197         m3gZero(sumValues, components * sizeof(M3Gfloat));
       
   198 
       
   199         do {
       
   200             SampleInfo sampleInfo;
       
   201             
       
   202             m3gGetContribution(track, time, sumValues, &sampleInfo);
       
   203             if (sampleInfo.validity <= 0) {
       
   204                 return 0;
       
   205             }
       
   206             sumWeights += sampleInfo.weight;
       
   207             validity = M3G_MIN(validity, sampleInfo.validity);
       
   208 
       
   209             if (++trackIndex == numTracks) {
       
   210                 break;
       
   211             }
       
   212             track = (const AnimationTrack *) m3gGetArrayElement(tracks,
       
   213                                                                 trackIndex);
       
   214             nextProperty = track->property;
       
   215         } while (nextProperty == property);
       
   216 
       
   217         if (sumWeights > 0) {
       
   218             M3G_VFUNC(Object, self, updateProperty)(
       
   219                 self, property, components, sumValues);
       
   220         }
       
   221         if (sumValues != stackSampleVector) {
       
   222             m3gFree(m3g, sumValues);
       
   223         }
       
   224     }
       
   225 
       
   226     return validity;
       
   227 }
       
   228 
       
   229 /*!
       
   230  * \internal
       
   231  * \brief Default \c isCompatible function implementation
       
   232  */
       
   233 static M3Gbool m3gObjectIsCompatible(M3Gint property)
       
   234 {
       
   235     M3G_UNREF(property);
       
   236     
       
   237     return M3G_FALSE;
       
   238 }
       
   239 
       
   240 /*!
       
   241  * \internal
       
   242  * \brief Default \c updateProperty function implementation
       
   243  *
       
   244  * Silently ignoring an update request for a non-existent object
       
   245  * property does no harm, so this just asserts in debug builds and is
       
   246  * NOP otherwise.
       
   247  */
       
   248 static void m3gObjectUpdateProperty(Object *self,
       
   249                                     M3Gint property,
       
   250                                     M3Gint valueSize,
       
   251                                     const M3Gfloat *value)
       
   252 {
       
   253     M3G_UNREF(self);
       
   254     M3G_UNREF(property);
       
   255     M3G_UNREF(valueSize);
       
   256     M3G_UNREF(value);
       
   257     
       
   258     M3G_ASSERT(M3G_FALSE);
       
   259 }
       
   260 
       
   261 /*!
       
   262  * \internal
       
   263  * \brief Default \c getReferences function implementation
       
   264  */
       
   265 static M3Gint m3gObjectDoGetReferences(Object *self, Object **references)
       
   266 {
       
   267     M3Gint i;
       
   268     if (self->animTracks != NULL) {
       
   269         if (references != NULL) {
       
   270             for (i = 0; i < m3gArraySize(self->animTracks); ++i) {
       
   271                 references[i] = (Object *)m3gGetArrayElement(self->animTracks, i);
       
   272             }
       
   273         }
       
   274         return m3gArraySize(self->animTracks);
       
   275     }
       
   276     return 0;
       
   277 }
       
   278 
       
   279 /*!
       
   280  * \internal
       
   281  * \brief Default \c find implementation
       
   282  */
       
   283 static Object *m3gObjectFindID(Object *self, M3Gint userID)
       
   284 {
       
   285     M3Gint i;
       
   286 
       
   287     if (self->userID == userID) {
       
   288         return self;
       
   289     }
       
   290     
       
   291     if (self->animTracks) {
       
   292         for (i = 0; i < m3gArraySize(self->animTracks); ++i) {
       
   293             Object *found =
       
   294                 m3gFindID((Object *) m3gGetArrayElement(self->animTracks, i),
       
   295                           userID);
       
   296             if (found) {
       
   297                 return found;
       
   298             }
       
   299         }
       
   300     }
       
   301     
       
   302     return NULL;
       
   303 }
       
   304 
       
   305 /*!
       
   306  * \internal
       
   307  * \brief Default \c duplicate function implementation
       
   308  */
       
   309 static M3Gbool m3gObjectDuplicate(const Object *original,
       
   310                                   Object **clone,
       
   311                                   Object **pairs,
       
   312                                   M3Gint *numPairs)
       
   313 {
       
   314     Interface *m3g = original->interface;
       
   315     M3G_ASSERT_PTR(*clone); /* abstract class, must be derived */
       
   316 
       
   317     pairs[2 * (*numPairs)] = (Object *)original;
       
   318     pairs[2 * (*numPairs) + 1] = *clone;
       
   319     (*numPairs)++;
       
   320 
       
   321     /* Copy basic object properties */
       
   322     
       
   323     (*clone)->interface = m3g;
       
   324     (*clone)->classID   = original->classID;
       
   325     (*clone)->userID    = original->userID;
       
   326 
       
   327     /* Copy animation tracks.  This may fail due to out-of-memory, so
       
   328      * we check for that; clean-up will be handled by the derived
       
   329      * class method. */
       
   330     
       
   331     if (original->animTracks != NULL) {
       
   332         M3Gsizei numTracks = m3gArraySize(original->animTracks);
       
   333         M3Gint i;
       
   334 
       
   335         /* Allocate the track array and make sure it has enough room
       
   336          * for holding the tracks we're about to copy */
       
   337         
       
   338         PointerArray *animTracks =
       
   339             (PointerArray*) m3gAlloc(m3g, sizeof(PointerArray));
       
   340         if (animTracks == NULL) {
       
   341             return M3G_FALSE; /* out of memory */
       
   342         }        
       
   343         (*clone)->animTracks = animTracks;
       
   344 
       
   345         m3gInitArray(animTracks);
       
   346         if (!m3gEnsureArrayCapacity(animTracks, numTracks, m3g)) {
       
   347             return M3G_FALSE; /* out of memory */
       
   348         }                           
       
   349 
       
   350         /* Copy tracks one-by-one and update references.  This can no
       
   351          * longer fail, as the capacity request above has been
       
   352          * satisfied */
       
   353         
       
   354         for (i = 0; i < numTracks; ++i) {
       
   355             AnimationTrack *track =
       
   356                 (AnimationTrack *) m3gGetArrayElement(original->animTracks, i);
       
   357 
       
   358             if (m3gArrayAppend(animTracks, track, m3g) != i) {
       
   359                 M3G_ASSERT(M3G_FALSE);
       
   360             }
       
   361             m3gAddRef((Object *) track);
       
   362         }
       
   363     }
       
   364     return M3G_TRUE;
       
   365 }
       
   366 
       
   367 #if defined(M3G_LOGLEVEL)
       
   368 /*!
       
   369  * \internal
       
   370  * \brief Returns the name of an object class
       
   371  */
       
   372 static const char *m3gClassName(M3GClass classID)
       
   373 {
       
   374     switch (classID) {
       
   375     case M3G_CLASS_ANIMATION_CONTROLLER:
       
   376         return "AnimationController";
       
   377     case M3G_CLASS_ANIMATION_TRACK:
       
   378         return "AnimationTrack";
       
   379     case M3G_CLASS_APPEARANCE:
       
   380         return "Appearance";
       
   381     case M3G_CLASS_BACKGROUND:
       
   382         return "Background";
       
   383     case M3G_CLASS_CAMERA:
       
   384         return "Camera";
       
   385     case M3G_CLASS_COMPOSITING_MODE:
       
   386         return "CompositingMode";
       
   387     case M3G_CLASS_FOG:
       
   388         return "Fog";
       
   389     case M3G_CLASS_GROUP:
       
   390         return "Group";
       
   391     case M3G_CLASS_IMAGE:
       
   392         return "Image";
       
   393     case M3G_CLASS_INDEX_BUFFER:
       
   394         return "IndexBuffer";
       
   395     case M3G_CLASS_KEYFRAME_SEQUENCE:
       
   396         return "KeyframeSequence";
       
   397     case M3G_CLASS_LIGHT:
       
   398         return "Light";
       
   399     case M3G_CLASS_LOADER:
       
   400         return "Loader";
       
   401     case M3G_CLASS_MATERIAL:
       
   402         return "Material";
       
   403     case M3G_CLASS_MESH:
       
   404         return "Mesh";
       
   405     case M3G_CLASS_MORPHING_MESH:
       
   406         return "MorphingMesh";
       
   407     case M3G_CLASS_POLYGON_MODE:
       
   408         return "PolygonMode";
       
   409     case M3G_CLASS_RENDER_CONTEXT:
       
   410         return "RenderContext";
       
   411     case M3G_CLASS_SKINNED_MESH:
       
   412         return "SkinnedMesh";
       
   413     case M3G_CLASS_SPRITE:
       
   414         return "Sprite";
       
   415     case M3G_CLASS_TEXTURE:
       
   416         return "Texture";
       
   417     case M3G_CLASS_VERTEX_ARRAY:
       
   418         return "VertexArray";
       
   419     case M3G_CLASS_VERTEX_BUFFER:
       
   420         return "VertexBuffer";
       
   421     case M3G_CLASS_WORLD:
       
   422         return "World";
       
   423     default:
       
   424         return "<abstract class?>";
       
   425     }
       
   426 }
       
   427 #endif /* defined(M3G_LOGLEVEL) */
       
   428 
       
   429 /*----------------------------------------------------------------------
       
   430  * Public interface functions
       
   431  *--------------------------------------------------------------------*/
       
   432 
       
   433 /*!
       
   434  * \brief Deletes an M3G object
       
   435  *
       
   436  * Similarly to m3gDeleteRef, the object will still remain until all
       
   437  * references to it are deleted.  The difference from m3gDeleteRef is
       
   438  * mostly stylistic: m3gDeleteObject is meant to be called by the
       
   439  * owner of an object, while m3gDeleteRef should be used by users of
       
   440  * the object.  Functionally, they are equivalent in all normal use
       
   441  * cases.
       
   442  *
       
   443  * \note The only functional differences between m3gDeleteObject and
       
   444  * m3gDeleteRef are that m3gDeleteObject can be used on an object with
       
   445  * a reference count of zero, while m3gDeleteRef asserts against this
       
   446  * in debug builds; and m3gDeleteObject accepts a NULL object.
       
   447  */
       
   448 /*@access M3GObject@*/
       
   449 M3G_API void m3gDeleteObject(M3GObject hObject)
       
   450 {
       
   451     Interface *m3g;
       
   452     Object *obj = (Object *) hObject;
       
   453 
       
   454     if (obj != NULL) {
       
   455         M3G_VALIDATE_OBJECT(obj);
       
   456 
       
   457         if (obj->refCount > 0) {
       
   458             m3gDeleteRef(obj);
       
   459         }
       
   460         else {
       
   461             M3G_LOG2(M3G_LOG_REFCOUNT,
       
   462                      "Deleting %s 0x%08X\n",
       
   463                      m3gClassName((M3GClass) obj->classID),
       
   464                      (unsigned) obj);
       
   465             
       
   466             m3g = obj->interface;
       
   467             M3G_VALIDATE_INTERFACE(m3g);
       
   468             
       
   469             M3G_ASSERT(m3gGetVFTable(obj)->destroy != NULL);
       
   470             
       
   471             M3G_VFUNC(Object, obj, destroy)(obj);
       
   472             m3gFree(m3g, obj);
       
   473         }
       
   474     }
       
   475 }
       
   476 
       
   477 /*!
       
   478  * \brief Notifies that a new reference to an object has been created
       
   479  *
       
   480  * An object will not be deleted while references to it exist.
       
   481  */
       
   482 M3G_API void m3gAddRef(M3GObject hObject)
       
   483 {
       
   484     Object *obj = (Object *) hObject;
       
   485     M3G_VALIDATE_OBJECT(obj);
       
   486 
       
   487     M3G_LOG3(M3G_LOG_REFCOUNT,
       
   488              "Adding ref to 0x%08X (%s), new count %u\n",
       
   489              (unsigned) obj,
       
   490              m3gClassName((M3GClass) obj->classID),
       
   491              (unsigned) (obj->refCount + 1));
       
   492 
       
   493     M3G_ASSERT(obj->refCount < 0xFFFFFF);
       
   494     ++obj->refCount;
       
   495 }
       
   496 
       
   497 /*!
       
   498  * \brief Notifies that a reference to an object has been deleted
       
   499  *
       
   500  * If the reference count for an object reaches zero, the object is
       
   501  * automatically destroyed.
       
   502  */
       
   503 M3G_API void m3gDeleteRef(M3GObject hObject)
       
   504 {
       
   505     Object *obj = (Object *) hObject;
       
   506     M3G_VALIDATE_OBJECT(obj);
       
   507 
       
   508     M3G_ASSERT(obj->refCount > 0);
       
   509 
       
   510     M3G_LOG3(M3G_LOG_REFCOUNT,
       
   511              "Deleting ref to 0x%08X (%s), new count %u\n",
       
   512              (unsigned) obj,
       
   513              m3gClassName((M3GClass) obj->classID),
       
   514              (unsigned) (obj->refCount - 1));
       
   515 
       
   516     if (--obj->refCount == 0) {
       
   517         m3gDeleteObject(hObject);
       
   518     }
       
   519 }
       
   520 
       
   521 /*!
       
   522  * \brief Returns the run-time class of an object
       
   523  */
       
   524 M3G_API M3GClass m3gGetClass(M3GObject hObject)
       
   525 {
       
   526     Object *obj = (Object *) hObject;
       
   527     M3G_VALIDATE_OBJECT(obj);
       
   528     return M3G_CLASS(obj);
       
   529 }
       
   530 
       
   531 /*!
       
   532  * \brief Returns the interface owning this object
       
   533  */
       
   534 M3G_API M3GInterface m3gGetObjectInterface(M3GObject hObject)
       
   535 {
       
   536     Object *obj = (Object *) hObject;
       
   537     M3G_VALIDATE_OBJECT(obj);
       
   538     return obj->interface;
       
   539 }
       
   540 
       
   541 /* ---------------- Object3D functions ---------------- */
       
   542 
       
   543 /*!
       
   544  *
       
   545  */
       
   546 M3G_API M3Gint m3gAddAnimationTrack(M3GObject hObject,
       
   547                                     M3GAnimationTrack hAnimationTrack)
       
   548 {
       
   549     AnimationTrack *track = (AnimationTrack *)hAnimationTrack;
       
   550     Object *obj = (Object *) hObject;
       
   551     Interface *m3g = M3G_INTERFACE(obj);
       
   552     M3G_VALIDATE_OBJECT(obj);
       
   553 
       
   554     /* Check for errors */
       
   555 
       
   556     if (!M3G_VFUNC(Object, obj, isCompatible)(track->property)) {
       
   557         m3gRaiseError(m3g, M3G_INVALID_OBJECT);
       
   558         return -1;
       
   559     }
       
   560 
       
   561      /* Allocate animation track array only when adding animations for
       
   562       * the first time */
       
   563 
       
   564     if (obj->animTracks == NULL) {
       
   565         obj->animTracks = (PointerArray*) m3gAlloc(m3g, sizeof(PointerArray));
       
   566         if (obj->animTracks == NULL) return 0;
       
   567         m3gInitArray(obj->animTracks);
       
   568     }
       
   569 
       
   570     /*  The animation tracks are maintained in a sorted order based on
       
   571      *  their target property enumerations.  This keeps all tracks
       
   572      *  targeting the same property adjacent so that we can easily
       
   573      *  handle animation blending. */
       
   574     {
       
   575         PointerArray *trackArray = obj->animTracks;
       
   576         M3Gsizei numTracks = m3gArraySize(trackArray);
       
   577         M3Gint i;
       
   578 
       
   579         for (i = 0; i < numTracks; ++i) {
       
   580 
       
   581             const AnimationTrack *arrayTrack =
       
   582                 (const AnimationTrack *) m3gGetArrayElement(trackArray, i);
       
   583 
       
   584             if (arrayTrack->property > track->property) {
       
   585                 break;
       
   586             }
       
   587 
       
   588             if ((track == arrayTrack) ||
       
   589                 (   (track->property == arrayTrack->property) &&
       
   590                     (track->sequence->numComponents != arrayTrack->sequence->numComponents))) {
       
   591 
       
   592                     m3gRaiseError(m3g, M3G_INVALID_OBJECT);
       
   593                     return -1;
       
   594                 }
       
   595         }
       
   596 
       
   597         if (m3gArrayInsert(trackArray, i, track, m3g) < 0) {
       
   598             return -1;
       
   599         }
       
   600         m3gAddRef((M3GObject) track);
       
   601 
       
   602         return i;
       
   603     }
       
   604 }
       
   605 
       
   606 /*!
       
   607  *
       
   608  */
       
   609 M3G_API void m3gRemoveAnimationTrack(M3GObject hObject,
       
   610                                      M3GAnimationTrack hAnimationTrack)
       
   611 {
       
   612     AnimationTrack *track = (AnimationTrack *)hAnimationTrack;
       
   613     Object *obj = (Object *) hObject;
       
   614     M3G_VALIDATE_OBJECT(obj);
       
   615 
       
   616     /* Remove the track from the array, and if no tracks remain,
       
   617      * delete the array, too */
       
   618 
       
   619     if (track != NULL && obj->animTracks != NULL) {
       
   620         M3Gint i = m3gArrayFind(obj->animTracks, track);
       
   621 
       
   622         if (i != -1) {
       
   623             m3gArrayDelete(obj->animTracks, i);
       
   624             m3gDeleteRef((Object *) track);
       
   625 
       
   626             if (m3gArraySize(obj->animTracks) == 0) {
       
   627                 m3gDestroyArray(obj->animTracks, M3G_INTERFACE(obj));
       
   628                 m3gFree(M3G_INTERFACE(obj), obj->animTracks);
       
   629                 obj->animTracks = NULL;
       
   630             }
       
   631         }
       
   632     }
       
   633 }
       
   634 
       
   635 /*!
       
   636  *
       
   637  */
       
   638 M3G_API M3Gint m3gGetAnimationTrackCount(M3GObject hObject)
       
   639 {
       
   640     Object *obj = (Object *) hObject;
       
   641     M3G_VALIDATE_OBJECT(obj);
       
   642 
       
   643     return (obj->animTracks == NULL ? 0 : m3gArraySize(obj->animTracks));
       
   644 }
       
   645 
       
   646 M3G_API M3GAnimationTrack m3gGetAnimationTrack(M3GObject hObject, M3Gint idx)
       
   647 {
       
   648     Object *obj = (Object *) hObject;
       
   649     M3G_VALIDATE_OBJECT(obj);
       
   650 
       
   651     /* idx must be in range [0, to size of array - 1] */
       
   652     if (obj->animTracks == NULL
       
   653             || !m3gInRange(idx, 0, m3gArraySize(obj->animTracks) - 1)) {
       
   654         m3gRaiseError(M3G_INTERFACE(obj), M3G_INVALID_INDEX);
       
   655         return NULL;
       
   656     }
       
   657 
       
   658     return (M3GAnimationTrack) m3gGetArrayElement(obj->animTracks, idx);
       
   659 }
       
   660 
       
   661 M3G_API M3Gint m3gAnimate(M3GObject hObject, M3Gint time)
       
   662 {
       
   663     M3Gint validity;
       
   664     Object *obj = (Object *) hObject;
       
   665 
       
   666     M3G_LOG2(M3G_LOG_STAGES,
       
   667              "Animating %s 0x%08X\n",
       
   668              m3gClassName((M3GClass) obj->classID), (unsigned) obj);
       
   669     
       
   670     M3G_VALIDATE_OBJECT(obj);
       
   671     
       
   672     M3G_BEGIN_PROFILE(M3G_INTERFACE(obj), M3G_PROFILE_ANIM);
       
   673     validity = M3G_VFUNC(Object, obj, applyAnimation)(obj, time);
       
   674     M3G_END_PROFILE(M3G_INTERFACE(obj), M3G_PROFILE_ANIM);
       
   675     
       
   676     return validity;
       
   677 }
       
   678 
       
   679 /*!
       
   680  * \brief Sets userID for this object
       
   681 */
       
   682 M3G_API void m3gSetUserID(M3GObject hObject, M3Gint userID)
       
   683 {
       
   684     Object *obj = (Object *) hObject;
       
   685     M3G_VALIDATE_OBJECT(obj);
       
   686 	obj->userID = userID;
       
   687 }
       
   688 
       
   689 /*!
       
   690  * \brief Gets userID of this object
       
   691 */
       
   692 M3G_API M3Gint m3gGetUserID(M3GObject hObject)
       
   693 {
       
   694     Object *obj = (Object *) hObject;
       
   695     M3G_VALIDATE_OBJECT(obj);
       
   696 
       
   697 	return obj->userID;
       
   698 }
       
   699 
       
   700 /*!
       
   701  * \brief Creates a duplicate of this Object3D
       
   702 */
       
   703 M3G_API M3GObject m3gDuplicate(M3GObject hObject, M3GObject *hReferences)
       
   704 {
       
   705     Object **references = (Object **)hReferences;
       
   706     const Object *obj = (const Object *) hObject;
       
   707     Object *clone = NULL;
       
   708     M3Gint numRef = 0;
       
   709 
       
   710     M3G_LOG2(M3G_LOG_STAGES|M3G_LOG_OBJECTS,
       
   711              "Duplicating %s 0x%08X\n",
       
   712              m3gClassName((M3GClass) obj->classID), (unsigned) obj);
       
   713 
       
   714     M3G_VALIDATE_OBJECT(obj);
       
   715     
       
   716     /* Clone the object (or subtree) */
       
   717     if (!M3G_VFUNC(Object, obj, duplicate)(obj, &clone, references, &numRef)) {
       
   718         m3gDeleteObject(clone);
       
   719         return NULL; /* failed; out of memory will have been thrown */
       
   720     }
       
   721 
       
   722     /* NOTE This will have to change (the virtual function moved to
       
   723      * the Object class) if we add classes where child objects may get
       
   724      * duplicated */
       
   725     
       
   726     if (clone->classID == M3G_CLASS_CAMERA ||
       
   727         clone->classID == M3G_CLASS_GROUP ||
       
   728         clone->classID == M3G_CLASS_WORLD ||
       
   729         clone->classID == M3G_CLASS_LIGHT ||
       
   730         clone->classID == M3G_CLASS_MESH ||
       
   731         clone->classID == M3G_CLASS_MORPHING_MESH ||
       
   732         clone->classID == M3G_CLASS_SKINNED_MESH ||
       
   733         clone->classID == M3G_CLASS_SPRITE)
       
   734         M3G_VFUNC(Node, clone, updateDuplicateReferences)((Node *)obj, references, numRef);
       
   735 
       
   736     return clone;
       
   737 }
       
   738 
       
   739 /*!
       
   740  * \brief Checks the length of the references array and calls virtual
       
   741  * getReferences
       
   742  */
       
   743 M3G_API M3Gint m3gGetReferences(M3GObject hObject,
       
   744                                 M3GObject *references,
       
   745                                 M3Gint length)
       
   746 {
       
   747     Object *obj = (Object *) hObject;
       
   748     M3G_VALIDATE_OBJECT(obj);
       
   749     if (references != NULL) {
       
   750         int num = M3G_VFUNC(Object, obj, getReferences)(obj, NULL);
       
   751         if (length < num) {
       
   752             m3gRaiseError(obj->interface, M3G_INVALID_OBJECT);
       
   753             return 0;
       
   754         }
       
   755     }
       
   756     return M3G_VFUNC(Object, obj, getReferences)(obj, (Object **)references);
       
   757 }
       
   758 
       
   759 /*!
       
   760  * \brief Uses m3gGetReferences to find given userID
       
   761  */
       
   762 M3G_API M3GObject m3gFind(M3GObject hObject, M3Gint userID)
       
   763 {
       
   764     Object *obj = (Object *) hObject;
       
   765 
       
   766     M3G_LOG3(M3G_LOG_STAGES, "Finding ID 0x%08X (%d) in 0x%08X\n",
       
   767              (unsigned) userID, userID, (unsigned) obj);
       
   768     
       
   769     M3G_VALIDATE_OBJECT(obj);
       
   770 
       
   771     if (obj->userID == userID) {
       
   772         return obj;
       
   773     }
       
   774     
       
   775     return M3G_VFUNC(Object, obj, find)(obj, userID);
       
   776 }
       
   777