os/graphics/m3g/m3gcore11/src/m3g_sprite.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     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: Sprite implementation
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Sprite implementation
    23  */
    24 
    25 #ifndef M3G_CORE_INCLUDE
    26 #   error included by m3g_core.c; do not compile separately.
    27 #endif
    28 
    29 /*#include <stdio.h>*/
    30 
    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"
    36 
    37 #define FLIPX   1
    38 #define FLIPY   2
    39 
    40 
    41 /*----------------------------------------------------------------------
    42  * Internal functions
    43  *--------------------------------------------------------------------*/
    44 
    45 /*!
    46  * \internal
    47  * \brief Destroys this Sprite object.
    48  *
    49  * \param obj Sprite object
    50  */
    51 static void m3gDestroySprite(Object *obj)
    52 {
    53     Sprite *sprite = (Sprite *) obj;
    54     M3G_VALIDATE_OBJECT(sprite);
    55 
    56     M3G_ASSIGN_REF(sprite->image, NULL);
    57     M3G_ASSIGN_REF(sprite->appearance, NULL);
    58 
    59     m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_RENDERABLES, -1);
    60     
    61     m3gDestroyNode(obj);
    62 }
    63 
    64 /*!
    65  * \internal
    66  * \brief Overloaded Object3D method.
    67  *
    68  * \param property      animation property
    69  * \retval M3G_TRUE     property supported
    70  * \retval M3G_FALSE    property not supported
    71  */
    72 static M3Gbool m3gSpriteIsCompatible(M3Gint property)
    73 {
    74     switch (property) {
    75     case M3G_ANIM_CROP:
    76         return M3G_TRUE;
    77     default:
    78         return m3gNodeIsCompatible(property);
    79     }
    80 }
    81 
    82 /*!
    83  * \internal
    84  * \brief Overloaded Node method
    85  */
    86 static M3Gint m3gSpriteGetBBox(Node *self, AABB *bbox)
    87 {
    88     Sprite *sprite = (Sprite*) self;
    89 
    90     /* Only scaled sprites can have a bounding box; non-scaled ones
    91      * are marked as non-cullable in the "SetParent" function in
    92      * m3g_node.c */
    93     
    94     if (sprite->scaled) {
    95         const AABB spriteBBox = { { -.5f, -.5f,  0.f },
    96                                   {  .5f,  .5f,  0.f } };
    97         *bbox = spriteBBox;
    98         return (4 * VFC_VERTEX_COST +
    99                 2 * VFC_TRIANGLE_COST +
   100                 VFC_NODE_OVERHEAD);
   101     }
   102     else {
   103         return 0; /* no bounding box for non-scaled sprites */
   104     }
   105 }
   106 
   107 /*!
   108  * \internal
   109  * \brief Overloaded Object3D method.
   110  *
   111  * \param self          Sprite object
   112  * \param property      animation property
   113  * \param valueSize     size of value array
   114  * \param value         value array
   115  */
   116 static void m3gSpriteUpdateProperty(Object *self,
   117                                     M3Gint property,
   118                                     M3Gint valueSize,
   119                                     const M3Gfloat *value)
   120 {
   121     Sprite *sprite = (Sprite *) self;
   122     M3G_VALIDATE_OBJECT(sprite);
   123     M3G_ASSERT_PTR(value);
   124 
   125     switch (property) {
   126     case M3G_ANIM_CROP:
   127         /* Assert that the value vector is large enough */
   128         if (valueSize > 2) {
   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) );
   138         }
   139         else {
   140             M3G_ASSERT(valueSize >= 2);
   141             m3gSetCrop(sprite,  m3gRoundToInt(value[0]),
   142                        m3gRoundToInt(value[1]),
   143                        sprite->crop.width,
   144                        sprite->crop.height );
   145         }
   146         break;
   147     default:
   148         m3gNodeUpdateProperty(self, property, valueSize, value);
   149     }
   150 }
   151 
   152 /*!
   153  * \internal
   154  * \brief Overloaded Node method.
   155  *
   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
   161  *
   162  * \retval M3G_TRUE continue render setup
   163  * \retval M3G_FALSE abort render setup
   164  */
   165 static M3Gbool m3gSpriteSetupRender(Node *self,
   166                                     const Node *caller,
   167                                     SetupRenderState *s,
   168                                     RenderQueue *renderQueue)
   169 {
   170     Sprite *sprite = (Sprite *)self;
   171     Interface *m3g = M3G_INTERFACE(sprite);
   172     M3G_UNREF(caller);
   173     m3gIncStat(M3G_INTERFACE(self), M3G_STAT_RENDER_NODES, 1);
   174 
   175     if ((self->enableBits & NODE_RENDER_BIT) != 0 &&
   176         (self->scope & renderQueue->scope) != 0) {
   177         
   178         if (sprite->appearance != NULL && sprite->image != NULL &&
   179             sprite->crop.width != 0 && sprite->crop.height != 0) {
   180 
   181             /* Fetch the cumulative alpha factor for this node */
   182             sprite->totalAlphaFactor =
   183                 (M3Gushort) m3gGetTotalAlphaFactor((Node*) sprite, renderQueue->root);
   184 
   185             /* Touch the POT image to make sure it's allocated prior
   186              * to rendering */
   187             
   188             if (!m3gGetPowerOfTwoImage(sprite->image) ||
   189                 !m3gInsertDrawable(m3g,
   190                                    renderQueue,
   191                                    self,
   192                                    &s->toCamera,
   193                                    0,
   194                                    m3gGetAppearanceSortKey(sprite->appearance)))
   195                 return M3G_FALSE;
   196         }
   197     }
   198 
   199     return M3G_TRUE;
   200 }
   201 
   202 /*!
   203  * \internal
   204  * \brief Calculates sprite vertex positions and texture coordinates.
   205  *
   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
   216  */
   217 static M3Gbool m3gGetSpriteCoordinates(Sprite *sprite,
   218                                        RenderContext *ctx,
   219                                        const Camera *cam,
   220                                        const Matrix *toCamera,
   221                                        M3Gint *vert,
   222                                        M3Gshort *texvert,
   223                                        Vec4 *eyeSpace,
   224                                        M3Gshort adjust)
   225 {
   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 */
   229     Vec4 ot;
   230     Rect rIsect, rImage;
   231 
   232     rImage.x = 0;
   233     rImage.y = 0;
   234     rImage.width = sprite->width;
   235     rImage.height = sprite->height;
   236 
   237     /* Intersection of image and crop*/
   238     if (!m3gIntersectRectangle(&rIsect, &rImage, &sprite->crop)) {
   239         /* No intersection -> nothing to render / pick */
   240         return M3G_FALSE;
   241     }
   242 
   243     /* Calculate origin and vectors after modelview */
   244     m3gTransformVec4(toCamera, &o);
   245     m3gTransformVec4(toCamera, &x);
   246     m3gTransformVec4(toCamera, &y);
   247 
   248     ot = o;
   249 
   250     m3gScaleVec4(&o, m3gRcp(o.w));
   251     m3gScaleVec4(&x, m3gRcp(x.w));
   252     m3gScaleVec4(&y, m3gRcp(y.w));
   253 
   254     /* Store eyespace coordinates */
   255     if (eyeSpace != NULL) {
   256         eyeSpace->x = o.x;
   257         eyeSpace->y = o.y;
   258         eyeSpace->z = o.z;
   259     }
   260 
   261     m3gSubVec4(&x, &o);
   262     m3gSubVec4(&y, &o);
   263 
   264     x.x = m3gAdd(ot.x, m3gLengthVec3((const Vec3*) &x));
   265     x.y = ot.y;
   266     x.z = ot.z;
   267     x.w = ot.w;
   268 
   269     y.y = m3gAdd(ot.y, m3gLengthVec3((const Vec3*) &y));
   270     y.x = ot.x;
   271     y.z = ot.z;
   272     y.w = ot.w;
   273 
   274     /* Calculate origin and vectors after projection */
   275     {
   276         const Matrix *projMatrix = m3gProjectionMatrix(cam);
   277         m3gTransformVec4(projMatrix, &ot);
   278         m3gTransformVec4(projMatrix, &x);
   279         m3gTransformVec4(projMatrix, &y);
   280     }
   281 #ifndef M3G_USE_NGL_API
   282     /* Store w after projection */
   283     if (eyeSpace != NULL) {
   284         eyeSpace->w = ot.w;
   285     }
   286 #endif
   287     m3gScaleVec4(&ot, m3gRcp(ot.w));
   288     m3gScaleVec4(&x, m3gRcp(x.w));
   289     m3gScaleVec4(&y, m3gRcp(y.w));
   290 
   291     m3gSubVec4(&x, &ot);
   292     m3gSubVec4(&y, &ot);
   293 
   294     x.x = m3gLengthVec3((const Vec3*) &x);
   295     y.y = m3gLengthVec3((const Vec3*) &y);
   296 
   297     /* Non-scaled sprites take width from crop rectangle*/
   298     if (!sprite->scaled) {
   299         M3Gint viewport[4];
   300         if (ctx != NULL) {
   301             m3gGetViewport(ctx, viewport, viewport + 1, viewport + 2, viewport + 3);
   302         }
   303         else {
   304             /* Use a dummy viewport, this is only when picking and
   305                not rendering to anything. Values must represent a valid viewport */
   306             viewport[0] = 0;
   307             viewport[1] = 0;
   308             viewport[2] = 256;
   309             viewport[3] = 256;
   310         }
   311 
   312         x.x = m3gDivif (rIsect.width, viewport[2]);
   313         y.y = m3gDivif (rIsect.height, viewport[3]);
   314 
   315         ot.x = m3gSub(ot.x,
   316                       m3gDivif (2 * sprite->crop.x + sprite->crop.width - 2 * rIsect.x - rIsect.width,
   317                                 viewport[2]));
   318 
   319         ot.y = m3gAdd(ot.y,
   320                       m3gDivif (2 * sprite->crop.y + sprite->crop.height - 2 * rIsect.y - rIsect.height,
   321                                 viewport[3]));
   322     }
   323     else {
   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);
   327 
   328         ot.x = m3gSub(ot.x,
   329                       m3gMul((M3Gfloat)(2 * sprite->crop.x + sprite->crop.width - 2 * rIsect.x - rIsect.width),
   330                              x.x));
   331 
   332         ot.y = m3gAdd(ot.y,
   333                       m3gMul((M3Gfloat)(2 * sprite->crop.y + sprite->crop.height - 2 * rIsect.y - rIsect.height),
   334                              y.y));
   335 
   336         x.x = m3gMul(x.x, (M3Gfloat) rIsect.width);
   337         y.y = m3gMul(y.y, (M3Gfloat) rIsect.height);
   338     }
   339 #ifdef M3G_USE_NGL_API
   340     /* Store final Z */
   341     if (eyeSpace != NULL) {
   342         eyeSpace->w = ot.z;
   343     }
   344 #endif
   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));
   349 
   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];
   353 
   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];
   357 
   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];
   361 
   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);
   368     }
   369     else {
   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;
   374     }
   375 
   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);
   381     }
   382     else {
   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;
   387     }
   388 
   389     return M3G_TRUE;
   390 }
   391 
   392 /*!
   393  * \internal
   394  * \brief Overloaded Node method.
   395  *
   396  * Renders the sprite as a textured quad.
   397  *
   398  * \param self Mesh object
   399  * \param ctx current render context
   400  * \param patchIndex submesh index
   401  */
   402 static void m3gSpriteDoRender(Node *self,
   403                               RenderContext *ctx,
   404                               const Matrix *toCamera,
   405                               M3Gint patchIndex)
   406 {
   407     Sprite *sprite = (Sprite *)self;
   408     M3Gshort texvert[4 * 2];
   409     M3Gint vert[4 * 3];
   410     Vec4 eyeSpace;
   411     Image *imagePow2;
   412     M3G_UNREF(patchIndex);
   413 
   414     M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
   415     if (!m3gGetSpriteCoordinates(sprite,
   416                                  ctx,
   417                                  m3gGetCurrentCamera(ctx),
   418                                  toCamera,
   419                                  vert,
   420                                  texvert,
   421                                  &eyeSpace,
   422                                  0)) {
   423         return;
   424     }
   425     M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
   426 
   427     /* Get power of two image */
   428     imagePow2 = m3gGetPowerOfTwoImage(sprite->image);
   429     /* If NULL -> out of memory */
   430     if (imagePow2 == NULL) {
   431         return;
   432     }
   433 
   434     if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
   435         m3gUpdateColorMaskStatus(ctx,
   436                                  m3gColorMask(sprite->appearance),
   437                                  m3gAlphaMask(sprite->appearance));
   438     }
   439 
   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();
   444 
   445     /* Disable color array, normals and textures*/
   446     glDisableClientState(GL_COLOR_ARRAY);
   447     glDisableClientState(GL_NORMAL_ARRAY);
   448     m3gDisableTextures();
   449 
   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);
   460 
   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);
   463     
   464     glMatrixMode(GL_TEXTURE);
   465     glLoadIdentity();
   466     glScalef(m3gRcp((M3Gfloat) m3gGetWidth(sprite->image)),
   467              m3gRcp((M3Gfloat) m3gGetHeight(sprite->image)),
   468              1.f);
   469     glMatrixMode(GL_MODELVIEW);
   470 
   471     /* Apply fog and compositing mode */
   472 #ifdef M3G_USE_NGL_API
   473     m3gApplySpriteFog(sprite->appearance->fog, eyeSpace.z, eyeSpace.w);
   474 #else
   475     m3gApplyFog(sprite->appearance->fog);
   476 #endif
   477     m3gApplyCompositingMode(sprite->appearance->compositingMode, ctx);
   478 
   479     {
   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);
   485     }
   486 
   487     /* Load vertices */
   488     glEnableClientState(GL_VERTEX_ARRAY);
   489     glVertexPointer(3, GL_FIXED, 0, vert);
   490 
   491     /* Store current matrices, then set up an identity modelview and
   492      * projection */
   493 
   494     m3gPushScreenSpace(ctx, M3G_FALSE);
   495 
   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. */
   499     {
   500         GLfloat transform[16];
   501         GLfloat scaleW[16] = { 0.f, 0.f, 0.f, 0.f,
   502                                0.f, 0.f, 0.f, 0.f,
   503                                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));
   507 
   508         m3gMatrixInverse(&invProjMatrix, projMatrix);
   509 		m3gGetMatrixColumns(&invProjMatrix, transform);
   510         
   511         glMatrixMode(GL_MODELVIEW);
   512         glMultMatrixf(transform);
   513         scaleW[0] = scaleW[5] = scaleW[10] = scaleW[15] = eyeSpace.w;
   514         glMultMatrixf(scaleW);
   515 
   516         glMatrixMode(GL_PROJECTION);
   517         m3gGetMatrixColumns(projMatrix, transform);
   518         glLoadMatrixf(transform);
   519     }
   520 #endif
   521 
   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);
   526 
   527     m3gReleaseTextureImage(imagePow2);
   528     
   529     /* Restore the previous modelview and projection */
   530 
   531     m3gPopSpace(ctx);
   532 }
   533 
   534 /*!
   535  * \internal
   536  * \brief Overloaded Node method.
   537  *
   538  * Picks a scaled sprite as 2D from viewport.
   539  *
   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
   547  */
   548 static M3Gbool m3gSpriteRayIntersect(Node *self,
   549                                      M3Gint mask,
   550                                      M3Gfloat *ray,
   551                                      RayIntersection *ri,
   552                                      Matrix *toGroup)
   553 {
   554     Sprite *sprite = (Sprite *)self;
   555     M3Gshort texvert[4 * 2];
   556     M3Gint vert[4 * 3];
   557     M3Gint x, y;
   558     Vec4 eyeSpace;
   559     M3Gfloat distance;
   560     M3G_UNREF(toGroup);
   561 
   562     /* Check that picking is possible */
   563     
   564     if (sprite->image == NULL ||
   565         sprite->appearance == NULL ||
   566         ri->camera == NULL ||
   567         !sprite->scaled ||
   568         sprite->crop.width == 0 ||
   569         sprite->crop.height == 0 ||
   570         (self->scope & mask) == 0) {
   571         return M3G_TRUE;
   572     }
   573 
   574     /* Calculate modelview transform, picking is possible without rendering */
   575     
   576     {
   577         Matrix toCamera;
   578         
   579         if (!m3gGetTransformTo(self, (Node *)ri->camera,
   580                                &toCamera)) {
   581             return M3G_FALSE;
   582         }
   583         if (!m3gGetSpriteCoordinates(sprite, NULL,
   584                                      (const Camera *)ri->camera, &toCamera,
   585                                      vert, texvert, &eyeSpace, 1)) {
   586             return M3G_TRUE;
   587         }
   588     }
   589 
   590     /* Do the pick in 2D, formula is from the spec and values are
   591        set to 16.16 fixed point format */
   592     
   593     x = m3gRoundToInt(m3gMul(2 * 65536, ri->x)) - 65536;
   594     y = 65536 - m3gRoundToInt(m3gMul(2 * 65536, ri->y));
   595 
   596     if (x >= vert[0 * 3 + 0] && x <= vert[2 * 3 + 0] &&
   597         y <= vert[0 * 3 + 1] && y >= vert[1 * 3 + 1] ) {
   598 
   599         distance = m3gDiv(m3gSub(eyeSpace.z, ray[6]), m3gSub(ray[7], ray[6]));
   600 
   601         if (distance <= 0 ||
   602             distance >= ri->tMin) return M3G_TRUE;
   603 
   604         ri->tMin = distance;
   605         ri->distance = ri->tMin;
   606         ri->submeshIndex = 0;
   607 
   608         x -= vert[0 * 3 + 0];
   609         y  = vert[0 * 3 + 1] - y;
   610 
   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]));
   615         }
   616         else {
   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]));
   620         }
   621 
   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]));
   626         }
   627         else {
   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]));
   631         }
   632 
   633         {
   634             /* Finally check against alpha */
   635             M3Gint threshold = 0, alpha;
   636 
   637             if (sprite->appearance->compositingMode) {
   638                 threshold = (M3Gint)m3gMul(m3gGetAlphaThreshold(sprite->appearance->compositingMode), 256);
   639             }
   640 
   641             alpha = m3gGetAlpha(sprite->image, (M3Gint)ri->textureS[0], (M3Gint)ri->textureT[0]);
   642 
   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);
   647 
   648                 ri->textureS[1] = 0.f;
   649                 ri->textureT[1] = 0.f;
   650 
   651                 ri->normal[0] = 0.f;
   652                 ri->normal[1] = 0.f;
   653                 ri->normal[2] = 1.f;
   654 
   655                 ri->intersected = self;
   656             }
   657         }
   658     }
   659 
   660     return M3G_TRUE;
   661 }
   662 
   663 /*!
   664  * \internal
   665  * \brief Overloaded Object3D method.
   666  *
   667  * \param self Sprite object
   668  * \param references array of reference objects
   669  * \return number of references
   670  */
   671 static M3Gint m3gSpriteDoGetReferences(Object *self, Object **references)
   672 {
   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;
   678         num++;
   679     }
   680     if (sprite->appearance != NULL) {
   681         if (references != NULL)
   682             references[num] = (Object *)sprite->appearance;
   683         num++;
   684     }
   685     return num;
   686 }
   687 
   688 /*!
   689  * \internal
   690  * \brief Overloaded Object3D method.
   691  */
   692 static Object *m3gSpriteFindID(Object *self, M3Gint userID)
   693 {
   694     Sprite *sprite = (Sprite *)self;
   695     Object *found = m3gObjectFindID(self, userID);
   696         
   697     if (!found && sprite->image != NULL) {
   698         found = m3gFindID((Object*) sprite->image, userID);
   699     }
   700     if (!found && sprite->appearance != NULL) {
   701         found = m3gFindID((Object*) sprite->appearance, userID);
   702     }
   703     return found;
   704 }
   705 
   706 /*!
   707  * \internal
   708  * \brief Overloaded Object3D method.
   709  *
   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
   714  */
   715 static M3Gbool m3gSpriteDuplicate(const Object *originalObj,
   716                                   Object **cloneObj,
   717                                   Object **pairs,
   718                                   M3Gint *numPairs)
   719 {
   720     Sprite *original = (Sprite *)originalObj;
   721     Sprite *clone;
   722     M3G_ASSERT(*cloneObj == NULL); /* no derived classes */
   723 
   724     /* Create the clone object */
   725     
   726     clone = (Sprite *)m3gCreateSprite(originalObj->interface,
   727                                       original->scaled,
   728                                       original->image,
   729                                       original->appearance);
   730     if (!clone) {
   731         return M3G_FALSE;
   732     }
   733     *cloneObj = (Object *)clone;
   734 
   735     /* Duplicate our own fields */
   736     
   737     clone->crop = original->crop;
   738     clone->flip = original->flip;
   739     
   740     /* Duplicate base class data */
   741     
   742     return m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs);
   743 }
   744 
   745 /*!
   746  * \internal
   747  * \brief Overloaded Object3D method.
   748  *
   749  * \param self Sprite object
   750  * \param time current world time
   751  * \return minimum validity
   752  */
   753 static M3Gint m3gSpriteApplyAnimation(Object *self, M3Gint time)
   754 {
   755     M3Gint validity, minValidity;
   756     Sprite *sprite = (Sprite *)self;
   757     Object *app;
   758     M3G_VALIDATE_OBJECT(sprite);
   759 
   760     minValidity = m3gObjectApplyAnimation(self, time);
   761 
   762     if (minValidity > 0) {
   763         app = (Object *) sprite->appearance;
   764         
   765         if (app != NULL) {
   766             validity = M3G_VFUNC(Object, app, applyAnimation)(app, time);
   767             minValidity = M3G_MIN(validity, minValidity);
   768         }
   769     }
   770     return minValidity;
   771 }
   772 
   773 /*!
   774  * \internal
   775  * \brief Initializes a Sprite object. See specification
   776  * for default values.
   777  *
   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
   785  */
   786 static M3Gbool m3gInitSprite(Interface *m3g,
   787                              Sprite *sprite,
   788                              M3Gbool scaled,
   789                              Appearance *appearance,
   790                              Image *image)
   791 {
   792     /* Sprite is derived from node */
   793     m3gInitNode(m3g, &sprite->node, M3G_CLASS_SPRITE);
   794     sprite->node.hasRenderables = M3G_TRUE;
   795 
   796     m3gIncStat(m3g, M3G_STAT_RENDERABLES, 1);
   797     
   798     sprite->scaled = scaled;
   799     M3G_ASSIGN_REF(sprite->appearance, appearance);
   800     return m3gSetSpriteImage(sprite, image);
   801 }
   802 
   803 /*----------------------------------------------------------------------
   804  * Virtual function table
   805  *--------------------------------------------------------------------*/
   806 
   807 static const NodeVFTable m3gvf_Sprite = {
   808     {
   809         {
   810             m3gSpriteApplyAnimation,
   811             m3gSpriteIsCompatible,
   812             m3gSpriteUpdateProperty,
   813             m3gSpriteDoGetReferences,
   814             m3gSpriteFindID,
   815             m3gSpriteDuplicate,
   816             m3gDestroySprite
   817         }
   818     },
   819     m3gNodeAlign,
   820     m3gSpriteDoRender,
   821     m3gSpriteGetBBox,
   822     m3gSpriteRayIntersect,
   823     m3gSpriteSetupRender,
   824     m3gNodeUpdateDuplicateReferences,
   825     m3gNodeValidate
   826 };
   827 
   828 
   829 /*----------------------------------------------------------------------
   830  * Public API functions
   831  *--------------------------------------------------------------------*/
   832 
   833 /*!
   834  * \brief Creates a Sprite object.
   835  *
   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
   842  */
   843 M3G_API M3GSprite m3gCreateSprite(M3GInterface hInterface,
   844                                   M3Gbool scaled,
   845                                   M3GImage hImage,
   846                                   M3GAppearance hAppearance)
   847 {
   848     Interface *m3g = (Interface *) hInterface;
   849     M3G_VALIDATE_INTERFACE(m3g);
   850 
   851     if (hImage == 0) {
   852         m3gRaiseError(m3g, M3G_NULL_POINTER);
   853         return NULL;
   854     }
   855 
   856     {
   857         Sprite *sprite =  m3gAllocZ(m3g, sizeof(Sprite));
   858 
   859         if (sprite != NULL) {
   860             if (!m3gInitSprite(m3g,
   861                                sprite,
   862                                scaled,
   863                                (Appearance *)hAppearance,
   864                                (Image *)hImage)) {
   865                 M3G_ASSIGN_REF(sprite->image, NULL);
   866                 M3G_ASSIGN_REF(sprite->appearance, NULL);
   867                 m3gFree(m3g, sprite);
   868                 return NULL;
   869             }
   870         }
   871 
   872         return (M3GSprite) sprite;
   873     }
   874 }
   875 
   876 /*!
   877  * \brief Get sprite scaled flag.
   878  *
   879  * \param handle        Sprite object
   880  * \retval M3G_TRUE     sprite is scaled
   881  * \retval M3G_FALSE    sprite is not scaled
   882  */
   883 M3G_API M3Gbool m3gIsScaledSprite(M3GSprite handle)
   884 {
   885     Sprite *sprite = (Sprite *) handle;
   886     M3G_VALIDATE_OBJECT(sprite);
   887 
   888     return sprite->scaled;
   889 }
   890 
   891 /*!
   892  * \brief Set sprite appearance.
   893  *
   894  * \param handle        Sprite object
   895  * \param hAppearance   Appearance object
   896  */
   897 M3G_API void m3gSetSpriteAppearance(M3GSprite handle,
   898                                     M3GAppearance hAppearance)
   899 {
   900     Sprite *sprite = (Sprite *) handle;
   901     M3G_VALIDATE_OBJECT(sprite);
   902 
   903     M3G_ASSIGN_REF(sprite->appearance, hAppearance);
   904 }
   905 
   906 /*!
   907  * \brief Set sprite image
   908  *
   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
   913  */
   914 M3G_API M3Gbool m3gSetSpriteImage(M3GSprite handle, M3GImage hImage)
   915 {
   916     Sprite *sprite = (Sprite *) handle;
   917     Image *image = (Image *)hImage;
   918     M3G_VALIDATE_OBJECT(sprite);
   919 
   920     if (image == NULL) {
   921         m3gRaiseError(M3G_INTERFACE(sprite), M3G_NULL_POINTER);
   922         return M3G_FALSE;
   923     }
   924 
   925     M3G_ASSIGN_REF(sprite->image, image);
   926 
   927     sprite->width = m3gGetWidth(image);
   928     sprite->height = m3gGetHeight(image);
   929 
   930     sprite->crop.x = 0;
   931     sprite->crop.y = 0;
   932     sprite->crop.width  = m3gClampInt(sprite->width,  0, M3G_MAX_TEXTURE_DIMENSION);
   933     sprite->crop.height = m3gClampInt(sprite->height, 0, M3G_MAX_TEXTURE_DIMENSION);
   934 
   935     sprite->flip = 0;
   936 
   937     return M3G_TRUE;
   938 }
   939 
   940 /*!
   941  * \brief Set sprite image crop rectangle.
   942  *
   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
   948  */
   949 M3G_API void m3gSetCrop(M3GSprite handle,
   950                         M3Gint cropX, M3Gint cropY,
   951                         M3Gint width, M3Gint height)
   952 {
   953     Sprite *sprite = (Sprite *) handle;
   954     M3G_VALIDATE_OBJECT(sprite);
   955 
   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);
   960         return;
   961     }
   962 
   963     sprite->crop.x = cropX;
   964     sprite->crop.y = cropY;
   965 
   966     if (width < 0) {
   967         sprite->crop.width = -width;
   968         sprite->flip |= FLIPX;
   969     }
   970     else {
   971         sprite->crop.width = width;
   972         sprite->flip &= ~FLIPX;
   973     }
   974 
   975     if (height < 0) {
   976         sprite->crop.height = -height;
   977         sprite->flip |= FLIPY;
   978     }
   979     else {
   980         sprite->crop.height = height;
   981         sprite->flip &= ~FLIPY;
   982     }
   983 }
   984 
   985 /*!
   986  * \brief Get sprite image crop parameter.
   987  *
   988  * \param handle        Sprite object
   989  * \param which         which crop parameter to return
   990  *                      \arg M3G_GET_CROPX
   991  *                      \arg M3G_GET_CROPY
   992  *                      \arg M3G_GET_CROPWIDTH
   993  *                      \arg M3G_GET_CROPHEIGHT
   994  * \return              image crop parameter
   995  */
   996 M3Gint m3gGetCrop(M3GSprite handle, M3Gint which)
   997 {
   998     Sprite *sprite = (Sprite *) handle;
   999     M3G_VALIDATE_OBJECT(sprite);
  1000 
  1001     switch(which) {
  1002     case M3G_GET_CROPX:
  1003         return sprite->crop.x;
  1004     case M3G_GET_CROPY:
  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:
  1009     default:
  1010         return (sprite->flip & FLIPY) ? -sprite->crop.height : sprite->crop.height;
  1011     }
  1012 }
  1013 
  1014 /*!
  1015  * \brief Gets sprite appearance.
  1016  *
  1017  * \param handle        Sprite object
  1018  * \return              Appearance object
  1019  */
  1020 M3G_API M3GAppearance m3gGetSpriteAppearance(M3GSprite handle)
  1021 {
  1022     Sprite *sprite = (Sprite *) handle;
  1023     M3G_VALIDATE_OBJECT(sprite);
  1024 
  1025     return sprite->appearance;
  1026 }
  1027 
  1028 /*!
  1029  * \brief Gets sprite image.
  1030  *
  1031  * \param handle        Sprite object
  1032  * \return              Image2D object
  1033  */
  1034 M3G_API M3GImage m3gGetSpriteImage(M3GSprite handle)
  1035 {
  1036     Sprite *sprite = (Sprite *) handle;
  1037     M3G_VALIDATE_OBJECT(sprite);
  1038 
  1039     return sprite->image;
  1040 }
  1041 
  1042 #undef FLIPX
  1043 #undef FLIPY
  1044