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);