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 +