First public contribution.
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: Sprite implementation
22 * \brief Sprite implementation
25 #ifndef M3G_CORE_INCLUDE
26 # error included by m3g_core.c; do not compile separately.
29 /*#include <stdio.h>*/
31 #include "m3g_sprite.h"
32 #include "m3g_appearance.h"
33 #include "m3g_camera.h"
34 #include "m3g_rendercontext.h"
35 #include "m3g_renderqueue.h"
41 /*----------------------------------------------------------------------
43 *--------------------------------------------------------------------*/
47 * \brief Destroys this Sprite object.
49 * \param obj Sprite object
51 static void m3gDestroySprite(Object *obj)
53 Sprite *sprite = (Sprite *) obj;
54 M3G_VALIDATE_OBJECT(sprite);
56 M3G_ASSIGN_REF(sprite->image, NULL);
57 M3G_ASSIGN_REF(sprite->appearance, NULL);
59 m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_RENDERABLES, -1);
66 * \brief Overloaded Object3D method.
68 * \param property animation property
69 * \retval M3G_TRUE property supported
70 * \retval M3G_FALSE property not supported
72 static M3Gbool m3gSpriteIsCompatible(M3Gint property)
78 return m3gNodeIsCompatible(property);
84 * \brief Overloaded Node method
86 static M3Gint m3gSpriteGetBBox(Node *self, AABB *bbox)
88 Sprite *sprite = (Sprite*) self;
90 /* Only scaled sprites can have a bounding box; non-scaled ones
91 * are marked as non-cullable in the "SetParent" function in
95 const AABB spriteBBox = { { -.5f, -.5f, 0.f },
98 return (4 * VFC_VERTEX_COST +
99 2 * VFC_TRIANGLE_COST +
103 return 0; /* no bounding box for non-scaled sprites */
109 * \brief Overloaded Object3D method.
111 * \param self Sprite object
112 * \param property animation property
113 * \param valueSize size of value array
114 * \param value value array
116 static void m3gSpriteUpdateProperty(Object *self,
119 const M3Gfloat *value)
121 Sprite *sprite = (Sprite *) self;
122 M3G_VALIDATE_OBJECT(sprite);
123 M3G_ASSERT_PTR(value);
127 /* Assert that the value vector is large enough */
129 M3G_ASSERT(valueSize >= 4);
130 m3gSetCrop(sprite, m3gRoundToInt(value[0]),
131 m3gRoundToInt(value[1]),
132 m3gClampInt(m3gRoundToInt(value[2]),
133 -M3G_MAX_TEXTURE_DIMENSION,
134 M3G_MAX_TEXTURE_DIMENSION),
135 m3gClampInt(m3gRoundToInt(value[3]),
136 -M3G_MAX_TEXTURE_DIMENSION,
137 M3G_MAX_TEXTURE_DIMENSION) );
140 M3G_ASSERT(valueSize >= 2);
141 m3gSetCrop(sprite, m3gRoundToInt(value[0]),
142 m3gRoundToInt(value[1]),
144 sprite->crop.height );
148 m3gNodeUpdateProperty(self, property, valueSize, value);
154 * \brief Overloaded Node method.
156 * \param self Sprite object
157 * \param toCamera transform to camera
158 * \param alphaFactor total alpha factor
159 * \param caller caller node
160 * \param renderQueue RenderQueue
162 * \retval M3G_TRUE continue render setup
163 * \retval M3G_FALSE abort render setup
165 static M3Gbool m3gSpriteSetupRender(Node *self,
168 RenderQueue *renderQueue)
170 Sprite *sprite = (Sprite *)self;
171 Interface *m3g = M3G_INTERFACE(sprite);
173 m3gIncStat(M3G_INTERFACE(self), M3G_STAT_RENDER_NODES, 1);
175 if ((self->enableBits & NODE_RENDER_BIT) != 0 &&
176 (self->scope & renderQueue->scope) != 0) {
178 if (sprite->appearance != NULL && sprite->image != NULL &&
179 sprite->crop.width != 0 && sprite->crop.height != 0) {
181 /* Fetch the cumulative alpha factor for this node */
182 sprite->totalAlphaFactor =
183 (M3Gushort) m3gGetTotalAlphaFactor((Node*) sprite, renderQueue->root);
185 /* Touch the POT image to make sure it's allocated prior
188 if (!m3gGetPowerOfTwoImage(sprite->image) ||
189 !m3gInsertDrawable(m3g,
194 m3gGetAppearanceSortKey(sprite->appearance)))
204 * \brief Calculates sprite vertex positions and texture coordinates.
206 * \param sprite Sprite object
207 * \param ctx RenderContext object (Graphics3D)
208 * \param cam Camera object
209 * \param vert vertex position to fill in
210 * \param texvert texture coordinates to fill in
211 * \param eyeSpace coordinates after modelview
212 * \param adjust adjust for texture coorinates, render and
213 * pick need different adjustment
214 * \retval M3G_TRUE crop and image intersect
215 * \retval M3G_FALSE crop and image do not intersect
217 static M3Gbool m3gGetSpriteCoordinates(Sprite *sprite,
220 const Matrix *toCamera,
226 Vec4 o = {0, 0, 0, 1}; /* Origin */
227 Vec4 x = {0.5f, 0, 0, 1}; /* Half of x unit */
228 Vec4 y = {0, 0.5f, 0, 1}; /* Half of y unit */
234 rImage.width = sprite->width;
235 rImage.height = sprite->height;
237 /* Intersection of image and crop*/
238 if (!m3gIntersectRectangle(&rIsect, &rImage, &sprite->crop)) {
239 /* No intersection -> nothing to render / pick */
243 /* Calculate origin and vectors after modelview */
244 m3gTransformVec4(toCamera, &o);
245 m3gTransformVec4(toCamera, &x);
246 m3gTransformVec4(toCamera, &y);
250 m3gScaleVec4(&o, m3gRcp(o.w));
251 m3gScaleVec4(&x, m3gRcp(x.w));
252 m3gScaleVec4(&y, m3gRcp(y.w));
254 /* Store eyespace coordinates */
255 if (eyeSpace != NULL) {
264 x.x = m3gAdd(ot.x, m3gLengthVec3((const Vec3*) &x));
269 y.y = m3gAdd(ot.y, m3gLengthVec3((const Vec3*) &y));
274 /* Calculate origin and vectors after projection */
276 const Matrix *projMatrix = m3gProjectionMatrix(cam);
277 m3gTransformVec4(projMatrix, &ot);
278 m3gTransformVec4(projMatrix, &x);
279 m3gTransformVec4(projMatrix, &y);
281 #ifndef M3G_USE_NGL_API
282 /* Store w after projection */
283 if (eyeSpace != NULL) {
287 m3gScaleVec4(&ot, m3gRcp(ot.w));
288 m3gScaleVec4(&x, m3gRcp(x.w));
289 m3gScaleVec4(&y, m3gRcp(y.w));
294 x.x = m3gLengthVec3((const Vec3*) &x);
295 y.y = m3gLengthVec3((const Vec3*) &y);
297 /* Non-scaled sprites take width from crop rectangle*/
298 if (!sprite->scaled) {
301 m3gGetViewport(ctx, viewport, viewport + 1, viewport + 2, viewport + 3);
304 /* Use a dummy viewport, this is only when picking and
305 not rendering to anything. Values must represent a valid viewport */
312 x.x = m3gDivif (rIsect.width, viewport[2]);
313 y.y = m3gDivif (rIsect.height, viewport[3]);
316 m3gDivif (2 * sprite->crop.x + sprite->crop.width - 2 * rIsect.x - rIsect.width,
320 m3gDivif (2 * sprite->crop.y + sprite->crop.height - 2 * rIsect.y - rIsect.height,
324 /* Adjust width and height according to cropping rectangle */
325 x.x = m3gDiv(x.x, (M3Gfloat) sprite->crop.width);
326 y.y = m3gDiv(y.y, (M3Gfloat) sprite->crop.height);
329 m3gMul((M3Gfloat)(2 * sprite->crop.x + sprite->crop.width - 2 * rIsect.x - rIsect.width),
333 m3gMul((M3Gfloat)(2 * sprite->crop.y + sprite->crop.height - 2 * rIsect.y - rIsect.height),
336 x.x = m3gMul(x.x, (M3Gfloat) rIsect.width);
337 y.y = m3gMul(y.y, (M3Gfloat) rIsect.height);
339 #ifdef M3G_USE_NGL_API
341 if (eyeSpace != NULL) {
345 /* Set up positions */
346 vert[0 * 3 + 0] = (M3Gint) m3gMul(65536, m3gSub(ot.x, x.x));
347 vert[0 * 3 + 1] = m3gRoundToInt(m3gAdd(m3gMul(65536, m3gAdd(ot.y, y.y)), 0.5f));
348 vert[0 * 3 + 2] = m3gRoundToInt(m3gMul(65536, ot.z));
350 vert[1 * 3 + 0] = vert[0 * 3 + 0];
351 vert[1 * 3 + 1] = (M3Gint) m3gMul(65536, m3gSub(ot.y, y.y));
352 vert[1 * 3 + 2] = vert[0 * 3 + 2];
354 vert[2 * 3 + 0] = m3gRoundToInt(m3gAdd(m3gMul(65536, m3gAdd(ot.x, x.x)), 0.5f));
355 vert[2 * 3 + 1] = vert[0 * 3 + 1];
356 vert[2 * 3 + 2] = vert[0 * 3 + 2];
358 vert[3 * 3 + 0] = vert[2 * 3 + 0];
359 vert[3 * 3 + 1] = vert[1 * 3 + 1];
360 vert[3 * 3 + 2] = vert[0 * 3 + 2];
362 /* Set up texture coordinates */
363 if (!(sprite->flip & FLIPX)) {
364 texvert[0 * 2 + 0] = (M3Gshort) rIsect.x;
365 texvert[1 * 2 + 0] = (M3Gshort) rIsect.x;
366 texvert[2 * 2 + 0] = (M3Gshort) (rIsect.x + rIsect.width - adjust);
367 texvert[3 * 2 + 0] = (M3Gshort) (rIsect.x + rIsect.width - adjust);
370 texvert[0 * 2 + 0] = (M3Gshort) (rIsect.x + rIsect.width - adjust);
371 texvert[1 * 2 + 0] = (M3Gshort) (rIsect.x + rIsect.width - adjust);
372 texvert[2 * 2 + 0] = (M3Gshort) rIsect.x;
373 texvert[3 * 2 + 0] = (M3Gshort) rIsect.x;
376 if (!(sprite->flip & FLIPY)) {
377 texvert[0 * 2 + 1] = (M3Gshort) rIsect.y;
378 texvert[1 * 2 + 1] = (M3Gshort) (rIsect.y + rIsect.height - adjust);
379 texvert[2 * 2 + 1] = (M3Gshort) rIsect.y;
380 texvert[3 * 2 + 1] = (M3Gshort) (rIsect.y + rIsect.height - adjust);
383 texvert[0 * 2 + 1] = (M3Gshort) (rIsect.y + rIsect.height - adjust);
384 texvert[1 * 2 + 1] = (M3Gshort) rIsect.y;
385 texvert[2 * 2 + 1] = (M3Gshort) (rIsect.y + rIsect.height - adjust);
386 texvert[3 * 2 + 1] = (M3Gshort) rIsect.y;
394 * \brief Overloaded Node method.
396 * Renders the sprite as a textured quad.
398 * \param self Mesh object
399 * \param ctx current render context
400 * \param patchIndex submesh index
402 static void m3gSpriteDoRender(Node *self,
404 const Matrix *toCamera,
407 Sprite *sprite = (Sprite *)self;
408 M3Gshort texvert[4 * 2];
412 M3G_UNREF(patchIndex);
414 M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
415 if (!m3gGetSpriteCoordinates(sprite,
417 m3gGetCurrentCamera(ctx),
425 M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
427 /* Get power of two image */
428 imagePow2 = m3gGetPowerOfTwoImage(sprite->image);
429 /* If NULL -> out of memory */
430 if (imagePow2 == NULL) {
434 if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
435 m3gUpdateColorMaskStatus(ctx,
436 m3gColorMask(sprite->appearance),
437 m3gAlphaMask(sprite->appearance));
440 /* Disable unwanted state. Note that we do this BEFORE setting the
441 * sprite color to avoid any problems with glColorMaterial */
442 m3gApplyDefaultMaterial();
443 m3gApplyDefaultPolygonMode();
445 /* Disable color array, normals and textures*/
446 glDisableClientState(GL_COLOR_ARRAY);
447 glDisableClientState(GL_NORMAL_ARRAY);
448 m3gDisableTextures();
450 /* Sprite image to texture unit 0 */
451 glClientActiveTexture(GL_TEXTURE0);
452 glActiveTexture(GL_TEXTURE0);
453 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
454 glTexCoordPointer(2, GL_SHORT, 0, texvert);
455 glEnable(GL_TEXTURE_2D);
456 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, (GLfixed) GL_MODULATE);
457 m3gBindTextureImage(imagePow2,
458 M3G_FILTER_BASE_LEVEL,
459 m3gIsAccelerated(ctx) ? M3G_FILTER_LINEAR : M3G_FILTER_NEAREST);
461 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
462 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
464 glMatrixMode(GL_TEXTURE);
466 glScalef(m3gRcp((M3Gfloat) m3gGetWidth(sprite->image)),
467 m3gRcp((M3Gfloat) m3gGetHeight(sprite->image)),
469 glMatrixMode(GL_MODELVIEW);
471 /* Apply fog and compositing mode */
472 #ifdef M3G_USE_NGL_API
473 m3gApplySpriteFog(sprite->appearance->fog, eyeSpace.z, eyeSpace.w);
475 m3gApplyFog(sprite->appearance->fog);
477 m3gApplyCompositingMode(sprite->appearance->compositingMode, ctx);
480 GLfixed a = (GLfixed) (0xff * sprite->totalAlphaFactor);
481 a = (a >> (NODE_ALPHA_FACTOR_BITS - 8))
482 + (a >> NODE_ALPHA_FACTOR_BITS)
483 + (a >> (NODE_ALPHA_FACTOR_BITS + 7));
484 glColor4x((GLfixed) 1 << 16, (GLfixed) 1 << 16, (GLfixed) 1 << 16, a);
488 glEnableClientState(GL_VERTEX_ARRAY);
489 glVertexPointer(3, GL_FIXED, 0, vert);
491 /* Store current matrices, then set up an identity modelview and
494 m3gPushScreenSpace(ctx, M3G_FALSE);
496 #ifndef M3G_USE_NGL_API
497 /* Transform the sprite vertices (in NDC) back to eye coordinates, so that
498 the fog distance will be calculated correctly in the OpenGL pipeline. */
500 GLfloat transform[16];
501 GLfloat scaleW[16] = { 0.f, 0.f, 0.f, 0.f,
504 0.f, 0.f, 0.f, 0.f };
505 Matrix invProjMatrix;
506 const Matrix *projMatrix = m3gProjectionMatrix(m3gGetCurrentCamera(ctx));
508 m3gMatrixInverse(&invProjMatrix, projMatrix);
509 m3gGetMatrixColumns(&invProjMatrix, transform);
511 glMatrixMode(GL_MODELVIEW);
512 glMultMatrixf(transform);
513 scaleW[0] = scaleW[5] = scaleW[10] = scaleW[15] = eyeSpace.w;
514 glMultMatrixf(scaleW);
516 glMatrixMode(GL_PROJECTION);
517 m3gGetMatrixColumns(projMatrix, transform);
518 glLoadMatrixf(transform);
522 /* Load indices -> draws the sprite */
523 M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_NGL_DRAW);
524 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
525 M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_NGL_DRAW);
527 m3gReleaseTextureImage(imagePow2);
529 /* Restore the previous modelview and projection */
536 * \brief Overloaded Node method.
538 * Picks a scaled sprite as 2D from viewport.
540 * \param self Mesh object
541 * \param mask pick scope mask
542 * \param ray pick ray
543 * \param ri RayIntersection object
544 * \param toGroup transform to originating group
545 * \retval M3G_TRUE continue pick
546 * \retval M3G_FALSE abort pick
548 static M3Gbool m3gSpriteRayIntersect(Node *self,
554 Sprite *sprite = (Sprite *)self;
555 M3Gshort texvert[4 * 2];
562 /* Check that picking is possible */
564 if (sprite->image == NULL ||
565 sprite->appearance == NULL ||
566 ri->camera == NULL ||
568 sprite->crop.width == 0 ||
569 sprite->crop.height == 0 ||
570 (self->scope & mask) == 0) {
574 /* Calculate modelview transform, picking is possible without rendering */
579 if (!m3gGetTransformTo(self, (Node *)ri->camera,
583 if (!m3gGetSpriteCoordinates(sprite, NULL,
584 (const Camera *)ri->camera, &toCamera,
585 vert, texvert, &eyeSpace, 1)) {
590 /* Do the pick in 2D, formula is from the spec and values are
591 set to 16.16 fixed point format */
593 x = m3gRoundToInt(m3gMul(2 * 65536, ri->x)) - 65536;
594 y = 65536 - m3gRoundToInt(m3gMul(2 * 65536, ri->y));
596 if (x >= vert[0 * 3 + 0] && x <= vert[2 * 3 + 0] &&
597 y <= vert[0 * 3 + 1] && y >= vert[1 * 3 + 1] ) {
599 distance = m3gDiv(m3gSub(eyeSpace.z, ray[6]), m3gSub(ray[7], ray[6]));
602 distance >= ri->tMin) return M3G_TRUE;
605 ri->distance = ri->tMin;
606 ri->submeshIndex = 0;
608 x -= vert[0 * 3 + 0];
609 y = vert[0 * 3 + 1] - y;
611 if (!(sprite->flip & FLIPX)) {
612 ri->textureS[0] = m3gAdd(texvert[0 * 2 + 0],
613 m3gDivif ((texvert[2 * 2 + 0] - texvert[0 * 2 + 0] + 1) * x,
614 vert[2 * 3 + 0] - vert[0 * 3 + 0]));
617 ri->textureS[0] = m3gSub((M3Gfloat)(texvert[0 * 2 + 0] + 1),
618 m3gDivif ((texvert[0 * 2 + 0] - texvert[2 * 2 + 0] + 1) * x,
619 vert[2 * 3 + 0] - vert[0 * 3 + 0]));
622 if (!(sprite->flip & FLIPY)) {
623 ri->textureT[0] = m3gAdd(texvert[0 * 2 + 1],
624 m3gDivif ((texvert[1 * 2 + 1] - texvert[0 * 2 + 1] + 1) * y,
625 vert[0 * 3 + 1] - vert[1 * 3 + 1]));
628 ri->textureT[0] = m3gSub((M3Gfloat)(texvert[0 * 2 + 1] + 1),
629 m3gDivif ((texvert[0 * 2 + 1] - texvert[1 * 2 + 1] + 1) * y,
630 vert[0 * 3 + 1] - vert[1 * 3 + 1]));
634 /* Finally check against alpha */
635 M3Gint threshold = 0, alpha;
637 if (sprite->appearance->compositingMode) {
638 threshold = (M3Gint)m3gMul(m3gGetAlphaThreshold(sprite->appearance->compositingMode), 256);
641 alpha = m3gGetAlpha(sprite->image, (M3Gint)ri->textureS[0], (M3Gint)ri->textureT[0]);
643 if (alpha >= threshold) {
644 /* Normalize texture coordinates */
645 ri->textureS[0] = m3gDiv(ri->textureS[0], (M3Gfloat) sprite->width);
646 ri->textureT[0] = m3gDiv(ri->textureT[0], (M3Gfloat) sprite->height);
648 ri->textureS[1] = 0.f;
649 ri->textureT[1] = 0.f;
655 ri->intersected = self;
665 * \brief Overloaded Object3D method.
667 * \param self Sprite object
668 * \param references array of reference objects
669 * \return number of references
671 static M3Gint m3gSpriteDoGetReferences(Object *self, Object **references)
673 Sprite *sprite = (Sprite *)self;
674 int num = m3gObjectDoGetReferences(self, references);
675 if (sprite->image != NULL) {
676 if (references != NULL)
677 references[num] = (Object *)sprite->image;
680 if (sprite->appearance != NULL) {
681 if (references != NULL)
682 references[num] = (Object *)sprite->appearance;
690 * \brief Overloaded Object3D method.
692 static Object *m3gSpriteFindID(Object *self, M3Gint userID)
694 Sprite *sprite = (Sprite *)self;
695 Object *found = m3gObjectFindID(self, userID);
697 if (!found && sprite->image != NULL) {
698 found = m3gFindID((Object*) sprite->image, userID);
700 if (!found && sprite->appearance != NULL) {
701 found = m3gFindID((Object*) sprite->appearance, userID);
708 * \brief Overloaded Object3D method.
710 * \param originalObj original Sprite object
711 * \param cloneObj pointer to cloned Sprite object
712 * \param pairs array for all object-duplicate pairs
713 * \param numPairs number of pairs
715 static M3Gbool m3gSpriteDuplicate(const Object *originalObj,
720 Sprite *original = (Sprite *)originalObj;
722 M3G_ASSERT(*cloneObj == NULL); /* no derived classes */
724 /* Create the clone object */
726 clone = (Sprite *)m3gCreateSprite(originalObj->interface,
729 original->appearance);
733 *cloneObj = (Object *)clone;
735 /* Duplicate our own fields */
737 clone->crop = original->crop;
738 clone->flip = original->flip;
740 /* Duplicate base class data */
742 return m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs);
747 * \brief Overloaded Object3D method.
749 * \param self Sprite object
750 * \param time current world time
751 * \return minimum validity
753 static M3Gint m3gSpriteApplyAnimation(Object *self, M3Gint time)
755 M3Gint validity, minValidity;
756 Sprite *sprite = (Sprite *)self;
758 M3G_VALIDATE_OBJECT(sprite);
760 minValidity = m3gObjectApplyAnimation(self, time);
762 if (minValidity > 0) {
763 app = (Object *) sprite->appearance;
766 validity = M3G_VFUNC(Object, app, applyAnimation)(app, time);
767 minValidity = M3G_MIN(validity, minValidity);
775 * \brief Initializes a Sprite object. See specification
776 * for default values.
778 * \param m3g M3G interface
779 * \param sprite Sprite object
780 * \param scaled scaled flag
781 * \param appearance Appearance object
782 * \param image Image2D object
783 * \retval M3G_TRUE Sprite initialized
784 * \retval M3G_FALSE initialization failed
786 static M3Gbool m3gInitSprite(Interface *m3g,
789 Appearance *appearance,
792 /* Sprite is derived from node */
793 m3gInitNode(m3g, &sprite->node, M3G_CLASS_SPRITE);
794 sprite->node.hasRenderables = M3G_TRUE;
796 m3gIncStat(m3g, M3G_STAT_RENDERABLES, 1);
798 sprite->scaled = scaled;
799 M3G_ASSIGN_REF(sprite->appearance, appearance);
800 return m3gSetSpriteImage(sprite, image);
803 /*----------------------------------------------------------------------
804 * Virtual function table
805 *--------------------------------------------------------------------*/
807 static const NodeVFTable m3gvf_Sprite = {
810 m3gSpriteApplyAnimation,
811 m3gSpriteIsCompatible,
812 m3gSpriteUpdateProperty,
813 m3gSpriteDoGetReferences,
822 m3gSpriteRayIntersect,
823 m3gSpriteSetupRender,
824 m3gNodeUpdateDuplicateReferences,
829 /*----------------------------------------------------------------------
830 * Public API functions
831 *--------------------------------------------------------------------*/
834 * \brief Creates a Sprite object.
836 * \param hInterface M3G interface
837 * \param scaled scaled flag
838 * \param hImage Image2D object
839 * \param hAppearance Appearance object
840 * \retval Sprite new Sprite object
841 * \retval NULL Sprite creating failed
843 M3G_API M3GSprite m3gCreateSprite(M3GInterface hInterface,
846 M3GAppearance hAppearance)
848 Interface *m3g = (Interface *) hInterface;
849 M3G_VALIDATE_INTERFACE(m3g);
852 m3gRaiseError(m3g, M3G_NULL_POINTER);
857 Sprite *sprite = m3gAllocZ(m3g, sizeof(Sprite));
859 if (sprite != NULL) {
860 if (!m3gInitSprite(m3g,
863 (Appearance *)hAppearance,
865 M3G_ASSIGN_REF(sprite->image, NULL);
866 M3G_ASSIGN_REF(sprite->appearance, NULL);
867 m3gFree(m3g, sprite);
872 return (M3GSprite) sprite;
877 * \brief Get sprite scaled flag.
879 * \param handle Sprite object
880 * \retval M3G_TRUE sprite is scaled
881 * \retval M3G_FALSE sprite is not scaled
883 M3G_API M3Gbool m3gIsScaledSprite(M3GSprite handle)
885 Sprite *sprite = (Sprite *) handle;
886 M3G_VALIDATE_OBJECT(sprite);
888 return sprite->scaled;
892 * \brief Set sprite appearance.
894 * \param handle Sprite object
895 * \param hAppearance Appearance object
897 M3G_API void m3gSetSpriteAppearance(M3GSprite handle,
898 M3GAppearance hAppearance)
900 Sprite *sprite = (Sprite *) handle;
901 M3G_VALIDATE_OBJECT(sprite);
903 M3G_ASSIGN_REF(sprite->appearance, hAppearance);
907 * \brief Set sprite image
909 * \param handle Sprite object
910 * \param hImage Image2D object
911 * \retval M3G_TRUE image was set
912 * \retval M3G_FALSE failed to set image
914 M3G_API M3Gbool m3gSetSpriteImage(M3GSprite handle, M3GImage hImage)
916 Sprite *sprite = (Sprite *) handle;
917 Image *image = (Image *)hImage;
918 M3G_VALIDATE_OBJECT(sprite);
921 m3gRaiseError(M3G_INTERFACE(sprite), M3G_NULL_POINTER);
925 M3G_ASSIGN_REF(sprite->image, image);
927 sprite->width = m3gGetWidth(image);
928 sprite->height = m3gGetHeight(image);
932 sprite->crop.width = m3gClampInt(sprite->width, 0, M3G_MAX_TEXTURE_DIMENSION);
933 sprite->crop.height = m3gClampInt(sprite->height, 0, M3G_MAX_TEXTURE_DIMENSION);
941 * \brief Set sprite image crop rectangle.
943 * \param handle Sprite object
944 * \param cropX crop upper left x
945 * \param cropY crop upper left y
946 * \param width crop width
947 * \param height crop height
949 M3G_API void m3gSetCrop(M3GSprite handle,
950 M3Gint cropX, M3Gint cropY,
951 M3Gint width, M3Gint height)
953 Sprite *sprite = (Sprite *) handle;
954 M3G_VALIDATE_OBJECT(sprite);
956 /* Check for illegal crop size */
957 if (!m3gInRange(width, -M3G_MAX_TEXTURE_DIMENSION, M3G_MAX_TEXTURE_DIMENSION) ||
958 !m3gInRange(height, -M3G_MAX_TEXTURE_DIMENSION, M3G_MAX_TEXTURE_DIMENSION) ) {
959 m3gRaiseError(M3G_INTERFACE(sprite), M3G_INVALID_VALUE);
963 sprite->crop.x = cropX;
964 sprite->crop.y = cropY;
967 sprite->crop.width = -width;
968 sprite->flip |= FLIPX;
971 sprite->crop.width = width;
972 sprite->flip &= ~FLIPX;
976 sprite->crop.height = -height;
977 sprite->flip |= FLIPY;
980 sprite->crop.height = height;
981 sprite->flip &= ~FLIPY;
986 * \brief Get sprite image crop parameter.
988 * \param handle Sprite object
989 * \param which which crop parameter to return
992 * \arg M3G_GET_CROPWIDTH
993 * \arg M3G_GET_CROPHEIGHT
994 * \return image crop parameter
996 M3Gint m3gGetCrop(M3GSprite handle, M3Gint which)
998 Sprite *sprite = (Sprite *) handle;
999 M3G_VALIDATE_OBJECT(sprite);
1003 return sprite->crop.x;
1005 return sprite->crop.y;
1006 case M3G_GET_CROPWIDTH:
1007 return (sprite->flip & FLIPX) ? -sprite->crop.width : sprite->crop.width;
1008 case M3G_GET_CROPHEIGHT:
1010 return (sprite->flip & FLIPY) ? -sprite->crop.height : sprite->crop.height;
1015 * \brief Gets sprite appearance.
1017 * \param handle Sprite object
1018 * \return Appearance object
1020 M3G_API M3GAppearance m3gGetSpriteAppearance(M3GSprite handle)
1022 Sprite *sprite = (Sprite *) handle;
1023 M3G_VALIDATE_OBJECT(sprite);
1025 return sprite->appearance;
1029 * \brief Gets sprite image.
1031 * \param handle Sprite object
1032 * \return Image2D object
1034 M3G_API M3GImage m3gGetSpriteImage(M3GSprite handle)
1036 Sprite *sprite = (Sprite *) handle;
1037 M3G_VALIDATE_OBJECT(sprite);
1039 return sprite->image;