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