1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/graphics/m3g/m3gcore11/src/m3g_sprite.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1044 @@
1.4 +/*
1.5 +* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
1.6 +* All rights reserved.
1.7 +* This component and the accompanying materials are made available
1.8 +* under the terms of the License "Eclipse Public License v1.0"
1.9 +* which accompanies this distribution, and is available
1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.11 +*
1.12 +* Initial Contributors:
1.13 +* Nokia Corporation - initial contribution.
1.14 +*
1.15 +* Contributors:
1.16 +*
1.17 +* Description: Sprite implementation
1.18 +*
1.19 +*/
1.20 +
1.21 +
1.22 +/*!
1.23 + * \internal
1.24 + * \file
1.25 + * \brief Sprite implementation
1.26 + */
1.27 +
1.28 +#ifndef M3G_CORE_INCLUDE
1.29 +# error included by m3g_core.c; do not compile separately.
1.30 +#endif
1.31 +
1.32 +/*#include <stdio.h>*/
1.33 +
1.34 +#include "m3g_sprite.h"
1.35 +#include "m3g_appearance.h"
1.36 +#include "m3g_camera.h"
1.37 +#include "m3g_rendercontext.h"
1.38 +#include "m3g_renderqueue.h"
1.39 +
1.40 +#define FLIPX 1
1.41 +#define FLIPY 2
1.42 +
1.43 +
1.44 +/*----------------------------------------------------------------------
1.45 + * Internal functions
1.46 + *--------------------------------------------------------------------*/
1.47 +
1.48 +/*!
1.49 + * \internal
1.50 + * \brief Destroys this Sprite object.
1.51 + *
1.52 + * \param obj Sprite object
1.53 + */
1.54 +static void m3gDestroySprite(Object *obj)
1.55 +{
1.56 + Sprite *sprite = (Sprite *) obj;
1.57 + M3G_VALIDATE_OBJECT(sprite);
1.58 +
1.59 + M3G_ASSIGN_REF(sprite->image, NULL);
1.60 + M3G_ASSIGN_REF(sprite->appearance, NULL);
1.61 +
1.62 + m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_RENDERABLES, -1);
1.63 +
1.64 + m3gDestroyNode(obj);
1.65 +}
1.66 +
1.67 +/*!
1.68 + * \internal
1.69 + * \brief Overloaded Object3D method.
1.70 + *
1.71 + * \param property animation property
1.72 + * \retval M3G_TRUE property supported
1.73 + * \retval M3G_FALSE property not supported
1.74 + */
1.75 +static M3Gbool m3gSpriteIsCompatible(M3Gint property)
1.76 +{
1.77 + switch (property) {
1.78 + case M3G_ANIM_CROP:
1.79 + return M3G_TRUE;
1.80 + default:
1.81 + return m3gNodeIsCompatible(property);
1.82 + }
1.83 +}
1.84 +
1.85 +/*!
1.86 + * \internal
1.87 + * \brief Overloaded Node method
1.88 + */
1.89 +static M3Gint m3gSpriteGetBBox(Node *self, AABB *bbox)
1.90 +{
1.91 + Sprite *sprite = (Sprite*) self;
1.92 +
1.93 + /* Only scaled sprites can have a bounding box; non-scaled ones
1.94 + * are marked as non-cullable in the "SetParent" function in
1.95 + * m3g_node.c */
1.96 +
1.97 + if (sprite->scaled) {
1.98 + const AABB spriteBBox = { { -.5f, -.5f, 0.f },
1.99 + { .5f, .5f, 0.f } };
1.100 + *bbox = spriteBBox;
1.101 + return (4 * VFC_VERTEX_COST +
1.102 + 2 * VFC_TRIANGLE_COST +
1.103 + VFC_NODE_OVERHEAD);
1.104 + }
1.105 + else {
1.106 + return 0; /* no bounding box for non-scaled sprites */
1.107 + }
1.108 +}
1.109 +
1.110 +/*!
1.111 + * \internal
1.112 + * \brief Overloaded Object3D method.
1.113 + *
1.114 + * \param self Sprite object
1.115 + * \param property animation property
1.116 + * \param valueSize size of value array
1.117 + * \param value value array
1.118 + */
1.119 +static void m3gSpriteUpdateProperty(Object *self,
1.120 + M3Gint property,
1.121 + M3Gint valueSize,
1.122 + const M3Gfloat *value)
1.123 +{
1.124 + Sprite *sprite = (Sprite *) self;
1.125 + M3G_VALIDATE_OBJECT(sprite);
1.126 + M3G_ASSERT_PTR(value);
1.127 +
1.128 + switch (property) {
1.129 + case M3G_ANIM_CROP:
1.130 + /* Assert that the value vector is large enough */
1.131 + if (valueSize > 2) {
1.132 + M3G_ASSERT(valueSize >= 4);
1.133 + m3gSetCrop(sprite, m3gRoundToInt(value[0]),
1.134 + m3gRoundToInt(value[1]),
1.135 + m3gClampInt(m3gRoundToInt(value[2]),
1.136 + -M3G_MAX_TEXTURE_DIMENSION,
1.137 + M3G_MAX_TEXTURE_DIMENSION),
1.138 + m3gClampInt(m3gRoundToInt(value[3]),
1.139 + -M3G_MAX_TEXTURE_DIMENSION,
1.140 + M3G_MAX_TEXTURE_DIMENSION) );
1.141 + }
1.142 + else {
1.143 + M3G_ASSERT(valueSize >= 2);
1.144 + m3gSetCrop(sprite, m3gRoundToInt(value[0]),
1.145 + m3gRoundToInt(value[1]),
1.146 + sprite->crop.width,
1.147 + sprite->crop.height );
1.148 + }
1.149 + break;
1.150 + default:
1.151 + m3gNodeUpdateProperty(self, property, valueSize, value);
1.152 + }
1.153 +}
1.154 +
1.155 +/*!
1.156 + * \internal
1.157 + * \brief Overloaded Node method.
1.158 + *
1.159 + * \param self Sprite object
1.160 + * \param toCamera transform to camera
1.161 + * \param alphaFactor total alpha factor
1.162 + * \param caller caller node
1.163 + * \param renderQueue RenderQueue
1.164 + *
1.165 + * \retval M3G_TRUE continue render setup
1.166 + * \retval M3G_FALSE abort render setup
1.167 + */
1.168 +static M3Gbool m3gSpriteSetupRender(Node *self,
1.169 + const Node *caller,
1.170 + SetupRenderState *s,
1.171 + RenderQueue *renderQueue)
1.172 +{
1.173 + Sprite *sprite = (Sprite *)self;
1.174 + Interface *m3g = M3G_INTERFACE(sprite);
1.175 + M3G_UNREF(caller);
1.176 + m3gIncStat(M3G_INTERFACE(self), M3G_STAT_RENDER_NODES, 1);
1.177 +
1.178 + if ((self->enableBits & NODE_RENDER_BIT) != 0 &&
1.179 + (self->scope & renderQueue->scope) != 0) {
1.180 +
1.181 + if (sprite->appearance != NULL && sprite->image != NULL &&
1.182 + sprite->crop.width != 0 && sprite->crop.height != 0) {
1.183 +
1.184 + /* Fetch the cumulative alpha factor for this node */
1.185 + sprite->totalAlphaFactor =
1.186 + (M3Gushort) m3gGetTotalAlphaFactor((Node*) sprite, renderQueue->root);
1.187 +
1.188 + /* Touch the POT image to make sure it's allocated prior
1.189 + * to rendering */
1.190 +
1.191 + if (!m3gGetPowerOfTwoImage(sprite->image) ||
1.192 + !m3gInsertDrawable(m3g,
1.193 + renderQueue,
1.194 + self,
1.195 + &s->toCamera,
1.196 + 0,
1.197 + m3gGetAppearanceSortKey(sprite->appearance)))
1.198 + return M3G_FALSE;
1.199 + }
1.200 + }
1.201 +
1.202 + return M3G_TRUE;
1.203 +}
1.204 +
1.205 +/*!
1.206 + * \internal
1.207 + * \brief Calculates sprite vertex positions and texture coordinates.
1.208 + *
1.209 + * \param sprite Sprite object
1.210 + * \param ctx RenderContext object (Graphics3D)
1.211 + * \param cam Camera object
1.212 + * \param vert vertex position to fill in
1.213 + * \param texvert texture coordinates to fill in
1.214 + * \param eyeSpace coordinates after modelview
1.215 + * \param adjust adjust for texture coorinates, render and
1.216 + * pick need different adjustment
1.217 + * \retval M3G_TRUE crop and image intersect
1.218 + * \retval M3G_FALSE crop and image do not intersect
1.219 + */
1.220 +static M3Gbool m3gGetSpriteCoordinates(Sprite *sprite,
1.221 + RenderContext *ctx,
1.222 + const Camera *cam,
1.223 + const Matrix *toCamera,
1.224 + M3Gint *vert,
1.225 + M3Gshort *texvert,
1.226 + Vec4 *eyeSpace,
1.227 + M3Gshort adjust)
1.228 +{
1.229 + Vec4 o = {0, 0, 0, 1}; /* Origin */
1.230 + Vec4 x = {0.5f, 0, 0, 1}; /* Half of x unit */
1.231 + Vec4 y = {0, 0.5f, 0, 1}; /* Half of y unit */
1.232 + Vec4 ot;
1.233 + Rect rIsect, rImage;
1.234 +
1.235 + rImage.x = 0;
1.236 + rImage.y = 0;
1.237 + rImage.width = sprite->width;
1.238 + rImage.height = sprite->height;
1.239 +
1.240 + /* Intersection of image and crop*/
1.241 + if (!m3gIntersectRectangle(&rIsect, &rImage, &sprite->crop)) {
1.242 + /* No intersection -> nothing to render / pick */
1.243 + return M3G_FALSE;
1.244 + }
1.245 +
1.246 + /* Calculate origin and vectors after modelview */
1.247 + m3gTransformVec4(toCamera, &o);
1.248 + m3gTransformVec4(toCamera, &x);
1.249 + m3gTransformVec4(toCamera, &y);
1.250 +
1.251 + ot = o;
1.252 +
1.253 + m3gScaleVec4(&o, m3gRcp(o.w));
1.254 + m3gScaleVec4(&x, m3gRcp(x.w));
1.255 + m3gScaleVec4(&y, m3gRcp(y.w));
1.256 +
1.257 + /* Store eyespace coordinates */
1.258 + if (eyeSpace != NULL) {
1.259 + eyeSpace->x = o.x;
1.260 + eyeSpace->y = o.y;
1.261 + eyeSpace->z = o.z;
1.262 + }
1.263 +
1.264 + m3gSubVec4(&x, &o);
1.265 + m3gSubVec4(&y, &o);
1.266 +
1.267 + x.x = m3gAdd(ot.x, m3gLengthVec3((const Vec3*) &x));
1.268 + x.y = ot.y;
1.269 + x.z = ot.z;
1.270 + x.w = ot.w;
1.271 +
1.272 + y.y = m3gAdd(ot.y, m3gLengthVec3((const Vec3*) &y));
1.273 + y.x = ot.x;
1.274 + y.z = ot.z;
1.275 + y.w = ot.w;
1.276 +
1.277 + /* Calculate origin and vectors after projection */
1.278 + {
1.279 + const Matrix *projMatrix = m3gProjectionMatrix(cam);
1.280 + m3gTransformVec4(projMatrix, &ot);
1.281 + m3gTransformVec4(projMatrix, &x);
1.282 + m3gTransformVec4(projMatrix, &y);
1.283 + }
1.284 +#ifndef M3G_USE_NGL_API
1.285 + /* Store w after projection */
1.286 + if (eyeSpace != NULL) {
1.287 + eyeSpace->w = ot.w;
1.288 + }
1.289 +#endif
1.290 + m3gScaleVec4(&ot, m3gRcp(ot.w));
1.291 + m3gScaleVec4(&x, m3gRcp(x.w));
1.292 + m3gScaleVec4(&y, m3gRcp(y.w));
1.293 +
1.294 + m3gSubVec4(&x, &ot);
1.295 + m3gSubVec4(&y, &ot);
1.296 +
1.297 + x.x = m3gLengthVec3((const Vec3*) &x);
1.298 + y.y = m3gLengthVec3((const Vec3*) &y);
1.299 +
1.300 + /* Non-scaled sprites take width from crop rectangle*/
1.301 + if (!sprite->scaled) {
1.302 + M3Gint viewport[4];
1.303 + if (ctx != NULL) {
1.304 + m3gGetViewport(ctx, viewport, viewport + 1, viewport + 2, viewport + 3);
1.305 + }
1.306 + else {
1.307 + /* Use a dummy viewport, this is only when picking and
1.308 + not rendering to anything. Values must represent a valid viewport */
1.309 + viewport[0] = 0;
1.310 + viewport[1] = 0;
1.311 + viewport[2] = 256;
1.312 + viewport[3] = 256;
1.313 + }
1.314 +
1.315 + x.x = m3gDivif (rIsect.width, viewport[2]);
1.316 + y.y = m3gDivif (rIsect.height, viewport[3]);
1.317 +
1.318 + ot.x = m3gSub(ot.x,
1.319 + m3gDivif (2 * sprite->crop.x + sprite->crop.width - 2 * rIsect.x - rIsect.width,
1.320 + viewport[2]));
1.321 +
1.322 + ot.y = m3gAdd(ot.y,
1.323 + m3gDivif (2 * sprite->crop.y + sprite->crop.height - 2 * rIsect.y - rIsect.height,
1.324 + viewport[3]));
1.325 + }
1.326 + else {
1.327 + /* Adjust width and height according to cropping rectangle */
1.328 + x.x = m3gDiv(x.x, (M3Gfloat) sprite->crop.width);
1.329 + y.y = m3gDiv(y.y, (M3Gfloat) sprite->crop.height);
1.330 +
1.331 + ot.x = m3gSub(ot.x,
1.332 + m3gMul((M3Gfloat)(2 * sprite->crop.x + sprite->crop.width - 2 * rIsect.x - rIsect.width),
1.333 + x.x));
1.334 +
1.335 + ot.y = m3gAdd(ot.y,
1.336 + m3gMul((M3Gfloat)(2 * sprite->crop.y + sprite->crop.height - 2 * rIsect.y - rIsect.height),
1.337 + y.y));
1.338 +
1.339 + x.x = m3gMul(x.x, (M3Gfloat) rIsect.width);
1.340 + y.y = m3gMul(y.y, (M3Gfloat) rIsect.height);
1.341 + }
1.342 +#ifdef M3G_USE_NGL_API
1.343 + /* Store final Z */
1.344 + if (eyeSpace != NULL) {
1.345 + eyeSpace->w = ot.z;
1.346 + }
1.347 +#endif
1.348 + /* Set up positions */
1.349 + vert[0 * 3 + 0] = (M3Gint) m3gMul(65536, m3gSub(ot.x, x.x));
1.350 + vert[0 * 3 + 1] = m3gRoundToInt(m3gAdd(m3gMul(65536, m3gAdd(ot.y, y.y)), 0.5f));
1.351 + vert[0 * 3 + 2] = m3gRoundToInt(m3gMul(65536, ot.z));
1.352 +
1.353 + vert[1 * 3 + 0] = vert[0 * 3 + 0];
1.354 + vert[1 * 3 + 1] = (M3Gint) m3gMul(65536, m3gSub(ot.y, y.y));
1.355 + vert[1 * 3 + 2] = vert[0 * 3 + 2];
1.356 +
1.357 + vert[2 * 3 + 0] = m3gRoundToInt(m3gAdd(m3gMul(65536, m3gAdd(ot.x, x.x)), 0.5f));
1.358 + vert[2 * 3 + 1] = vert[0 * 3 + 1];
1.359 + vert[2 * 3 + 2] = vert[0 * 3 + 2];
1.360 +
1.361 + vert[3 * 3 + 0] = vert[2 * 3 + 0];
1.362 + vert[3 * 3 + 1] = vert[1 * 3 + 1];
1.363 + vert[3 * 3 + 2] = vert[0 * 3 + 2];
1.364 +
1.365 + /* Set up texture coordinates */
1.366 + if (!(sprite->flip & FLIPX)) {
1.367 + texvert[0 * 2 + 0] = (M3Gshort) rIsect.x;
1.368 + texvert[1 * 2 + 0] = (M3Gshort) rIsect.x;
1.369 + texvert[2 * 2 + 0] = (M3Gshort) (rIsect.x + rIsect.width - adjust);
1.370 + texvert[3 * 2 + 0] = (M3Gshort) (rIsect.x + rIsect.width - adjust);
1.371 + }
1.372 + else {
1.373 + texvert[0 * 2 + 0] = (M3Gshort) (rIsect.x + rIsect.width - adjust);
1.374 + texvert[1 * 2 + 0] = (M3Gshort) (rIsect.x + rIsect.width - adjust);
1.375 + texvert[2 * 2 + 0] = (M3Gshort) rIsect.x;
1.376 + texvert[3 * 2 + 0] = (M3Gshort) rIsect.x;
1.377 + }
1.378 +
1.379 + if (!(sprite->flip & FLIPY)) {
1.380 + texvert[0 * 2 + 1] = (M3Gshort) rIsect.y;
1.381 + texvert[1 * 2 + 1] = (M3Gshort) (rIsect.y + rIsect.height - adjust);
1.382 + texvert[2 * 2 + 1] = (M3Gshort) rIsect.y;
1.383 + texvert[3 * 2 + 1] = (M3Gshort) (rIsect.y + rIsect.height - adjust);
1.384 + }
1.385 + else {
1.386 + texvert[0 * 2 + 1] = (M3Gshort) (rIsect.y + rIsect.height - adjust);
1.387 + texvert[1 * 2 + 1] = (M3Gshort) rIsect.y;
1.388 + texvert[2 * 2 + 1] = (M3Gshort) (rIsect.y + rIsect.height - adjust);
1.389 + texvert[3 * 2 + 1] = (M3Gshort) rIsect.y;
1.390 + }
1.391 +
1.392 + return M3G_TRUE;
1.393 +}
1.394 +
1.395 +/*!
1.396 + * \internal
1.397 + * \brief Overloaded Node method.
1.398 + *
1.399 + * Renders the sprite as a textured quad.
1.400 + *
1.401 + * \param self Mesh object
1.402 + * \param ctx current render context
1.403 + * \param patchIndex submesh index
1.404 + */
1.405 +static void m3gSpriteDoRender(Node *self,
1.406 + RenderContext *ctx,
1.407 + const Matrix *toCamera,
1.408 + M3Gint patchIndex)
1.409 +{
1.410 + Sprite *sprite = (Sprite *)self;
1.411 + M3Gshort texvert[4 * 2];
1.412 + M3Gint vert[4 * 3];
1.413 + Vec4 eyeSpace;
1.414 + Image *imagePow2;
1.415 + M3G_UNREF(patchIndex);
1.416 +
1.417 + M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
1.418 + if (!m3gGetSpriteCoordinates(sprite,
1.419 + ctx,
1.420 + m3gGetCurrentCamera(ctx),
1.421 + toCamera,
1.422 + vert,
1.423 + texvert,
1.424 + &eyeSpace,
1.425 + 0)) {
1.426 + return;
1.427 + }
1.428 + M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
1.429 +
1.430 + /* Get power of two image */
1.431 + imagePow2 = m3gGetPowerOfTwoImage(sprite->image);
1.432 + /* If NULL -> out of memory */
1.433 + if (imagePow2 == NULL) {
1.434 + return;
1.435 + }
1.436 +
1.437 + if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
1.438 + m3gUpdateColorMaskStatus(ctx,
1.439 + m3gColorMask(sprite->appearance),
1.440 + m3gAlphaMask(sprite->appearance));
1.441 + }
1.442 +
1.443 + /* Disable unwanted state. Note that we do this BEFORE setting the
1.444 + * sprite color to avoid any problems with glColorMaterial */
1.445 + m3gApplyDefaultMaterial();
1.446 + m3gApplyDefaultPolygonMode();
1.447 +
1.448 + /* Disable color array, normals and textures*/
1.449 + glDisableClientState(GL_COLOR_ARRAY);
1.450 + glDisableClientState(GL_NORMAL_ARRAY);
1.451 + m3gDisableTextures();
1.452 +
1.453 + /* Sprite image to texture unit 0 */
1.454 + glClientActiveTexture(GL_TEXTURE0);
1.455 + glActiveTexture(GL_TEXTURE0);
1.456 + glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1.457 + glTexCoordPointer(2, GL_SHORT, 0, texvert);
1.458 + glEnable(GL_TEXTURE_2D);
1.459 + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, (GLfixed) GL_MODULATE);
1.460 + m3gBindTextureImage(imagePow2,
1.461 + M3G_FILTER_BASE_LEVEL,
1.462 + m3gIsAccelerated(ctx) ? M3G_FILTER_LINEAR : M3G_FILTER_NEAREST);
1.463 +
1.464 + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1.465 + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1.466 +
1.467 + glMatrixMode(GL_TEXTURE);
1.468 + glLoadIdentity();
1.469 + glScalef(m3gRcp((M3Gfloat) m3gGetWidth(sprite->image)),
1.470 + m3gRcp((M3Gfloat) m3gGetHeight(sprite->image)),
1.471 + 1.f);
1.472 + glMatrixMode(GL_MODELVIEW);
1.473 +
1.474 + /* Apply fog and compositing mode */
1.475 +#ifdef M3G_USE_NGL_API
1.476 + m3gApplySpriteFog(sprite->appearance->fog, eyeSpace.z, eyeSpace.w);
1.477 +#else
1.478 + m3gApplyFog(sprite->appearance->fog);
1.479 +#endif
1.480 + m3gApplyCompositingMode(sprite->appearance->compositingMode, ctx);
1.481 +
1.482 + {
1.483 + GLfixed a = (GLfixed) (0xff * sprite->totalAlphaFactor);
1.484 + a = (a >> (NODE_ALPHA_FACTOR_BITS - 8))
1.485 + + (a >> NODE_ALPHA_FACTOR_BITS)
1.486 + + (a >> (NODE_ALPHA_FACTOR_BITS + 7));
1.487 + glColor4x((GLfixed) 1 << 16, (GLfixed) 1 << 16, (GLfixed) 1 << 16, a);
1.488 + }
1.489 +
1.490 + /* Load vertices */
1.491 + glEnableClientState(GL_VERTEX_ARRAY);
1.492 + glVertexPointer(3, GL_FIXED, 0, vert);
1.493 +
1.494 + /* Store current matrices, then set up an identity modelview and
1.495 + * projection */
1.496 +
1.497 + m3gPushScreenSpace(ctx, M3G_FALSE);
1.498 +
1.499 +#ifndef M3G_USE_NGL_API
1.500 + /* Transform the sprite vertices (in NDC) back to eye coordinates, so that
1.501 + the fog distance will be calculated correctly in the OpenGL pipeline. */
1.502 + {
1.503 + GLfloat transform[16];
1.504 + GLfloat scaleW[16] = { 0.f, 0.f, 0.f, 0.f,
1.505 + 0.f, 0.f, 0.f, 0.f,
1.506 + 0.f, 0.f, 0.f, 0.f,
1.507 + 0.f, 0.f, 0.f, 0.f };
1.508 + Matrix invProjMatrix;
1.509 + const Matrix *projMatrix = m3gProjectionMatrix(m3gGetCurrentCamera(ctx));
1.510 +
1.511 + m3gMatrixInverse(&invProjMatrix, projMatrix);
1.512 + m3gGetMatrixColumns(&invProjMatrix, transform);
1.513 +
1.514 + glMatrixMode(GL_MODELVIEW);
1.515 + glMultMatrixf(transform);
1.516 + scaleW[0] = scaleW[5] = scaleW[10] = scaleW[15] = eyeSpace.w;
1.517 + glMultMatrixf(scaleW);
1.518 +
1.519 + glMatrixMode(GL_PROJECTION);
1.520 + m3gGetMatrixColumns(projMatrix, transform);
1.521 + glLoadMatrixf(transform);
1.522 + }
1.523 +#endif
1.524 +
1.525 + /* Load indices -> draws the sprite */
1.526 + M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_NGL_DRAW);
1.527 + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1.528 + M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_NGL_DRAW);
1.529 +
1.530 + m3gReleaseTextureImage(imagePow2);
1.531 +
1.532 + /* Restore the previous modelview and projection */
1.533 +
1.534 + m3gPopSpace(ctx);
1.535 +}
1.536 +
1.537 +/*!
1.538 + * \internal
1.539 + * \brief Overloaded Node method.
1.540 + *
1.541 + * Picks a scaled sprite as 2D from viewport.
1.542 + *
1.543 + * \param self Mesh object
1.544 + * \param mask pick scope mask
1.545 + * \param ray pick ray
1.546 + * \param ri RayIntersection object
1.547 + * \param toGroup transform to originating group
1.548 + * \retval M3G_TRUE continue pick
1.549 + * \retval M3G_FALSE abort pick
1.550 + */
1.551 +static M3Gbool m3gSpriteRayIntersect(Node *self,
1.552 + M3Gint mask,
1.553 + M3Gfloat *ray,
1.554 + RayIntersection *ri,
1.555 + Matrix *toGroup)
1.556 +{
1.557 + Sprite *sprite = (Sprite *)self;
1.558 + M3Gshort texvert[4 * 2];
1.559 + M3Gint vert[4 * 3];
1.560 + M3Gint x, y;
1.561 + Vec4 eyeSpace;
1.562 + M3Gfloat distance;
1.563 + M3G_UNREF(toGroup);
1.564 +
1.565 + /* Check that picking is possible */
1.566 +
1.567 + if (sprite->image == NULL ||
1.568 + sprite->appearance == NULL ||
1.569 + ri->camera == NULL ||
1.570 + !sprite->scaled ||
1.571 + sprite->crop.width == 0 ||
1.572 + sprite->crop.height == 0 ||
1.573 + (self->scope & mask) == 0) {
1.574 + return M3G_TRUE;
1.575 + }
1.576 +
1.577 + /* Calculate modelview transform, picking is possible without rendering */
1.578 +
1.579 + {
1.580 + Matrix toCamera;
1.581 +
1.582 + if (!m3gGetTransformTo(self, (Node *)ri->camera,
1.583 + &toCamera)) {
1.584 + return M3G_FALSE;
1.585 + }
1.586 + if (!m3gGetSpriteCoordinates(sprite, NULL,
1.587 + (const Camera *)ri->camera, &toCamera,
1.588 + vert, texvert, &eyeSpace, 1)) {
1.589 + return M3G_TRUE;
1.590 + }
1.591 + }
1.592 +
1.593 + /* Do the pick in 2D, formula is from the spec and values are
1.594 + set to 16.16 fixed point format */
1.595 +
1.596 + x = m3gRoundToInt(m3gMul(2 * 65536, ri->x)) - 65536;
1.597 + y = 65536 - m3gRoundToInt(m3gMul(2 * 65536, ri->y));
1.598 +
1.599 + if (x >= vert[0 * 3 + 0] && x <= vert[2 * 3 + 0] &&
1.600 + y <= vert[0 * 3 + 1] && y >= vert[1 * 3 + 1] ) {
1.601 +
1.602 + distance = m3gDiv(m3gSub(eyeSpace.z, ray[6]), m3gSub(ray[7], ray[6]));
1.603 +
1.604 + if (distance <= 0 ||
1.605 + distance >= ri->tMin) return M3G_TRUE;
1.606 +
1.607 + ri->tMin = distance;
1.608 + ri->distance = ri->tMin;
1.609 + ri->submeshIndex = 0;
1.610 +
1.611 + x -= vert[0 * 3 + 0];
1.612 + y = vert[0 * 3 + 1] - y;
1.613 +
1.614 + if (!(sprite->flip & FLIPX)) {
1.615 + ri->textureS[0] = m3gAdd(texvert[0 * 2 + 0],
1.616 + m3gDivif ((texvert[2 * 2 + 0] - texvert[0 * 2 + 0] + 1) * x,
1.617 + vert[2 * 3 + 0] - vert[0 * 3 + 0]));
1.618 + }
1.619 + else {
1.620 + ri->textureS[0] = m3gSub((M3Gfloat)(texvert[0 * 2 + 0] + 1),
1.621 + m3gDivif ((texvert[0 * 2 + 0] - texvert[2 * 2 + 0] + 1) * x,
1.622 + vert[2 * 3 + 0] - vert[0 * 3 + 0]));
1.623 + }
1.624 +
1.625 + if (!(sprite->flip & FLIPY)) {
1.626 + ri->textureT[0] = m3gAdd(texvert[0 * 2 + 1],
1.627 + m3gDivif ((texvert[1 * 2 + 1] - texvert[0 * 2 + 1] + 1) * y,
1.628 + vert[0 * 3 + 1] - vert[1 * 3 + 1]));
1.629 + }
1.630 + else {
1.631 + ri->textureT[0] = m3gSub((M3Gfloat)(texvert[0 * 2 + 1] + 1),
1.632 + m3gDivif ((texvert[0 * 2 + 1] - texvert[1 * 2 + 1] + 1) * y,
1.633 + vert[0 * 3 + 1] - vert[1 * 3 + 1]));
1.634 + }
1.635 +
1.636 + {
1.637 + /* Finally check against alpha */
1.638 + M3Gint threshold = 0, alpha;
1.639 +
1.640 + if (sprite->appearance->compositingMode) {
1.641 + threshold = (M3Gint)m3gMul(m3gGetAlphaThreshold(sprite->appearance->compositingMode), 256);
1.642 + }
1.643 +
1.644 + alpha = m3gGetAlpha(sprite->image, (M3Gint)ri->textureS[0], (M3Gint)ri->textureT[0]);
1.645 +
1.646 + if (alpha >= threshold) {
1.647 + /* Normalize texture coordinates */
1.648 + ri->textureS[0] = m3gDiv(ri->textureS[0], (M3Gfloat) sprite->width);
1.649 + ri->textureT[0] = m3gDiv(ri->textureT[0], (M3Gfloat) sprite->height);
1.650 +
1.651 + ri->textureS[1] = 0.f;
1.652 + ri->textureT[1] = 0.f;
1.653 +
1.654 + ri->normal[0] = 0.f;
1.655 + ri->normal[1] = 0.f;
1.656 + ri->normal[2] = 1.f;
1.657 +
1.658 + ri->intersected = self;
1.659 + }
1.660 + }
1.661 + }
1.662 +
1.663 + return M3G_TRUE;
1.664 +}
1.665 +
1.666 +/*!
1.667 + * \internal
1.668 + * \brief Overloaded Object3D method.
1.669 + *
1.670 + * \param self Sprite object
1.671 + * \param references array of reference objects
1.672 + * \return number of references
1.673 + */
1.674 +static M3Gint m3gSpriteDoGetReferences(Object *self, Object **references)
1.675 +{
1.676 + Sprite *sprite = (Sprite *)self;
1.677 + int num = m3gObjectDoGetReferences(self, references);
1.678 + if (sprite->image != NULL) {
1.679 + if (references != NULL)
1.680 + references[num] = (Object *)sprite->image;
1.681 + num++;
1.682 + }
1.683 + if (sprite->appearance != NULL) {
1.684 + if (references != NULL)
1.685 + references[num] = (Object *)sprite->appearance;
1.686 + num++;
1.687 + }
1.688 + return num;
1.689 +}
1.690 +
1.691 +/*!
1.692 + * \internal
1.693 + * \brief Overloaded Object3D method.
1.694 + */
1.695 +static Object *m3gSpriteFindID(Object *self, M3Gint userID)
1.696 +{
1.697 + Sprite *sprite = (Sprite *)self;
1.698 + Object *found = m3gObjectFindID(self, userID);
1.699 +
1.700 + if (!found && sprite->image != NULL) {
1.701 + found = m3gFindID((Object*) sprite->image, userID);
1.702 + }
1.703 + if (!found && sprite->appearance != NULL) {
1.704 + found = m3gFindID((Object*) sprite->appearance, userID);
1.705 + }
1.706 + return found;
1.707 +}
1.708 +
1.709 +/*!
1.710 + * \internal
1.711 + * \brief Overloaded Object3D method.
1.712 + *
1.713 + * \param originalObj original Sprite object
1.714 + * \param cloneObj pointer to cloned Sprite object
1.715 + * \param pairs array for all object-duplicate pairs
1.716 + * \param numPairs number of pairs
1.717 + */
1.718 +static M3Gbool m3gSpriteDuplicate(const Object *originalObj,
1.719 + Object **cloneObj,
1.720 + Object **pairs,
1.721 + M3Gint *numPairs)
1.722 +{
1.723 + Sprite *original = (Sprite *)originalObj;
1.724 + Sprite *clone;
1.725 + M3G_ASSERT(*cloneObj == NULL); /* no derived classes */
1.726 +
1.727 + /* Create the clone object */
1.728 +
1.729 + clone = (Sprite *)m3gCreateSprite(originalObj->interface,
1.730 + original->scaled,
1.731 + original->image,
1.732 + original->appearance);
1.733 + if (!clone) {
1.734 + return M3G_FALSE;
1.735 + }
1.736 + *cloneObj = (Object *)clone;
1.737 +
1.738 + /* Duplicate our own fields */
1.739 +
1.740 + clone->crop = original->crop;
1.741 + clone->flip = original->flip;
1.742 +
1.743 + /* Duplicate base class data */
1.744 +
1.745 + return m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs);
1.746 +}
1.747 +
1.748 +/*!
1.749 + * \internal
1.750 + * \brief Overloaded Object3D method.
1.751 + *
1.752 + * \param self Sprite object
1.753 + * \param time current world time
1.754 + * \return minimum validity
1.755 + */
1.756 +static M3Gint m3gSpriteApplyAnimation(Object *self, M3Gint time)
1.757 +{
1.758 + M3Gint validity, minValidity;
1.759 + Sprite *sprite = (Sprite *)self;
1.760 + Object *app;
1.761 + M3G_VALIDATE_OBJECT(sprite);
1.762 +
1.763 + minValidity = m3gObjectApplyAnimation(self, time);
1.764 +
1.765 + if (minValidity > 0) {
1.766 + app = (Object *) sprite->appearance;
1.767 +
1.768 + if (app != NULL) {
1.769 + validity = M3G_VFUNC(Object, app, applyAnimation)(app, time);
1.770 + minValidity = M3G_MIN(validity, minValidity);
1.771 + }
1.772 + }
1.773 + return minValidity;
1.774 +}
1.775 +
1.776 +/*!
1.777 + * \internal
1.778 + * \brief Initializes a Sprite object. See specification
1.779 + * for default values.
1.780 + *
1.781 + * \param m3g M3G interface
1.782 + * \param sprite Sprite object
1.783 + * \param scaled scaled flag
1.784 + * \param appearance Appearance object
1.785 + * \param image Image2D object
1.786 + * \retval M3G_TRUE Sprite initialized
1.787 + * \retval M3G_FALSE initialization failed
1.788 + */
1.789 +static M3Gbool m3gInitSprite(Interface *m3g,
1.790 + Sprite *sprite,
1.791 + M3Gbool scaled,
1.792 + Appearance *appearance,
1.793 + Image *image)
1.794 +{
1.795 + /* Sprite is derived from node */
1.796 + m3gInitNode(m3g, &sprite->node, M3G_CLASS_SPRITE);
1.797 + sprite->node.hasRenderables = M3G_TRUE;
1.798 +
1.799 + m3gIncStat(m3g, M3G_STAT_RENDERABLES, 1);
1.800 +
1.801 + sprite->scaled = scaled;
1.802 + M3G_ASSIGN_REF(sprite->appearance, appearance);
1.803 + return m3gSetSpriteImage(sprite, image);
1.804 +}
1.805 +
1.806 +/*----------------------------------------------------------------------
1.807 + * Virtual function table
1.808 + *--------------------------------------------------------------------*/
1.809 +
1.810 +static const NodeVFTable m3gvf_Sprite = {
1.811 + {
1.812 + {
1.813 + m3gSpriteApplyAnimation,
1.814 + m3gSpriteIsCompatible,
1.815 + m3gSpriteUpdateProperty,
1.816 + m3gSpriteDoGetReferences,
1.817 + m3gSpriteFindID,
1.818 + m3gSpriteDuplicate,
1.819 + m3gDestroySprite
1.820 + }
1.821 + },
1.822 + m3gNodeAlign,
1.823 + m3gSpriteDoRender,
1.824 + m3gSpriteGetBBox,
1.825 + m3gSpriteRayIntersect,
1.826 + m3gSpriteSetupRender,
1.827 + m3gNodeUpdateDuplicateReferences,
1.828 + m3gNodeValidate
1.829 +};
1.830 +
1.831 +
1.832 +/*----------------------------------------------------------------------
1.833 + * Public API functions
1.834 + *--------------------------------------------------------------------*/
1.835 +
1.836 +/*!
1.837 + * \brief Creates a Sprite object.
1.838 + *
1.839 + * \param hInterface M3G interface
1.840 + * \param scaled scaled flag
1.841 + * \param hImage Image2D object
1.842 + * \param hAppearance Appearance object
1.843 + * \retval Sprite new Sprite object
1.844 + * \retval NULL Sprite creating failed
1.845 + */
1.846 +M3G_API M3GSprite m3gCreateSprite(M3GInterface hInterface,
1.847 + M3Gbool scaled,
1.848 + M3GImage hImage,
1.849 + M3GAppearance hAppearance)
1.850 +{
1.851 + Interface *m3g = (Interface *) hInterface;
1.852 + M3G_VALIDATE_INTERFACE(m3g);
1.853 +
1.854 + if (hImage == 0) {
1.855 + m3gRaiseError(m3g, M3G_NULL_POINTER);
1.856 + return NULL;
1.857 + }
1.858 +
1.859 + {
1.860 + Sprite *sprite = m3gAllocZ(m3g, sizeof(Sprite));
1.861 +
1.862 + if (sprite != NULL) {
1.863 + if (!m3gInitSprite(m3g,
1.864 + sprite,
1.865 + scaled,
1.866 + (Appearance *)hAppearance,
1.867 + (Image *)hImage)) {
1.868 + M3G_ASSIGN_REF(sprite->image, NULL);
1.869 + M3G_ASSIGN_REF(sprite->appearance, NULL);
1.870 + m3gFree(m3g, sprite);
1.871 + return NULL;
1.872 + }
1.873 + }
1.874 +
1.875 + return (M3GSprite) sprite;
1.876 + }
1.877 +}
1.878 +
1.879 +/*!
1.880 + * \brief Get sprite scaled flag.
1.881 + *
1.882 + * \param handle Sprite object
1.883 + * \retval M3G_TRUE sprite is scaled
1.884 + * \retval M3G_FALSE sprite is not scaled
1.885 + */
1.886 +M3G_API M3Gbool m3gIsScaledSprite(M3GSprite handle)
1.887 +{
1.888 + Sprite *sprite = (Sprite *) handle;
1.889 + M3G_VALIDATE_OBJECT(sprite);
1.890 +
1.891 + return sprite->scaled;
1.892 +}
1.893 +
1.894 +/*!
1.895 + * \brief Set sprite appearance.
1.896 + *
1.897 + * \param handle Sprite object
1.898 + * \param hAppearance Appearance object
1.899 + */
1.900 +M3G_API void m3gSetSpriteAppearance(M3GSprite handle,
1.901 + M3GAppearance hAppearance)
1.902 +{
1.903 + Sprite *sprite = (Sprite *) handle;
1.904 + M3G_VALIDATE_OBJECT(sprite);
1.905 +
1.906 + M3G_ASSIGN_REF(sprite->appearance, hAppearance);
1.907 +}
1.908 +
1.909 +/*!
1.910 + * \brief Set sprite image
1.911 + *
1.912 + * \param handle Sprite object
1.913 + * \param hImage Image2D object
1.914 + * \retval M3G_TRUE image was set
1.915 + * \retval M3G_FALSE failed to set image
1.916 + */
1.917 +M3G_API M3Gbool m3gSetSpriteImage(M3GSprite handle, M3GImage hImage)
1.918 +{
1.919 + Sprite *sprite = (Sprite *) handle;
1.920 + Image *image = (Image *)hImage;
1.921 + M3G_VALIDATE_OBJECT(sprite);
1.922 +
1.923 + if (image == NULL) {
1.924 + m3gRaiseError(M3G_INTERFACE(sprite), M3G_NULL_POINTER);
1.925 + return M3G_FALSE;
1.926 + }
1.927 +
1.928 + M3G_ASSIGN_REF(sprite->image, image);
1.929 +
1.930 + sprite->width = m3gGetWidth(image);
1.931 + sprite->height = m3gGetHeight(image);
1.932 +
1.933 + sprite->crop.x = 0;
1.934 + sprite->crop.y = 0;
1.935 + sprite->crop.width = m3gClampInt(sprite->width, 0, M3G_MAX_TEXTURE_DIMENSION);
1.936 + sprite->crop.height = m3gClampInt(sprite->height, 0, M3G_MAX_TEXTURE_DIMENSION);
1.937 +
1.938 + sprite->flip = 0;
1.939 +
1.940 + return M3G_TRUE;
1.941 +}
1.942 +
1.943 +/*!
1.944 + * \brief Set sprite image crop rectangle.
1.945 + *
1.946 + * \param handle Sprite object
1.947 + * \param cropX crop upper left x
1.948 + * \param cropY crop upper left y
1.949 + * \param width crop width
1.950 + * \param height crop height
1.951 + */
1.952 +M3G_API void m3gSetCrop(M3GSprite handle,
1.953 + M3Gint cropX, M3Gint cropY,
1.954 + M3Gint width, M3Gint height)
1.955 +{
1.956 + Sprite *sprite = (Sprite *) handle;
1.957 + M3G_VALIDATE_OBJECT(sprite);
1.958 +
1.959 + /* Check for illegal crop size */
1.960 + if (!m3gInRange(width, -M3G_MAX_TEXTURE_DIMENSION, M3G_MAX_TEXTURE_DIMENSION) ||
1.961 + !m3gInRange(height, -M3G_MAX_TEXTURE_DIMENSION, M3G_MAX_TEXTURE_DIMENSION) ) {
1.962 + m3gRaiseError(M3G_INTERFACE(sprite), M3G_INVALID_VALUE);
1.963 + return;
1.964 + }
1.965 +
1.966 + sprite->crop.x = cropX;
1.967 + sprite->crop.y = cropY;
1.968 +
1.969 + if (width < 0) {
1.970 + sprite->crop.width = -width;
1.971 + sprite->flip |= FLIPX;
1.972 + }
1.973 + else {
1.974 + sprite->crop.width = width;
1.975 + sprite->flip &= ~FLIPX;
1.976 + }
1.977 +
1.978 + if (height < 0) {
1.979 + sprite->crop.height = -height;
1.980 + sprite->flip |= FLIPY;
1.981 + }
1.982 + else {
1.983 + sprite->crop.height = height;
1.984 + sprite->flip &= ~FLIPY;
1.985 + }
1.986 +}
1.987 +
1.988 +/*!
1.989 + * \brief Get sprite image crop parameter.
1.990 + *
1.991 + * \param handle Sprite object
1.992 + * \param which which crop parameter to return
1.993 + * \arg M3G_GET_CROPX
1.994 + * \arg M3G_GET_CROPY
1.995 + * \arg M3G_GET_CROPWIDTH
1.996 + * \arg M3G_GET_CROPHEIGHT
1.997 + * \return image crop parameter
1.998 + */
1.999 +M3Gint m3gGetCrop(M3GSprite handle, M3Gint which)
1.1000 +{
1.1001 + Sprite *sprite = (Sprite *) handle;
1.1002 + M3G_VALIDATE_OBJECT(sprite);
1.1003 +
1.1004 + switch(which) {
1.1005 + case M3G_GET_CROPX:
1.1006 + return sprite->crop.x;
1.1007 + case M3G_GET_CROPY:
1.1008 + return sprite->crop.y;
1.1009 + case M3G_GET_CROPWIDTH:
1.1010 + return (sprite->flip & FLIPX) ? -sprite->crop.width : sprite->crop.width;
1.1011 + case M3G_GET_CROPHEIGHT:
1.1012 + default:
1.1013 + return (sprite->flip & FLIPY) ? -sprite->crop.height : sprite->crop.height;
1.1014 + }
1.1015 +}
1.1016 +
1.1017 +/*!
1.1018 + * \brief Gets sprite appearance.
1.1019 + *
1.1020 + * \param handle Sprite object
1.1021 + * \return Appearance object
1.1022 + */
1.1023 +M3G_API M3GAppearance m3gGetSpriteAppearance(M3GSprite handle)
1.1024 +{
1.1025 + Sprite *sprite = (Sprite *) handle;
1.1026 + M3G_VALIDATE_OBJECT(sprite);
1.1027 +
1.1028 + return sprite->appearance;
1.1029 +}
1.1030 +
1.1031 +/*!
1.1032 + * \brief Gets sprite image.
1.1033 + *
1.1034 + * \param handle Sprite object
1.1035 + * \return Image2D object
1.1036 + */
1.1037 +M3G_API M3GImage m3gGetSpriteImage(M3GSprite handle)
1.1038 +{
1.1039 + Sprite *sprite = (Sprite *) handle;
1.1040 + M3G_VALIDATE_OBJECT(sprite);
1.1041 +
1.1042 + return sprite->image;
1.1043 +}
1.1044 +
1.1045 +#undef FLIPX
1.1046 +#undef FLIPY
1.1047 +