Update contrib.
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
 
     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".
 
     9 * Initial Contributors:
 
    10 * Nokia Corporation - initial contribution.
 
    14 * Description: Base object class implementation
 
    22  * \brief Base object class implementation
 
    26 #ifndef M3G_CORE_INCLUDE
 
    27 #   error included by m3g_core.c; do not compile separately.
 
    30 /*----------------------------------------------------------------------
 
    31  * Constructor & destructor
 
    32  *--------------------------------------------------------------------*/
 
    36  * \brief Constructor for all Objects
 
    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.
 
    42 static void m3gInitObject(Object *obj,
 
    47     M3G_VALIDATE_INTERFACE(interface);
 
    49     M3G_ASSERT(m3gInRange(classID,
 
    50                           M3G_CLASS_ANIMATION_CONTROLLER, M3G_CLASS_WORLD));
 
    52     obj->classID = (M3Guint) classID;
 
    53     obj->interface = interface;
 
    54     obj->animTracks = NULL;
 
    57     M3G_VALIDATE_OBJECT(obj);
 
    59     m3gAddChildObject(interface, obj);
 
    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),
 
    70  * \brief Destructor for all Objects
 
    72 static void m3gDestroyObject(Object *obj)
 
    74     M3G_VALIDATE_OBJECT(obj);
 
    75     M3G_ASSERT(m3gIsObject(obj));
 
    77     if (obj->animTracks != NULL) {
 
    78         int n = m3gArraySize(obj->animTracks);
 
    81         for (i = 0; i < n; ++i) {
 
    82             M3GObject hTrk = (M3GObject) m3gGetArrayElement(obj->animTracks, i);
 
    85         m3gDestroyArray(obj->animTracks, M3G_INTERFACE(obj));
 
    86         m3gFree(obj->interface, obj->animTracks);
 
    89     m3gDelChildObject(obj->interface, obj);
 
    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),
 
    98 /*----------------------------------------------------------------------
 
   100  *--------------------------------------------------------------------*/
 
   104  * \brief Sets an object reference to a new value and updates the
 
   105  * reference count accordingly
 
   107  * Note that this may lead to the originally referenced object being
 
   110  * \param ref Pointer to the reference (pointer) to set to a new value
 
   111  * \param obj New value of the reference
 
   113 static void m3gSetRef(Object **ref, Object *obj)
 
   119             m3gAddRef((M3GObject) obj);
 
   122             m3gDeleteRef((M3GObject) *ref);
 
   128 #if defined(M3G_DEBUG)
 
   131  * \brief Checks the integrity of an Object-derived object
 
   133 static void m3gValidateObject(const void *pObj)
 
   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));
 
   142 #endif /* M3G_DEBUG */
 
   145 /* ---------------- Internal Object3D functions ---------------- */
 
   149  * \brief Default \c applyAnimation function implementation
 
   151 static M3Gint m3gObjectApplyAnimation(Object *self, M3Gint time)
 
   153     Interface *m3g = M3G_INTERFACE(self);
 
   154     M3Gint validity = 0x7FFFFFFF;
 
   155     M3Gint trackIndex, numTracks;
 
   156     M3Gfloat stackSampleVector[4];
 
   157     const PointerArray *tracks = self->animTracks;
 
   159     /* Quick exit if no animation tracks */
 
   161     if (tracks == NULL) {
 
   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. */
 
   169     numTracks = m3gArraySize(tracks);
 
   171     for (trackIndex = 0; trackIndex < numTracks; ) {
 
   172         const AnimationTrack *track = (const AnimationTrack *)
 
   173             m3gGetArrayElement(tracks, trackIndex);
 
   174         const KeyframeSequence *sequence = track->sequence;
 
   176         M3Gint components = sequence->numComponents;
 
   177         M3Gint property = track->property;
 
   180         M3Gfloat sumWeights = 0;
 
   183         /* Collect the contributions from all the tracks targeting the
 
   186         if (components <= 4) {
 
   187             sumValues = stackSampleVector;
 
   190             sumValues = (M3Gfloat *)
 
   191                 m3gAlloc(m3g, components * sizeof(M3Gfloat));
 
   192             if (sumValues == NULL) {
 
   197         m3gZero(sumValues, components * sizeof(M3Gfloat));
 
   200             SampleInfo sampleInfo;
 
   202             m3gGetContribution(track, time, sumValues, &sampleInfo);
 
   203             if (sampleInfo.validity <= 0) {
 
   206             sumWeights += sampleInfo.weight;
 
   207             validity = M3G_MIN(validity, sampleInfo.validity);
 
   209             if (++trackIndex == numTracks) {
 
   212             track = (const AnimationTrack *) m3gGetArrayElement(tracks,
 
   214             nextProperty = track->property;
 
   215         } while (nextProperty == property);
 
   217         if (sumWeights > 0) {
 
   218             M3G_VFUNC(Object, self, updateProperty)(
 
   219                 self, property, components, sumValues);
 
   221         if (sumValues != stackSampleVector) {
 
   222             m3gFree(m3g, sumValues);
 
   231  * \brief Default \c isCompatible function implementation
 
   233 static M3Gbool m3gObjectIsCompatible(M3Gint property)
 
   242  * \brief Default \c updateProperty function implementation
 
   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
 
   248 static void m3gObjectUpdateProperty(Object *self,
 
   251                                     const M3Gfloat *value)
 
   255     M3G_UNREF(valueSize);
 
   258     M3G_ASSERT(M3G_FALSE);
 
   263  * \brief Default \c getReferences function implementation
 
   265 static M3Gint m3gObjectDoGetReferences(Object *self, Object **references)
 
   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);
 
   274         return m3gArraySize(self->animTracks);
 
   281  * \brief Default \c find implementation
 
   283 static Object *m3gObjectFindID(Object *self, M3Gint userID)
 
   287     if (self->userID == userID) {
 
   291     if (self->animTracks) {
 
   292         for (i = 0; i < m3gArraySize(self->animTracks); ++i) {
 
   294                 m3gFindID((Object *) m3gGetArrayElement(self->animTracks, i),
 
   307  * \brief Default \c duplicate function implementation
 
   309 static M3Gbool m3gObjectDuplicate(const Object *original,
 
   314     Interface *m3g = original->interface;
 
   315     M3G_ASSERT_PTR(*clone); /* abstract class, must be derived */
 
   317     pairs[2 * (*numPairs)] = (Object *)original;
 
   318     pairs[2 * (*numPairs) + 1] = *clone;
 
   321     /* Copy basic object properties */
 
   323     (*clone)->interface = m3g;
 
   324     (*clone)->classID   = original->classID;
 
   325     (*clone)->userID    = original->userID;
 
   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
 
   331     if (original->animTracks != NULL) {
 
   332         M3Gsizei numTracks = m3gArraySize(original->animTracks);
 
   335         /* Allocate the track array and make sure it has enough room
 
   336          * for holding the tracks we're about to copy */
 
   338         PointerArray *animTracks =
 
   339             (PointerArray*) m3gAlloc(m3g, sizeof(PointerArray));
 
   340         if (animTracks == NULL) {
 
   341             return M3G_FALSE; /* out of memory */
 
   343         (*clone)->animTracks = animTracks;
 
   345         m3gInitArray(animTracks);
 
   346         if (!m3gEnsureArrayCapacity(animTracks, numTracks, m3g)) {
 
   347             return M3G_FALSE; /* out of memory */
 
   350         /* Copy tracks one-by-one and update references.  This can no
 
   351          * longer fail, as the capacity request above has been
 
   354         for (i = 0; i < numTracks; ++i) {
 
   355             AnimationTrack *track =
 
   356                 (AnimationTrack *) m3gGetArrayElement(original->animTracks, i);
 
   358             if (m3gArrayAppend(animTracks, track, m3g) != i) {
 
   359                 M3G_ASSERT(M3G_FALSE);
 
   361             m3gAddRef((Object *) track);
 
   367 #if defined(M3G_LOGLEVEL)
 
   370  * \brief Returns the name of an object class
 
   372 static const char *m3gClassName(M3GClass classID)
 
   375     case M3G_CLASS_ANIMATION_CONTROLLER:
 
   376         return "AnimationController";
 
   377     case M3G_CLASS_ANIMATION_TRACK:
 
   378         return "AnimationTrack";
 
   379     case M3G_CLASS_APPEARANCE:
 
   381     case M3G_CLASS_BACKGROUND:
 
   383     case M3G_CLASS_CAMERA:
 
   385     case M3G_CLASS_COMPOSITING_MODE:
 
   386         return "CompositingMode";
 
   389     case M3G_CLASS_GROUP:
 
   391     case M3G_CLASS_IMAGE:
 
   393     case M3G_CLASS_INDEX_BUFFER:
 
   394         return "IndexBuffer";
 
   395     case M3G_CLASS_KEYFRAME_SEQUENCE:
 
   396         return "KeyframeSequence";
 
   397     case M3G_CLASS_LIGHT:
 
   399     case M3G_CLASS_LOADER:
 
   401     case M3G_CLASS_MATERIAL:
 
   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:
 
   415     case M3G_CLASS_TEXTURE:
 
   417     case M3G_CLASS_VERTEX_ARRAY:
 
   418         return "VertexArray";
 
   419     case M3G_CLASS_VERTEX_BUFFER:
 
   420         return "VertexBuffer";
 
   421     case M3G_CLASS_WORLD:
 
   424         return "<abstract class?>";
 
   427 #endif /* defined(M3G_LOGLEVEL) */
 
   429 /*----------------------------------------------------------------------
 
   430  * Public interface functions
 
   431  *--------------------------------------------------------------------*/
 
   434  * \brief Deletes an M3G object
 
   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
 
   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.
 
   448 /*@access M3GObject@*/
 
   449 M3G_API void m3gDeleteObject(M3GObject hObject)
 
   452     Object *obj = (Object *) hObject;
 
   455         M3G_VALIDATE_OBJECT(obj);
 
   457         if (obj->refCount > 0) {
 
   461             M3G_LOG2(M3G_LOG_REFCOUNT,
 
   462                      "Deleting %s 0x%08X\n",
 
   463                      m3gClassName((M3GClass) obj->classID),
 
   466             m3g = obj->interface;
 
   467             M3G_VALIDATE_INTERFACE(m3g);
 
   469             M3G_ASSERT(m3gGetVFTable(obj)->destroy != NULL);
 
   471             M3G_VFUNC(Object, obj, destroy)(obj);
 
   478  * \brief Notifies that a new reference to an object has been created
 
   480  * An object will not be deleted while references to it exist.
 
   482 M3G_API void m3gAddRef(M3GObject hObject)
 
   484     Object *obj = (Object *) hObject;
 
   485     M3G_VALIDATE_OBJECT(obj);
 
   487     M3G_LOG3(M3G_LOG_REFCOUNT,
 
   488              "Adding ref to 0x%08X (%s), new count %u\n",
 
   490              m3gClassName((M3GClass) obj->classID),
 
   491              (unsigned) (obj->refCount + 1));
 
   493     M3G_ASSERT(obj->refCount < 0xFFFFFF);
 
   498  * \brief Notifies that a reference to an object has been deleted
 
   500  * If the reference count for an object reaches zero, the object is
 
   501  * automatically destroyed.
 
   503 M3G_API void m3gDeleteRef(M3GObject hObject)
 
   505     Object *obj = (Object *) hObject;
 
   506     M3G_VALIDATE_OBJECT(obj);
 
   508     M3G_ASSERT(obj->refCount > 0);
 
   510     M3G_LOG3(M3G_LOG_REFCOUNT,
 
   511              "Deleting ref to 0x%08X (%s), new count %u\n",
 
   513              m3gClassName((M3GClass) obj->classID),
 
   514              (unsigned) (obj->refCount - 1));
 
   516     if (--obj->refCount == 0) {
 
   517         m3gDeleteObject(hObject);
 
   522  * \brief Returns the run-time class of an object
 
   524 M3G_API M3GClass m3gGetClass(M3GObject hObject)
 
   526     Object *obj = (Object *) hObject;
 
   527     M3G_VALIDATE_OBJECT(obj);
 
   528     return M3G_CLASS(obj);
 
   532  * \brief Returns the interface owning this object
 
   534 M3G_API M3GInterface m3gGetObjectInterface(M3GObject hObject)
 
   536     Object *obj = (Object *) hObject;
 
   537     M3G_VALIDATE_OBJECT(obj);
 
   538     return obj->interface;
 
   541 /* ---------------- Object3D functions ---------------- */
 
   546 M3G_API M3Gint m3gAddAnimationTrack(M3GObject hObject,
 
   547                                     M3GAnimationTrack hAnimationTrack)
 
   549     AnimationTrack *track = (AnimationTrack *)hAnimationTrack;
 
   550     Object *obj = (Object *) hObject;
 
   551     Interface *m3g = M3G_INTERFACE(obj);
 
   552     M3G_VALIDATE_OBJECT(obj);
 
   554     /* Check for errors */
 
   556     if (!M3G_VFUNC(Object, obj, isCompatible)(track->property)) {
 
   557         m3gRaiseError(m3g, M3G_INVALID_OBJECT);
 
   561      /* Allocate animation track array only when adding animations for
 
   564     if (obj->animTracks == NULL) {
 
   565         obj->animTracks = (PointerArray*) m3gAlloc(m3g, sizeof(PointerArray));
 
   566         if (obj->animTracks == NULL) return 0;
 
   567         m3gInitArray(obj->animTracks);
 
   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. */
 
   575         PointerArray *trackArray = obj->animTracks;
 
   576         M3Gsizei numTracks = m3gArraySize(trackArray);
 
   579         for (i = 0; i < numTracks; ++i) {
 
   581             const AnimationTrack *arrayTrack =
 
   582                 (const AnimationTrack *) m3gGetArrayElement(trackArray, i);
 
   584             if (arrayTrack->property > track->property) {
 
   588             if ((track == arrayTrack) ||
 
   589                 (   (track->property == arrayTrack->property) &&
 
   590                     (track->sequence->numComponents != arrayTrack->sequence->numComponents))) {
 
   592                     m3gRaiseError(m3g, M3G_INVALID_OBJECT);
 
   597         if (m3gArrayInsert(trackArray, i, track, m3g) < 0) {
 
   600         m3gAddRef((M3GObject) track);
 
   609 M3G_API void m3gRemoveAnimationTrack(M3GObject hObject,
 
   610                                      M3GAnimationTrack hAnimationTrack)
 
   612     AnimationTrack *track = (AnimationTrack *)hAnimationTrack;
 
   613     Object *obj = (Object *) hObject;
 
   614     M3G_VALIDATE_OBJECT(obj);
 
   616     /* Remove the track from the array, and if no tracks remain,
 
   617      * delete the array, too */
 
   619     if (track != NULL && obj->animTracks != NULL) {
 
   620         M3Gint i = m3gArrayFind(obj->animTracks, track);
 
   623             m3gArrayDelete(obj->animTracks, i);
 
   624             m3gDeleteRef((Object *) track);
 
   626             if (m3gArraySize(obj->animTracks) == 0) {
 
   627                 m3gDestroyArray(obj->animTracks, M3G_INTERFACE(obj));
 
   628                 m3gFree(M3G_INTERFACE(obj), obj->animTracks);
 
   629                 obj->animTracks = NULL;
 
   638 M3G_API M3Gint m3gGetAnimationTrackCount(M3GObject hObject)
 
   640     Object *obj = (Object *) hObject;
 
   641     M3G_VALIDATE_OBJECT(obj);
 
   643     return (obj->animTracks == NULL ? 0 : m3gArraySize(obj->animTracks));
 
   646 M3G_API M3GAnimationTrack m3gGetAnimationTrack(M3GObject hObject, M3Gint idx)
 
   648     Object *obj = (Object *) hObject;
 
   649     M3G_VALIDATE_OBJECT(obj);
 
   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);
 
   658     return (M3GAnimationTrack) m3gGetArrayElement(obj->animTracks, idx);
 
   661 M3G_API M3Gint m3gAnimate(M3GObject hObject, M3Gint time)
 
   664     Object *obj = (Object *) hObject;
 
   666     M3G_LOG2(M3G_LOG_STAGES,
 
   667              "Animating %s 0x%08X\n",
 
   668              m3gClassName((M3GClass) obj->classID), (unsigned) obj);
 
   670     M3G_VALIDATE_OBJECT(obj);
 
   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);
 
   680  * \brief Sets userID for this object
 
   682 M3G_API void m3gSetUserID(M3GObject hObject, M3Gint userID)
 
   684     Object *obj = (Object *) hObject;
 
   685     M3G_VALIDATE_OBJECT(obj);
 
   686 	obj->userID = userID;
 
   690  * \brief Gets userID of this object
 
   692 M3G_API M3Gint m3gGetUserID(M3GObject hObject)
 
   694     Object *obj = (Object *) hObject;
 
   695     M3G_VALIDATE_OBJECT(obj);
 
   701  * \brief Creates a duplicate of this Object3D
 
   703 M3G_API M3GObject m3gDuplicate(M3GObject hObject, M3GObject *hReferences)
 
   705     Object **references = (Object **)hReferences;
 
   706     const Object *obj = (const Object *) hObject;
 
   707     Object *clone = NULL;
 
   710     M3G_LOG2(M3G_LOG_STAGES|M3G_LOG_OBJECTS,
 
   711              "Duplicating %s 0x%08X\n",
 
   712              m3gClassName((M3GClass) obj->classID), (unsigned) obj);
 
   714     M3G_VALIDATE_OBJECT(obj);
 
   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 */
 
   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
 
   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);
 
   740  * \brief Checks the length of the references array and calls virtual
 
   743 M3G_API M3Gint m3gGetReferences(M3GObject hObject,
 
   744                                 M3GObject *references,
 
   747     Object *obj = (Object *) hObject;
 
   748     M3G_VALIDATE_OBJECT(obj);
 
   749     if (references != NULL) {
 
   750         int num = M3G_VFUNC(Object, obj, getReferences)(obj, NULL);
 
   752             m3gRaiseError(obj->interface, M3G_INVALID_OBJECT);
 
   756     return M3G_VFUNC(Object, obj, getReferences)(obj, (Object **)references);
 
   760  * \brief Uses m3gGetReferences to find given userID
 
   762 M3G_API M3GObject m3gFind(M3GObject hObject, M3Gint userID)
 
   764     Object *obj = (Object *) hObject;
 
   766     M3G_LOG3(M3G_LOG_STAGES, "Finding ID 0x%08X (%d) in 0x%08X\n",
 
   767              (unsigned) userID, userID, (unsigned) obj);
 
   769     M3G_VALIDATE_OBJECT(obj);
 
   771     if (obj->userID == userID) {
 
   775     return M3G_VFUNC(Object, obj, find)(obj, userID);