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