os/graphics/m3g/m3gcore11/src/m3g_object.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/graphics/m3g/m3gcore11/src/m3g_object.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,777 @@
     1.4 +/*
     1.5 +* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     1.6 +* All rights reserved.
     1.7 +* This component and the accompanying materials are made available
     1.8 +* under the terms of the License "Eclipse Public License v1.0"
     1.9 +* which accompanies this distribution, and is available
    1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.11 +*
    1.12 +* Initial Contributors:
    1.13 +* Nokia Corporation - initial contribution.
    1.14 +*
    1.15 +* Contributors:
    1.16 +*
    1.17 +* Description: Base object class implementation
    1.18 +*
    1.19 +*/
    1.20 +
    1.21 +
    1.22 +/*!
    1.23 + * \internal
    1.24 + * \file
    1.25 + * \brief Base object class implementation
    1.26 + *
    1.27 + */
    1.28 +
    1.29 +#ifndef M3G_CORE_INCLUDE
    1.30 +#   error included by m3g_core.c; do not compile separately.
    1.31 +#endif
    1.32 +
    1.33 +/*----------------------------------------------------------------------
    1.34 + * Constructor & destructor
    1.35 + *--------------------------------------------------------------------*/
    1.36 +
    1.37 +/*!
    1.38 + * \internal
    1.39 + * \brief Constructor for all Objects
    1.40 + *
    1.41 + * The reference count for new objects is initialized to zero; the
    1.42 + * object pointer must be stored using \c m3gSetRef, or m3gAddRef
    1.43 + * called explicitly, to increase this to one.
    1.44 + */
    1.45 +static void m3gInitObject(Object *obj,
    1.46 +                          Interface *interface,
    1.47 +                          M3GClass classID)
    1.48 +{
    1.49 +    M3G_ASSERT_PTR(obj);
    1.50 +    M3G_VALIDATE_INTERFACE(interface);
    1.51 +
    1.52 +    M3G_ASSERT(m3gInRange(classID,
    1.53 +                          M3G_CLASS_ANIMATION_CONTROLLER, M3G_CLASS_WORLD));
    1.54 +    
    1.55 +    obj->classID = (M3Guint) classID;
    1.56 +    obj->interface = interface;
    1.57 +    obj->animTracks = NULL;
    1.58 +    obj->refCount = 0u;
    1.59 +    
    1.60 +    M3G_VALIDATE_OBJECT(obj);
    1.61 +
    1.62 +    m3gAddChildObject(interface, obj);
    1.63 +    m3gMarkObject(obj);
    1.64 +    
    1.65 +    m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_OBJECTS, 1);
    1.66 +    M3G_LOG2(M3G_LOG_OBJECTS, "New %s 0x%08X\n",
    1.67 +             m3gClassName((M3GClass) obj->classID),
    1.68 +             (unsigned) obj);
    1.69 +}
    1.70 +
    1.71 +/*!
    1.72 + * \internal
    1.73 + * \brief Destructor for all Objects
    1.74 + */
    1.75 +static void m3gDestroyObject(Object *obj)
    1.76 +{
    1.77 +    M3G_VALIDATE_OBJECT(obj);
    1.78 +    M3G_ASSERT(m3gIsObject(obj));
    1.79 +
    1.80 +    if (obj->animTracks != NULL) {
    1.81 +        int n = m3gArraySize(obj->animTracks);
    1.82 +        int i;
    1.83 +
    1.84 +        for (i = 0; i < n; ++i) {
    1.85 +            M3GObject hTrk = (M3GObject) m3gGetArrayElement(obj->animTracks, i);
    1.86 +            m3gDeleteRef(hTrk);
    1.87 +        }
    1.88 +        m3gDestroyArray(obj->animTracks, M3G_INTERFACE(obj));
    1.89 +        m3gFree(obj->interface, obj->animTracks);
    1.90 +    }
    1.91 +
    1.92 +    m3gDelChildObject(obj->interface, obj);
    1.93 +    m3gUnmarkObject(obj);
    1.94 +    
    1.95 +    m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_OBJECTS, -1);
    1.96 +    M3G_LOG2(M3G_LOG_OBJECTS, "Destroyed %s 0x%08X\n",
    1.97 +             m3gClassName((M3GClass) obj->classID),
    1.98 +             (unsigned) obj);
    1.99 +}
   1.100 +
   1.101 +/*----------------------------------------------------------------------
   1.102 + * Internal functions
   1.103 + *--------------------------------------------------------------------*/
   1.104 +
   1.105 +/*!
   1.106 + * \internal
   1.107 + * \brief Sets an object reference to a new value and updates the
   1.108 + * reference count accordingly
   1.109 + *
   1.110 + * Note that this may lead to the originally referenced object being
   1.111 + * destroyed.
   1.112 + *
   1.113 + * \param ref Pointer to the reference (pointer) to set to a new value
   1.114 + * \param obj New value of the reference
   1.115 + */
   1.116 +static void m3gSetRef(Object **ref, Object *obj)
   1.117 +{
   1.118 +    M3G_ASSERT_PTR(ref);
   1.119 +
   1.120 +    if (*ref != obj) {
   1.121 +        if (obj != NULL) {
   1.122 +            m3gAddRef((M3GObject) obj);
   1.123 +        }
   1.124 +        if (*ref != NULL) {
   1.125 +            m3gDeleteRef((M3GObject) *ref);
   1.126 +        }
   1.127 +        *ref = obj;
   1.128 +    }
   1.129 +}
   1.130 +
   1.131 +#if defined(M3G_DEBUG)
   1.132 +/*!
   1.133 + * \internal
   1.134 + * \brief Checks the integrity of an Object-derived object
   1.135 + */
   1.136 +static void m3gValidateObject(const void *pObj)
   1.137 +{
   1.138 +    const Object *obj = (const Object *) pObj;
   1.139 +    M3G_VALIDATE_MEMBLOCK(obj);
   1.140 +    M3G_VALIDATE_MEMBLOCK(obj->interface);
   1.141 +    M3G_ASSERT(m3gInRange(obj->classID,
   1.142 +                          M3G_CLASS_ANIMATION_CONTROLLER, M3G_CLASS_WORLD));
   1.143 +    M3G_ASSERT_PTR(m3gGetVFTable(obj));
   1.144 +}
   1.145 +#endif /* M3G_DEBUG */
   1.146 +
   1.147 +
   1.148 +/* ---------------- Internal Object3D functions ---------------- */
   1.149 +
   1.150 +/*!
   1.151 + * \internal
   1.152 + * \brief Default \c applyAnimation function implementation
   1.153 + */
   1.154 +static M3Gint m3gObjectApplyAnimation(Object *self, M3Gint time)
   1.155 +{
   1.156 +    Interface *m3g = M3G_INTERFACE(self);
   1.157 +    M3Gint validity = 0x7FFFFFFF;
   1.158 +    M3Gint trackIndex, numTracks;
   1.159 +    M3Gfloat stackSampleVector[4];
   1.160 +    const PointerArray *tracks = self->animTracks;
   1.161 +
   1.162 +    /* Quick exit if no animation tracks */
   1.163 +
   1.164 +    if (tracks == NULL) {
   1.165 +        return validity;
   1.166 +    }
   1.167 +
   1.168 +    /* Loop through the tracks. Note that the tracks are ordered so
   1.169 +     * that tracks targeting the same property are adjacent in the
   1.170 +     * array; this makes animation blending easier. */
   1.171 +
   1.172 +    numTracks = m3gArraySize(tracks);
   1.173 +
   1.174 +    for (trackIndex = 0; trackIndex < numTracks; ) {
   1.175 +        const AnimationTrack *track = (const AnimationTrack *)
   1.176 +            m3gGetArrayElement(tracks, trackIndex);
   1.177 +        const KeyframeSequence *sequence = track->sequence;
   1.178 +
   1.179 +        M3Gint components = sequence->numComponents;
   1.180 +        M3Gint property = track->property;
   1.181 +        M3Gint nextProperty;
   1.182 +
   1.183 +        M3Gfloat sumWeights = 0;
   1.184 +        M3Gfloat *sumValues;
   1.185 +
   1.186 +        /* Collect the contributions from all the tracks targeting the
   1.187 +         * same property */
   1.188 +
   1.189 +        if (components <= 4) {
   1.190 +            sumValues = stackSampleVector;
   1.191 +        }
   1.192 +        else {
   1.193 +            sumValues = (M3Gfloat *)
   1.194 +                m3gAlloc(m3g, components * sizeof(M3Gfloat));
   1.195 +            if (sumValues == NULL) {
   1.196 +                return 0;
   1.197 +            }
   1.198 +        }
   1.199 +
   1.200 +        m3gZero(sumValues, components * sizeof(M3Gfloat));
   1.201 +
   1.202 +        do {
   1.203 +            SampleInfo sampleInfo;
   1.204 +            
   1.205 +            m3gGetContribution(track, time, sumValues, &sampleInfo);
   1.206 +            if (sampleInfo.validity <= 0) {
   1.207 +                return 0;
   1.208 +            }
   1.209 +            sumWeights += sampleInfo.weight;
   1.210 +            validity = M3G_MIN(validity, sampleInfo.validity);
   1.211 +
   1.212 +            if (++trackIndex == numTracks) {
   1.213 +                break;
   1.214 +            }
   1.215 +            track = (const AnimationTrack *) m3gGetArrayElement(tracks,
   1.216 +                                                                trackIndex);
   1.217 +            nextProperty = track->property;
   1.218 +        } while (nextProperty == property);
   1.219 +
   1.220 +        if (sumWeights > 0) {
   1.221 +            M3G_VFUNC(Object, self, updateProperty)(
   1.222 +                self, property, components, sumValues);
   1.223 +        }
   1.224 +        if (sumValues != stackSampleVector) {
   1.225 +            m3gFree(m3g, sumValues);
   1.226 +        }
   1.227 +    }
   1.228 +
   1.229 +    return validity;
   1.230 +}
   1.231 +
   1.232 +/*!
   1.233 + * \internal
   1.234 + * \brief Default \c isCompatible function implementation
   1.235 + */
   1.236 +static M3Gbool m3gObjectIsCompatible(M3Gint property)
   1.237 +{
   1.238 +    M3G_UNREF(property);
   1.239 +    
   1.240 +    return M3G_FALSE;
   1.241 +}
   1.242 +
   1.243 +/*!
   1.244 + * \internal
   1.245 + * \brief Default \c updateProperty function implementation
   1.246 + *
   1.247 + * Silently ignoring an update request for a non-existent object
   1.248 + * property does no harm, so this just asserts in debug builds and is
   1.249 + * NOP otherwise.
   1.250 + */
   1.251 +static void m3gObjectUpdateProperty(Object *self,
   1.252 +                                    M3Gint property,
   1.253 +                                    M3Gint valueSize,
   1.254 +                                    const M3Gfloat *value)
   1.255 +{
   1.256 +    M3G_UNREF(self);
   1.257 +    M3G_UNREF(property);
   1.258 +    M3G_UNREF(valueSize);
   1.259 +    M3G_UNREF(value);
   1.260 +    
   1.261 +    M3G_ASSERT(M3G_FALSE);
   1.262 +}
   1.263 +
   1.264 +/*!
   1.265 + * \internal
   1.266 + * \brief Default \c getReferences function implementation
   1.267 + */
   1.268 +static M3Gint m3gObjectDoGetReferences(Object *self, Object **references)
   1.269 +{
   1.270 +    M3Gint i;
   1.271 +    if (self->animTracks != NULL) {
   1.272 +        if (references != NULL) {
   1.273 +            for (i = 0; i < m3gArraySize(self->animTracks); ++i) {
   1.274 +                references[i] = (Object *)m3gGetArrayElement(self->animTracks, i);
   1.275 +            }
   1.276 +        }
   1.277 +        return m3gArraySize(self->animTracks);
   1.278 +    }
   1.279 +    return 0;
   1.280 +}
   1.281 +
   1.282 +/*!
   1.283 + * \internal
   1.284 + * \brief Default \c find implementation
   1.285 + */
   1.286 +static Object *m3gObjectFindID(Object *self, M3Gint userID)
   1.287 +{
   1.288 +    M3Gint i;
   1.289 +
   1.290 +    if (self->userID == userID) {
   1.291 +        return self;
   1.292 +    }
   1.293 +    
   1.294 +    if (self->animTracks) {
   1.295 +        for (i = 0; i < m3gArraySize(self->animTracks); ++i) {
   1.296 +            Object *found =
   1.297 +                m3gFindID((Object *) m3gGetArrayElement(self->animTracks, i),
   1.298 +                          userID);
   1.299 +            if (found) {
   1.300 +                return found;
   1.301 +            }
   1.302 +        }
   1.303 +    }
   1.304 +    
   1.305 +    return NULL;
   1.306 +}
   1.307 +
   1.308 +/*!
   1.309 + * \internal
   1.310 + * \brief Default \c duplicate function implementation
   1.311 + */
   1.312 +static M3Gbool m3gObjectDuplicate(const Object *original,
   1.313 +                                  Object **clone,
   1.314 +                                  Object **pairs,
   1.315 +                                  M3Gint *numPairs)
   1.316 +{
   1.317 +    Interface *m3g = original->interface;
   1.318 +    M3G_ASSERT_PTR(*clone); /* abstract class, must be derived */
   1.319 +
   1.320 +    pairs[2 * (*numPairs)] = (Object *)original;
   1.321 +    pairs[2 * (*numPairs) + 1] = *clone;
   1.322 +    (*numPairs)++;
   1.323 +
   1.324 +    /* Copy basic object properties */
   1.325 +    
   1.326 +    (*clone)->interface = m3g;
   1.327 +    (*clone)->classID   = original->classID;
   1.328 +    (*clone)->userID    = original->userID;
   1.329 +
   1.330 +    /* Copy animation tracks.  This may fail due to out-of-memory, so
   1.331 +     * we check for that; clean-up will be handled by the derived
   1.332 +     * class method. */
   1.333 +    
   1.334 +    if (original->animTracks != NULL) {
   1.335 +        M3Gsizei numTracks = m3gArraySize(original->animTracks);
   1.336 +        M3Gint i;
   1.337 +
   1.338 +        /* Allocate the track array and make sure it has enough room
   1.339 +         * for holding the tracks we're about to copy */
   1.340 +        
   1.341 +        PointerArray *animTracks =
   1.342 +            (PointerArray*) m3gAlloc(m3g, sizeof(PointerArray));
   1.343 +        if (animTracks == NULL) {
   1.344 +            return M3G_FALSE; /* out of memory */
   1.345 +        }        
   1.346 +        (*clone)->animTracks = animTracks;
   1.347 +
   1.348 +        m3gInitArray(animTracks);
   1.349 +        if (!m3gEnsureArrayCapacity(animTracks, numTracks, m3g)) {
   1.350 +            return M3G_FALSE; /* out of memory */
   1.351 +        }                           
   1.352 +
   1.353 +        /* Copy tracks one-by-one and update references.  This can no
   1.354 +         * longer fail, as the capacity request above has been
   1.355 +         * satisfied */
   1.356 +        
   1.357 +        for (i = 0; i < numTracks; ++i) {
   1.358 +            AnimationTrack *track =
   1.359 +                (AnimationTrack *) m3gGetArrayElement(original->animTracks, i);
   1.360 +
   1.361 +            if (m3gArrayAppend(animTracks, track, m3g) != i) {
   1.362 +                M3G_ASSERT(M3G_FALSE);
   1.363 +            }
   1.364 +            m3gAddRef((Object *) track);
   1.365 +        }
   1.366 +    }
   1.367 +    return M3G_TRUE;
   1.368 +}
   1.369 +
   1.370 +#if defined(M3G_LOGLEVEL)
   1.371 +/*!
   1.372 + * \internal
   1.373 + * \brief Returns the name of an object class
   1.374 + */
   1.375 +static const char *m3gClassName(M3GClass classID)
   1.376 +{
   1.377 +    switch (classID) {
   1.378 +    case M3G_CLASS_ANIMATION_CONTROLLER:
   1.379 +        return "AnimationController";
   1.380 +    case M3G_CLASS_ANIMATION_TRACK:
   1.381 +        return "AnimationTrack";
   1.382 +    case M3G_CLASS_APPEARANCE:
   1.383 +        return "Appearance";
   1.384 +    case M3G_CLASS_BACKGROUND:
   1.385 +        return "Background";
   1.386 +    case M3G_CLASS_CAMERA:
   1.387 +        return "Camera";
   1.388 +    case M3G_CLASS_COMPOSITING_MODE:
   1.389 +        return "CompositingMode";
   1.390 +    case M3G_CLASS_FOG:
   1.391 +        return "Fog";
   1.392 +    case M3G_CLASS_GROUP:
   1.393 +        return "Group";
   1.394 +    case M3G_CLASS_IMAGE:
   1.395 +        return "Image";
   1.396 +    case M3G_CLASS_INDEX_BUFFER:
   1.397 +        return "IndexBuffer";
   1.398 +    case M3G_CLASS_KEYFRAME_SEQUENCE:
   1.399 +        return "KeyframeSequence";
   1.400 +    case M3G_CLASS_LIGHT:
   1.401 +        return "Light";
   1.402 +    case M3G_CLASS_LOADER:
   1.403 +        return "Loader";
   1.404 +    case M3G_CLASS_MATERIAL:
   1.405 +        return "Material";
   1.406 +    case M3G_CLASS_MESH:
   1.407 +        return "Mesh";
   1.408 +    case M3G_CLASS_MORPHING_MESH:
   1.409 +        return "MorphingMesh";
   1.410 +    case M3G_CLASS_POLYGON_MODE:
   1.411 +        return "PolygonMode";
   1.412 +    case M3G_CLASS_RENDER_CONTEXT:
   1.413 +        return "RenderContext";
   1.414 +    case M3G_CLASS_SKINNED_MESH:
   1.415 +        return "SkinnedMesh";
   1.416 +    case M3G_CLASS_SPRITE:
   1.417 +        return "Sprite";
   1.418 +    case M3G_CLASS_TEXTURE:
   1.419 +        return "Texture";
   1.420 +    case M3G_CLASS_VERTEX_ARRAY:
   1.421 +        return "VertexArray";
   1.422 +    case M3G_CLASS_VERTEX_BUFFER:
   1.423 +        return "VertexBuffer";
   1.424 +    case M3G_CLASS_WORLD:
   1.425 +        return "World";
   1.426 +    default:
   1.427 +        return "<abstract class?>";
   1.428 +    }
   1.429 +}
   1.430 +#endif /* defined(M3G_LOGLEVEL) */
   1.431 +
   1.432 +/*----------------------------------------------------------------------
   1.433 + * Public interface functions
   1.434 + *--------------------------------------------------------------------*/
   1.435 +
   1.436 +/*!
   1.437 + * \brief Deletes an M3G object
   1.438 + *
   1.439 + * Similarly to m3gDeleteRef, the object will still remain until all
   1.440 + * references to it are deleted.  The difference from m3gDeleteRef is
   1.441 + * mostly stylistic: m3gDeleteObject is meant to be called by the
   1.442 + * owner of an object, while m3gDeleteRef should be used by users of
   1.443 + * the object.  Functionally, they are equivalent in all normal use
   1.444 + * cases.
   1.445 + *
   1.446 + * \note The only functional differences between m3gDeleteObject and
   1.447 + * m3gDeleteRef are that m3gDeleteObject can be used on an object with
   1.448 + * a reference count of zero, while m3gDeleteRef asserts against this
   1.449 + * in debug builds; and m3gDeleteObject accepts a NULL object.
   1.450 + */
   1.451 +/*@access M3GObject@*/
   1.452 +M3G_API void m3gDeleteObject(M3GObject hObject)
   1.453 +{
   1.454 +    Interface *m3g;
   1.455 +    Object *obj = (Object *) hObject;
   1.456 +
   1.457 +    if (obj != NULL) {
   1.458 +        M3G_VALIDATE_OBJECT(obj);
   1.459 +
   1.460 +        if (obj->refCount > 0) {
   1.461 +            m3gDeleteRef(obj);
   1.462 +        }
   1.463 +        else {
   1.464 +            M3G_LOG2(M3G_LOG_REFCOUNT,
   1.465 +                     "Deleting %s 0x%08X\n",
   1.466 +                     m3gClassName((M3GClass) obj->classID),
   1.467 +                     (unsigned) obj);
   1.468 +            
   1.469 +            m3g = obj->interface;
   1.470 +            M3G_VALIDATE_INTERFACE(m3g);
   1.471 +            
   1.472 +            M3G_ASSERT(m3gGetVFTable(obj)->destroy != NULL);
   1.473 +            
   1.474 +            M3G_VFUNC(Object, obj, destroy)(obj);
   1.475 +            m3gFree(m3g, obj);
   1.476 +        }
   1.477 +    }
   1.478 +}
   1.479 +
   1.480 +/*!
   1.481 + * \brief Notifies that a new reference to an object has been created
   1.482 + *
   1.483 + * An object will not be deleted while references to it exist.
   1.484 + */
   1.485 +M3G_API void m3gAddRef(M3GObject hObject)
   1.486 +{
   1.487 +    Object *obj = (Object *) hObject;
   1.488 +    M3G_VALIDATE_OBJECT(obj);
   1.489 +
   1.490 +    M3G_LOG3(M3G_LOG_REFCOUNT,
   1.491 +             "Adding ref to 0x%08X (%s), new count %u\n",
   1.492 +             (unsigned) obj,
   1.493 +             m3gClassName((M3GClass) obj->classID),
   1.494 +             (unsigned) (obj->refCount + 1));
   1.495 +
   1.496 +    M3G_ASSERT(obj->refCount < 0xFFFFFF);
   1.497 +    ++obj->refCount;
   1.498 +}
   1.499 +
   1.500 +/*!
   1.501 + * \brief Notifies that a reference to an object has been deleted
   1.502 + *
   1.503 + * If the reference count for an object reaches zero, the object is
   1.504 + * automatically destroyed.
   1.505 + */
   1.506 +M3G_API void m3gDeleteRef(M3GObject hObject)
   1.507 +{
   1.508 +    Object *obj = (Object *) hObject;
   1.509 +    M3G_VALIDATE_OBJECT(obj);
   1.510 +
   1.511 +    M3G_ASSERT(obj->refCount > 0);
   1.512 +
   1.513 +    M3G_LOG3(M3G_LOG_REFCOUNT,
   1.514 +             "Deleting ref to 0x%08X (%s), new count %u\n",
   1.515 +             (unsigned) obj,
   1.516 +             m3gClassName((M3GClass) obj->classID),
   1.517 +             (unsigned) (obj->refCount - 1));
   1.518 +
   1.519 +    if (--obj->refCount == 0) {
   1.520 +        m3gDeleteObject(hObject);
   1.521 +    }
   1.522 +}
   1.523 +
   1.524 +/*!
   1.525 + * \brief Returns the run-time class of an object
   1.526 + */
   1.527 +M3G_API M3GClass m3gGetClass(M3GObject hObject)
   1.528 +{
   1.529 +    Object *obj = (Object *) hObject;
   1.530 +    M3G_VALIDATE_OBJECT(obj);
   1.531 +    return M3G_CLASS(obj);
   1.532 +}
   1.533 +
   1.534 +/*!
   1.535 + * \brief Returns the interface owning this object
   1.536 + */
   1.537 +M3G_API M3GInterface m3gGetObjectInterface(M3GObject hObject)
   1.538 +{
   1.539 +    Object *obj = (Object *) hObject;
   1.540 +    M3G_VALIDATE_OBJECT(obj);
   1.541 +    return obj->interface;
   1.542 +}
   1.543 +
   1.544 +/* ---------------- Object3D functions ---------------- */
   1.545 +
   1.546 +/*!
   1.547 + *
   1.548 + */
   1.549 +M3G_API M3Gint m3gAddAnimationTrack(M3GObject hObject,
   1.550 +                                    M3GAnimationTrack hAnimationTrack)
   1.551 +{
   1.552 +    AnimationTrack *track = (AnimationTrack *)hAnimationTrack;
   1.553 +    Object *obj = (Object *) hObject;
   1.554 +    Interface *m3g = M3G_INTERFACE(obj);
   1.555 +    M3G_VALIDATE_OBJECT(obj);
   1.556 +
   1.557 +    /* Check for errors */
   1.558 +
   1.559 +    if (!M3G_VFUNC(Object, obj, isCompatible)(track->property)) {
   1.560 +        m3gRaiseError(m3g, M3G_INVALID_OBJECT);
   1.561 +        return -1;
   1.562 +    }
   1.563 +
   1.564 +     /* Allocate animation track array only when adding animations for
   1.565 +      * the first time */
   1.566 +
   1.567 +    if (obj->animTracks == NULL) {
   1.568 +        obj->animTracks = (PointerArray*) m3gAlloc(m3g, sizeof(PointerArray));
   1.569 +        if (obj->animTracks == NULL) return 0;
   1.570 +        m3gInitArray(obj->animTracks);
   1.571 +    }
   1.572 +
   1.573 +    /*  The animation tracks are maintained in a sorted order based on
   1.574 +     *  their target property enumerations.  This keeps all tracks
   1.575 +     *  targeting the same property adjacent so that we can easily
   1.576 +     *  handle animation blending. */
   1.577 +    {
   1.578 +        PointerArray *trackArray = obj->animTracks;
   1.579 +        M3Gsizei numTracks = m3gArraySize(trackArray);
   1.580 +        M3Gint i;
   1.581 +
   1.582 +        for (i = 0; i < numTracks; ++i) {
   1.583 +
   1.584 +            const AnimationTrack *arrayTrack =
   1.585 +                (const AnimationTrack *) m3gGetArrayElement(trackArray, i);
   1.586 +
   1.587 +            if (arrayTrack->property > track->property) {
   1.588 +                break;
   1.589 +            }
   1.590 +
   1.591 +            if ((track == arrayTrack) ||
   1.592 +                (   (track->property == arrayTrack->property) &&
   1.593 +                    (track->sequence->numComponents != arrayTrack->sequence->numComponents))) {
   1.594 +
   1.595 +                    m3gRaiseError(m3g, M3G_INVALID_OBJECT);
   1.596 +                    return -1;
   1.597 +                }
   1.598 +        }
   1.599 +
   1.600 +        if (m3gArrayInsert(trackArray, i, track, m3g) < 0) {
   1.601 +            return -1;
   1.602 +        }
   1.603 +        m3gAddRef((M3GObject) track);
   1.604 +
   1.605 +        return i;
   1.606 +    }
   1.607 +}
   1.608 +
   1.609 +/*!
   1.610 + *
   1.611 + */
   1.612 +M3G_API void m3gRemoveAnimationTrack(M3GObject hObject,
   1.613 +                                     M3GAnimationTrack hAnimationTrack)
   1.614 +{
   1.615 +    AnimationTrack *track = (AnimationTrack *)hAnimationTrack;
   1.616 +    Object *obj = (Object *) hObject;
   1.617 +    M3G_VALIDATE_OBJECT(obj);
   1.618 +
   1.619 +    /* Remove the track from the array, and if no tracks remain,
   1.620 +     * delete the array, too */
   1.621 +
   1.622 +    if (track != NULL && obj->animTracks != NULL) {
   1.623 +        M3Gint i = m3gArrayFind(obj->animTracks, track);
   1.624 +
   1.625 +        if (i != -1) {
   1.626 +            m3gArrayDelete(obj->animTracks, i);
   1.627 +            m3gDeleteRef((Object *) track);
   1.628 +
   1.629 +            if (m3gArraySize(obj->animTracks) == 0) {
   1.630 +                m3gDestroyArray(obj->animTracks, M3G_INTERFACE(obj));
   1.631 +                m3gFree(M3G_INTERFACE(obj), obj->animTracks);
   1.632 +                obj->animTracks = NULL;
   1.633 +            }
   1.634 +        }
   1.635 +    }
   1.636 +}
   1.637 +
   1.638 +/*!
   1.639 + *
   1.640 + */
   1.641 +M3G_API M3Gint m3gGetAnimationTrackCount(M3GObject hObject)
   1.642 +{
   1.643 +    Object *obj = (Object *) hObject;
   1.644 +    M3G_VALIDATE_OBJECT(obj);
   1.645 +
   1.646 +    return (obj->animTracks == NULL ? 0 : m3gArraySize(obj->animTracks));
   1.647 +}
   1.648 +
   1.649 +M3G_API M3GAnimationTrack m3gGetAnimationTrack(M3GObject hObject, M3Gint idx)
   1.650 +{
   1.651 +    Object *obj = (Object *) hObject;
   1.652 +    M3G_VALIDATE_OBJECT(obj);
   1.653 +
   1.654 +    /* idx must be in range [0, to size of array - 1] */
   1.655 +    if (obj->animTracks == NULL
   1.656 +            || !m3gInRange(idx, 0, m3gArraySize(obj->animTracks) - 1)) {
   1.657 +        m3gRaiseError(M3G_INTERFACE(obj), M3G_INVALID_INDEX);
   1.658 +        return NULL;
   1.659 +    }
   1.660 +
   1.661 +    return (M3GAnimationTrack) m3gGetArrayElement(obj->animTracks, idx);
   1.662 +}
   1.663 +
   1.664 +M3G_API M3Gint m3gAnimate(M3GObject hObject, M3Gint time)
   1.665 +{
   1.666 +    M3Gint validity;
   1.667 +    Object *obj = (Object *) hObject;
   1.668 +
   1.669 +    M3G_LOG2(M3G_LOG_STAGES,
   1.670 +             "Animating %s 0x%08X\n",
   1.671 +             m3gClassName((M3GClass) obj->classID), (unsigned) obj);
   1.672 +    
   1.673 +    M3G_VALIDATE_OBJECT(obj);
   1.674 +    
   1.675 +    M3G_BEGIN_PROFILE(M3G_INTERFACE(obj), M3G_PROFILE_ANIM);
   1.676 +    validity = M3G_VFUNC(Object, obj, applyAnimation)(obj, time);
   1.677 +    M3G_END_PROFILE(M3G_INTERFACE(obj), M3G_PROFILE_ANIM);
   1.678 +    
   1.679 +    return validity;
   1.680 +}
   1.681 +
   1.682 +/*!
   1.683 + * \brief Sets userID for this object
   1.684 +*/
   1.685 +M3G_API void m3gSetUserID(M3GObject hObject, M3Gint userID)
   1.686 +{
   1.687 +    Object *obj = (Object *) hObject;
   1.688 +    M3G_VALIDATE_OBJECT(obj);
   1.689 +	obj->userID = userID;
   1.690 +}
   1.691 +
   1.692 +/*!
   1.693 + * \brief Gets userID of this object
   1.694 +*/
   1.695 +M3G_API M3Gint m3gGetUserID(M3GObject hObject)
   1.696 +{
   1.697 +    Object *obj = (Object *) hObject;
   1.698 +    M3G_VALIDATE_OBJECT(obj);
   1.699 +
   1.700 +	return obj->userID;
   1.701 +}
   1.702 +
   1.703 +/*!
   1.704 + * \brief Creates a duplicate of this Object3D
   1.705 +*/
   1.706 +M3G_API M3GObject m3gDuplicate(M3GObject hObject, M3GObject *hReferences)
   1.707 +{
   1.708 +    Object **references = (Object **)hReferences;
   1.709 +    const Object *obj = (const Object *) hObject;
   1.710 +    Object *clone = NULL;
   1.711 +    M3Gint numRef = 0;
   1.712 +
   1.713 +    M3G_LOG2(M3G_LOG_STAGES|M3G_LOG_OBJECTS,
   1.714 +             "Duplicating %s 0x%08X\n",
   1.715 +             m3gClassName((M3GClass) obj->classID), (unsigned) obj);
   1.716 +
   1.717 +    M3G_VALIDATE_OBJECT(obj);
   1.718 +    
   1.719 +    /* Clone the object (or subtree) */
   1.720 +    if (!M3G_VFUNC(Object, obj, duplicate)(obj, &clone, references, &numRef)) {
   1.721 +        m3gDeleteObject(clone);
   1.722 +        return NULL; /* failed; out of memory will have been thrown */
   1.723 +    }
   1.724 +
   1.725 +    /* NOTE This will have to change (the virtual function moved to
   1.726 +     * the Object class) if we add classes where child objects may get
   1.727 +     * duplicated */
   1.728 +    
   1.729 +    if (clone->classID == M3G_CLASS_CAMERA ||
   1.730 +        clone->classID == M3G_CLASS_GROUP ||
   1.731 +        clone->classID == M3G_CLASS_WORLD ||
   1.732 +        clone->classID == M3G_CLASS_LIGHT ||
   1.733 +        clone->classID == M3G_CLASS_MESH ||
   1.734 +        clone->classID == M3G_CLASS_MORPHING_MESH ||
   1.735 +        clone->classID == M3G_CLASS_SKINNED_MESH ||
   1.736 +        clone->classID == M3G_CLASS_SPRITE)
   1.737 +        M3G_VFUNC(Node, clone, updateDuplicateReferences)((Node *)obj, references, numRef);
   1.738 +
   1.739 +    return clone;
   1.740 +}
   1.741 +
   1.742 +/*!
   1.743 + * \brief Checks the length of the references array and calls virtual
   1.744 + * getReferences
   1.745 + */
   1.746 +M3G_API M3Gint m3gGetReferences(M3GObject hObject,
   1.747 +                                M3GObject *references,
   1.748 +                                M3Gint length)
   1.749 +{
   1.750 +    Object *obj = (Object *) hObject;
   1.751 +    M3G_VALIDATE_OBJECT(obj);
   1.752 +    if (references != NULL) {
   1.753 +        int num = M3G_VFUNC(Object, obj, getReferences)(obj, NULL);
   1.754 +        if (length < num) {
   1.755 +            m3gRaiseError(obj->interface, M3G_INVALID_OBJECT);
   1.756 +            return 0;
   1.757 +        }
   1.758 +    }
   1.759 +    return M3G_VFUNC(Object, obj, getReferences)(obj, (Object **)references);
   1.760 +}
   1.761 +
   1.762 +/*!
   1.763 + * \brief Uses m3gGetReferences to find given userID
   1.764 + */
   1.765 +M3G_API M3GObject m3gFind(M3GObject hObject, M3Gint userID)
   1.766 +{
   1.767 +    Object *obj = (Object *) hObject;
   1.768 +
   1.769 +    M3G_LOG3(M3G_LOG_STAGES, "Finding ID 0x%08X (%d) in 0x%08X\n",
   1.770 +             (unsigned) userID, userID, (unsigned) obj);
   1.771 +    
   1.772 +    M3G_VALIDATE_OBJECT(obj);
   1.773 +
   1.774 +    if (obj->userID == userID) {
   1.775 +        return obj;
   1.776 +    }
   1.777 +    
   1.778 +    return M3G_VFUNC(Object, obj, find)(obj, userID);
   1.779 +}
   1.780 +