os/graphics/m3g/m3gcore11/src/m3g_rendercontext.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: Rendering context function implementations
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Rendering context function implementations
    23  */
    24 
    25 #ifndef M3G_CORE_INCLUDE
    26 #   error included by m3g_core.c; do not compile separately.
    27 #endif
    28 
    29 #include "m3g_rendercontext.h"
    30 #include "m3g_object.h"
    31 
    32 #include "m3g_gl.h"
    33 #include "m3g_memory.h"
    34 #include "m3g_appearance.h"
    35 #include "m3g_indexbuffer.h"
    36 #include "m3g_lightmanager.h"
    37 #include "m3g_vertexbuffer.h"
    38 #include "m3g_world.h"
    39 
    40 /*----------------------------------------------------------------------
    41  * Private data types
    42  *--------------------------------------------------------------------*/
    43 
    44 #if defined(M3G_NGL_CONTEXT_API)
    45 /*!
    46  * \internal
    47  * \brief Depth buffer data
    48  */
    49 typedef struct
    50 {
    51     M3GMemObject handle;
    52     M3Gsizei size;
    53 } DepthBuffer;
    54 #endif /*M3G_NGL_CONTEXT_API*/
    55 
    56 #if !defined(M3G_NGL_CONTEXT_API)
    57 /*! \internal \brief OpenGL rendering context record */
    58 typedef struct {
    59     EGLContext handle;
    60     M3GPixelFormat format;
    61     M3Gbitmask bufferBits;
    62     M3Gbitmask surfaceTypeBits;
    63     M3Gbitmask modeBits;
    64     M3Guint lastUseTime;
    65 } GLContextRecord;
    66 
    67 /*! \internal \brief OpenGL surface record */
    68 typedef struct {
    69     EGLSurface handle;
    70     M3Gbitmask bufferBits;
    71     M3Gbitmask type;
    72     M3Guint width;
    73     M3Guint height;
    74     M3Guint format;
    75     M3Guint targetHandle;
    76     void* pixels;
    77     M3Guint lastUseTime;
    78 } GLSurfaceRecord;
    79 #endif /*!M3G_NGL_CONTEXT_API*/
    80 
    81 /*!
    82  * \internal \brief Rendering target data
    83  */
    84 typedef struct 
    85 {
    86     M3Gbitmask type;
    87     M3GPixelFormat format;
    88     M3Gint width, height;
    89     M3Guint stride;
    90     /*@shared@*/ void *pixels, *lockedPixels;
    91     EGLSurface surface;
    92     M3Guint handle;
    93     M3Guint userData;
    94     
    95     /*!
    96      * \internal
    97      * \brief Flag set to indicate back buffer rendering
    98      *
    99      * The final target is only written to, via a format
   100      * conversion, when releasing the target.
   101      */
   102     M3Gbool buffered;
   103 } RenderTarget;
   104 
   105 /*!
   106  * \internal
   107  * \brief Back color buffer data
   108  */
   109 typedef struct {
   110 #   if defined(M3G_NGL_CONTEXT_API)
   111     M3GMemObject handle;
   112     M3Gsizei size;
   113 #   else
   114     M3Gint width, height;
   115     EGLSurface glSurface;
   116 #   endif /* M3G_NGL_CONTEXT_API */
   117     M3Gbool contentsValid;
   118 } BackBuffer;
   119 
   120 /*!
   121  * \internal
   122  * \brief Rendering context data structure
   123  *
   124  * This includes data related to a specific rendering context,
   125  * including e.g.  viewport settings, and active lights and
   126  * camera. This is equivalent to the Graphics3D class in the Java API.
   127  */
   128 struct M3GRenderContextImpl
   129 {
   130     Object object;
   131     
   132     RenderTarget target;
   133     BackBuffer backBuffer;
   134 #   if defined(M3G_NGL_CONTEXT_API)
   135     DepthBuffer depthBuffer;
   136 #   endif
   137 
   138 #   if !defined(M3G_NGL_CONTEXT_API)
   139     
   140     /* OpenGL context and surface caches */
   141     
   142     GLContextRecord glContext[M3G_MAX_GL_CONTEXTS];
   143     GLSurfaceRecord glSurface[M3G_MAX_GL_SURFACES];
   144     M3Guint cacheTimeStamp;
   145     
   146 #   endif /* M3G_NGL_CONTEXT_API */
   147 
   148     /*! \internal \brief Current/last rendering mode */
   149     M3Genum renderMode;
   150     
   151     /*! \internal \brief OpenGL viewing transformation */
   152     GLfloat viewTransform[16];
   153 
   154     /*! \internal \brief Current camera */
   155     const Camera *camera;
   156 
   157     /*! \internal \brief Light manager component */
   158     LightManager lightManager;
   159 
   160     /*! \internal \brief Last used scope, to speed up light selection */
   161     M3Gint lastScope;
   162 
   163 	M3Gfloat depthNear;
   164 	M3Gfloat depthFar;
   165     
   166     /*! \internal \brief Clipping rectangle parameters */
   167     struct { M3Gint x0, y0, x1, y1; } clip;
   168 
   169     /*! \internal \brief Scissor and viewport rectangles */
   170     struct { GLint x, y, width, height; } scissor, viewport;
   171 
   172     /*! \internal \brief Physical display size */
   173     struct { M3Gint width, height; } display;
   174     
   175     M3Gbitmask bufferBits;      /*!< \brief Rendering buffer bits */
   176     M3Gbitmask modeBits;        /*!< \brief Rendering mode bits */
   177 
   178     /*! \internal \brief OpenGL subsystem initialization flag */
   179     M3Gbool glInitialized;
   180 
   181     /*! \internal \brief HW acceleration status flag */
   182     M3Gbool accelerated;
   183     
   184     /*! \internal \brief Render queue for this context */
   185 	RenderQueue *renderQueue;
   186 
   187     M3Gbool currentColorWrite;
   188     M3Gbool currentAlphaWrite;
   189     M3Gbool inSplitDraw;
   190     M3Gbool alphaWrite;
   191 };
   192 
   193 /*
   194  * Rendering target types; note that the values here MUST match the
   195  * respective EGL bit values
   196  */
   197 enum SurfaceType {
   198     SURFACE_NONE = 0,
   199     SURFACE_IMAGE = 0x01,       /* EGL_PBUFFER_BIT */
   200     SURFACE_BITMAP = 0x02,      /* EGL_PIXMAP_BIT */
   201     SURFACE_WINDOW = 0x04,      /* EGL_WINDOW_BIT */
   202     SURFACE_MEMORY = SURFACE_IMAGE | SURFACE_BITMAP | SURFACE_WINDOW,
   203     SURFACE_EGL = 0x80
   204 };
   205 
   206 enum RenderMode {
   207     RENDER_IMMEDIATE,
   208     RENDER_NODES,
   209     RENDER_WORLD
   210 };
   211 
   212 /*----------------------------------------------------------------------
   213  * Platform specific code
   214  *--------------------------------------------------------------------*/
   215 
   216 static M3Gbool m3gBindRenderTarget(RenderContext *ctx,
   217                                    M3Genum targetType,
   218                                    M3Gint width, M3Gint height,
   219                                    M3GPixelFormat format,
   220                                    M3Guint handle);
   221 static void m3gResetRectangles(RenderContext *ctx);
   222 static void m3gSetGLDefaults(void);
   223 static void m3gUpdateScissor(RenderContext *ctx);
   224 static void m3gValidateBuffers(RenderContext *ctx);
   225 static M3Gbool m3gValidTargetFormat(M3GPixelFormat format);
   226 
   227 #include "m3g_rendercontext.inl"
   228 
   229 /*----------------------------------------------------------------------
   230  * Internal functions
   231  *--------------------------------------------------------------------*/
   232 
   233 /*!
   234  * \internal
   235  * \brief Rendering context destructor
   236  *
   237  */
   238 static void m3gDestroyContext(/*@only@*/ Object *obj)
   239 {
   240     RenderContext *ctx = (RenderContext *) obj;
   241     M3G_VALIDATE_OBJECT(ctx);
   242 
   243     M3G_ASSIGN_REF(ctx->camera, NULL);
   244     
   245 #   if defined(M3G_NGL_CONTEXT_API)
   246     if (ctx->target.type == SURFACE_MEMORY && ctx->target.pixels == NULL) {
   247         m3gSignalTargetRelease(M3G_INTERFACE(ctx), ctx->target.handle);
   248     }
   249 
   250     m3gFreeObject(M3G_INTERFACE(ctx), ctx->depthBuffer.handle);
   251     m3gFreeObject(M3G_INTERFACE(ctx), ctx->backBuffer.handle);
   252     
   253 #   else /* !M3G_NGL_CONTEXT_API */
   254     
   255     {
   256         int i;
   257         for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
   258             if (ctx->glContext[i].handle != 0) {
   259                 m3gDeleteGLContext(ctx->glContext[i].handle);
   260             }
   261         }
   262         for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
   263             if (ctx->glSurface[i].handle != 0) {
   264                 m3gDeleteGLSurface(ctx->glSurface[i].handle);
   265             }
   266         }
   267     }
   268 
   269 #   endif /* M3G_NGL_CONTEXT_API */
   270     
   271     if (ctx->glInitialized) {
   272         m3gShutdownGL(M3G_INTERFACE(ctx));
   273     }
   274 
   275     m3gDestroyLightManager(&ctx->lightManager, M3G_INTERFACE(ctx));
   276     m3gDestroyRenderQueue(M3G_INTERFACE(ctx), ctx->renderQueue);
   277     m3gDestroyObject(obj);
   278 }
   279 
   280 /*!
   281  * \internal
   282  * \brief Resets the clipping and viewport rectangles to defaults
   283  *
   284  * This is called after binding a new target.
   285  */
   286 static void m3gResetRectangles(RenderContext *ctx)
   287 {
   288     int w = ctx->display.width;
   289     int h = ctx->display.height;
   290     
   291     ctx->clip.x0 = 0;
   292     ctx->clip.y0 = ctx->target.height - ctx->display.height;
   293     ctx->clip.x1 = w;
   294     ctx->clip.y1 = ctx->clip.y0 + h;
   295 
   296     ctx->viewport.x = 0;
   297     ctx->viewport.y = 0;
   298     ctx->viewport.width = M3G_MIN(w, M3G_MAX_VIEWPORT_DIMENSION);
   299     ctx->viewport.height = M3G_MIN(h, M3G_MAX_VIEWPORT_DIMENSION);
   300 }
   301 
   302 /*!
   303  * \internal
   304  * \brief Constrains the clip rectangle to the rendering target.
   305  */
   306 static void m3gValidateClipRect(RenderContext *ctx)
   307 {
   308     int xMin = 0;
   309     int xMax = ctx->display.width;
   310     int yMin = ctx->target.height - ctx->display.height;
   311     int yMax = yMin + ctx->display.height;
   312     
   313     ctx->clip.x0 = m3gClampInt(ctx->clip.x0, xMin, xMax);
   314     ctx->clip.y0 = m3gClampInt(ctx->clip.y0, yMin, yMax);
   315     ctx->clip.x1 = m3gClampInt(ctx->clip.x1, xMin, xMax);
   316     ctx->clip.y1 = m3gClampInt(ctx->clip.y1, yMin, yMax);
   317 }
   318 
   319 /*!
   320  * \internal
   321  * \brief Computes the GL scissor rectangle
   322  *
   323  * The scissor rectangle is the intersection of the viewport and the
   324  * clipping rectangle.
   325  */
   326 static void m3gUpdateScissor(RenderContext *ctx)
   327 {
   328     int sx0 = ctx->viewport.x;
   329     int sy0 = ctx->viewport.y;
   330     int sx1 = sx0 + ctx->viewport.width;
   331     int sy1 = sy0 + ctx->viewport.height;
   332 
   333     sx0 = M3G_MAX(sx0, ctx->clip.x0);
   334     sy0 = M3G_MAX(sy0, ctx->clip.y0);
   335     sx1 = M3G_MIN(sx1, ctx->clip.x1);
   336     sy1 = M3G_MIN(sy1, ctx->clip.y1);
   337 
   338     ctx->scissor.x = sx0;
   339     ctx->scissor.y = sy0;
   340     
   341     if (sx0 < sx1 && sy0 < sy1) {
   342         ctx->scissor.width = sx1 - sx0;
   343         ctx->scissor.height = sy1 - sy0;
   344     }
   345     else {
   346         ctx->scissor.width = ctx->scissor.height = 0;
   347     }
   348 }
   349 
   350 /*!
   351  * \internal
   352  * \brief Checks whether we can render in a given format
   353  */
   354 static M3Gbool m3gValidTargetFormat(M3GPixelFormat format)
   355 {
   356     return m3gInRange(format, M3G_RGB8, M3G_RGBA4);
   357 }
   358 
   359 /*!
   360  * \internal
   361  * \brief Checks whether a given format has alpha
   362  */
   363 static M3Gbool m3gFormatHasAlpha(M3GPixelFormat format)
   364 {
   365     switch (format) {
   366     case M3G_A8:
   367     case M3G_LA8:
   368     case M3G_LA4:
   369     case M3G_RGBA8:
   370     case M3G_BGRA8:
   371     case M3G_RGBA4:
   372     case M3G_RGB5A1:
   373     case M3G_PALETTE8_RGBA8:
   374         return M3G_TRUE;
   375     default:
   376         return M3G_FALSE;
   377     }
   378 }
   379 
   380 /*!
   381  * \internal
   382  * \brief Sets the global alpha write enable flag. 
   383  *
   384  *	Used for disabling the alpha channel writes when the rendering 
   385  *  target is a Java MIDP Image that has an alpha channel.
   386  *
   387  * \param ctx        the rendering context
   388  * \param enable     alpha write enable flag
   389  */
   390 M3G_API void m3gSetAlphaWrite(M3GRenderContext ctx, M3Gbool enable)
   391 {
   392 	ctx->alphaWrite = enable;
   393 }
   394 
   395 /*!
   396  * \internal
   397  * \brief Reads the global alpha write enable flag. 
   398  *
   399  * \param ctx        the rendering context
   400  */
   401 M3G_API M3Gbool m3gGetAlphaWrite(M3GRenderContext ctx)
   402 {
   403 	return ctx->alphaWrite;
   404 }
   405 
   406 /*!
   407  * \brief Frees all GLES resources allocated by the M3G API 
   408  *        (EGL surfaces, contexts and texture objects). 
   409  *
   410  * \note M3G must not be bound to any target when calling this.
   411  *
   412  */
   413 M3G_API void m3gFreeGLESResources(M3GRenderContext ctx)
   414 {
   415 #ifdef M3G_ENABLE_GLES_RESOURCE_HANDLING
   416 
   417     PointerArray image2DObjects;
   418     M3Gint i;
   419 
   420     /* M3G must not be bound to a rendering target at this point. */
   421     if (ctx->target.type != SURFACE_NONE) {
   422         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
   423     }
   424 
   425     /* EGL might not be initialized yet, so do it here just in case. */ 
   426     eglInitialize(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL);
   427     eglBindAPI(EGL_OPENGL_ES_API);
   428     eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL, NULL);
   429 
   430     /* Delete EGL surfaces */
   431     for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
   432         GLSurfaceRecord *surf = &ctx->glSurface[i];
   433         if (surf->handle) {
   434             m3gDeleteGLSurface(surf->handle);
   435         }
   436         m3gZero(surf, sizeof(GLSurfaceRecord));
   437     }
   438     if (ctx->backBuffer.glSurface != NULL) {
   439         m3gDeleteGLSurface(ctx->backBuffer.glSurface);
   440         m3gZero(&ctx->backBuffer, sizeof(BackBuffer));
   441     }
   442 
   443     /* Delete EGL contexts */
   444     for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
   445         GLContextRecord *context = &ctx->glContext[i];
   446         if (context->handle) {
   447             m3gDeleteGLContext(context->handle);
   448         }
   449         m3gZero(context, sizeof(GLContextRecord));
   450     }
   451 
   452     /* Delete references to GLES texture objects from all live Image2D objects. 
   453        Texture objects themselves have already been destroyed with the last GL context. */
   454 
   455     m3gInitArray(&image2DObjects);
   456     m3gGetObjectsWithClassID(M3G_INTERFACE(ctx), M3G_CLASS_IMAGE, &image2DObjects);
   457 
   458     i = m3gArraySize(&image2DObjects);
   459 
   460     while (i > 0) {
   461         Image *image = (Image*)m3gGetArrayElement(&image2DObjects, --i);
   462 
   463         m3gInvalidateImage(image);
   464         image->texObject = 0;
   465     }
   466     m3gDestroyArray(&image2DObjects, M3G_INTERFACE(ctx));
   467 #endif
   468 }
   469 
   470 
   471 /*!
   472  * \internal
   473  * \brief Sets up a new rendering target
   474  *
   475  * \param ctx        the rendering context
   476  * \param targetType rendering target type
   477  * \param width      width of the target
   478  * \param height     height of the target
   479  * \param format     target pixel format
   480  * \param handle     user object handle
   481  */
   482 static M3Gbool m3gBindRenderTarget(RenderContext *ctx,
   483                                    M3Genum targetType,
   484                                    M3Gint width, M3Gint height,
   485                                    M3GPixelFormat format,
   486                                    M3Guint handle)
   487 {
   488     /* Check for generic errors */
   489     
   490     if (ctx->target.type != SURFACE_NONE) {
   491         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
   492         return M3G_FALSE;
   493     }
   494     if (!m3gValidTargetFormat(format)) {
   495         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
   496         return M3G_FALSE;
   497     }
   498 
   499     /* If target width or height exceeds maximum viewport width or height
   500        an exception is thrown. */
   501     
   502     if (width > M3G_MAX_VIEWPORT_WIDTH ||
   503         height > M3G_MAX_VIEWPORT_HEIGHT) {
   504         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
   505         return M3G_FALSE;
   506     }
   507 
   508     /* Everything checks out; set up the target parameters */
   509     
   510     ctx->target.type = targetType;
   511     ctx->target.width = width;
   512     ctx->target.height = height;
   513     ctx->display.width = width;
   514     ctx->display.height = height;
   515     ctx->target.format = format;
   516     ctx->target.handle = handle;
   517     m3gResetRectangles(ctx);
   518     m3gUpdateScissor(ctx);
   519     m3gValidateBuffers(ctx);
   520     
   521     /* Invalidate lights in case we're using a different OpenGL
   522      * rendering context this time around */
   523     
   524     ctx->lastScope = 0;
   525     
   526     return M3G_TRUE;
   527 }
   528 
   529 /*!
   530  * \internal
   531  * \brief Initializes the current GL context to default settings.
   532  */
   533 static void m3gSetGLDefaults(void)
   534 {
   535 	static const GLfloat black[] = {0.f, 0.f, 0.f, 0.f};
   536     glEnable(GL_NORMALIZE);
   537     glEnable(GL_SCISSOR_TEST);
   538 	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, black);
   539     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   540 }
   541 
   542 /*!
   543  * \internal
   544  * \brief Validates the buffers required for a rendering context
   545  *
   546  * Allocates or reallocates buffers as necessary, according to the
   547  * currently set flags of the context.
   548  */
   549 static void m3gValidateBuffers(RenderContext *ctx)
   550 {
   551     M3G_VALIDATE_OBJECT(ctx);
   552 
   553     /* Initialize OpenGL if not already done */
   554     
   555     if (!ctx->glInitialized) {
   556         m3gInitializeGL(M3G_INTERFACE(ctx));
   557         ctx->glInitialized = M3G_TRUE;
   558     }
   559 
   560     /* Check whether we can render directly to the target or need to
   561      * use a back buffer */
   562     
   563     ctx->target.buffered = !m3gCanDirectRender(ctx);
   564 #   if defined(M3G_FORCE_BUFFERED_RENDERING)
   565     ctx->target.buffered = M3G_TRUE;
   566 #   endif
   567     
   568     /* If direct rendering wasn't possible, check that the back buffer
   569      * for buffered rendering exists. */
   570     
   571     if (ctx->target.buffered) {
   572         if (!m3gValidateBackBuffer(ctx)) {
   573             return; /* out of memory */
   574         }
   575     }
   576 
   577     /* With the legacy NGL API, we also manage the depth buffer */
   578     
   579 #   if defined(M3G_NGL_CONTEXT_API)
   580     if (!m3gValidateDepthBuffer(ctx)) {
   581         return; /* out of memory */
   582     }
   583 #   endif
   584 
   585     /* Delay blitting from the front buffer until we know it's
   586      * necessary; let's raise a flag to check that later on */
   587     
   588     if (ctx->target.buffered) {
   589         if (ctx->modeBits & M3G_OVERWRITE_BIT) {
   590             ctx->backBuffer.contentsValid = M3G_TRUE;
   591         }
   592         else {
   593             ctx->backBuffer.contentsValid = M3G_FALSE;
   594         }
   595     }
   596 }
   597 
   598 /*!
   599  * \internal
   600  * \brief Makes a GL context current to this thread and the currently
   601  * set rendering target buffer
   602  */
   603 static void m3gMakeCurrent(RenderContext *ctx)
   604 {
   605     m3gMakeGLCurrent(ctx);
   606 
   607     /* Note that the depth buffer may in some cases exist even if not
   608      * explicitly requested, so we need to disable the depth test just
   609      * in case */
   610     
   611     if ((ctx->bufferBits & M3G_DEPTH_BUFFER_BIT) == 0) {
   612         glDisable(GL_DEPTH_TEST);
   613     }
   614     else {
   615         glEnable(GL_DEPTH_TEST);
   616     }
   617 
   618     /* Enable multisampling if required */
   619 
   620     if (ctx->modeBits & M3G_ANTIALIAS_BIT) {
   621         glEnable(GL_MULTISAMPLE);
   622     }
   623     else {
   624         glDisable(GL_MULTISAMPLE);
   625     }
   626     
   627     M3G_ASSERT_GL;
   628 }
   629 
   630 /*!
   631  * \internal
   632  * \brief Returns the HW acceleration status of the current context
   633  */
   634 static M3Gbool m3gIsAccelerated(const RenderContext *ctx)
   635 {
   636     return ctx->accelerated;
   637 }
   638 
   639 /*!
   640  * \internal
   641  * \brief Sets the currently enabled lights to the GL state
   642  *
   643  * \note the correct viewing matrix *must* be set prior to calling
   644  * this for the lights to be transformed into eye space correctly
   645  */
   646 static M3G_INLINE void m3gApplyLights(RenderContext *ctx, M3Gint scope)
   647 {
   648     if (ctx->lastScope != scope) {
   649 
   650         /* If coming from RenderNode, we have the geometry in camera
   651          * space but the lights in world space, so we need to apply
   652          * the viewing matrix to the lights only */
   653         
   654         if (ctx->renderMode == RENDER_NODES) {
   655             glPushMatrix();
   656             glLoadMatrixf(ctx->viewTransform);
   657         }
   658         
   659         m3gSelectGLLights(&ctx->lightManager, 8, scope, 0, 0, 0);
   660         ctx->lastScope = scope;
   661         
   662         if (ctx->renderMode == RENDER_NODES) {
   663             glPopMatrix();
   664         }
   665     }
   666 	M3G_ASSERT_GL;
   667 }
   668 
   669 /*!
   670  * \internal
   671  * \brief Gets the current camera
   672  */
   673 static const Camera *m3gGetCurrentCamera(const RenderContext *ctx) {
   674 	return ctx->camera;	
   675 }
   676 
   677 /*!
   678  * \internal
   679  * \brief Sets up some rendering parameters that
   680  * do not change during scene renders.
   681  */
   682 static void m3gInitRender(M3GRenderContext context, M3Genum renderMode)
   683 {
   684     RenderContext *ctx = (RenderContext *) context;
   685     M3G_VALIDATE_OBJECT(ctx);
   686 
   687     m3gIncrementRenderTimeStamp(ctx);
   688     m3gMakeCurrent(ctx);
   689     m3gCollectGLObjects(M3G_INTERFACE(ctx));
   690     
   691     /* If buffered rendering, blit the image to the back buffer at
   692      * this point */
   693     
   694     if (ctx->target.buffered && !ctx->backBuffer.contentsValid) {
   695         m3gUpdateBackBuffer(ctx);
   696     }
   697     
   698     /* Set up viewport and scissoring */
   699     
   700     glViewport(ctx->viewport.x, ctx->viewport.y,
   701                ctx->viewport.width, ctx->viewport.height);
   702 	glDepthRangef(ctx->depthNear, ctx->depthFar);
   703     glScissor(ctx->scissor.x, ctx->scissor.y,
   704               ctx->scissor.width, ctx->scissor.height);
   705     M3G_ASSERT_GL;
   706     
   707     /* Set up the projection and viewing transformations (static
   708      * during rendering) */
   709 
   710 	m3gApplyProjection(ctx->camera);
   711     if (renderMode == RENDER_NODES) {
   712         glLoadIdentity();
   713     }
   714     else {
   715         glLoadMatrixf(ctx->viewTransform);
   716     }
   717     M3G_ASSERT_GL;
   718 
   719     /* Invalidate any already set GL lights if rendering mode changed */
   720     
   721     if (renderMode != ctx->renderMode) {
   722         ctx->lastScope = 0;
   723     }
   724     ctx->renderMode = renderMode;
   725 }
   726 
   727 /*!
   728  * \internal
   729  * \brief A workaround for a broken implementation of glColorMask
   730  *
   731  * Saves the framebuffer in the OpenGL default texture each time the
   732  * color mask changes, for restoring later.  Not very pretty, but
   733  * works as long as the default texture is not touched in between --
   734  * currently, we only touch that when copying to and from the back
   735  * buffer.
   736  *
   737  * \param newColorWrite the color mask state we're about to change to
   738  * \param newAlphaWrite the alpha write state we're about to change to
   739  */
   740 static void m3gUpdateColorMaskStatus(RenderContext *ctx,
   741                                      M3Gbool newColorWrite,
   742                                      M3Gbool newAlphaWrite)
   743 {
   744     GLint pow2Width, pow2Height;
   745 
   746 	/* Get the global alpha write value */
   747 	newAlphaWrite &= m3gGetAlphaWrite(ctx);
   748 
   749     /* Check that the ColorMask state is actually about to change */
   750     
   751     if (ctx->currentColorWrite == newColorWrite
   752         && (ctx->currentAlphaWrite == newAlphaWrite || !m3gFormatHasAlpha(ctx->target.format))) {
   753         return; /* no change, quick exit */
   754     }
   755     
   756     pow2Width = m3gNextPowerOfTwo(ctx->clip.x1 - ctx->clip.x0);
   757     pow2Height = m3gNextPowerOfTwo(ctx->clip.y1 - ctx->clip.y0);
   758     
   759     /* If we previously had stored something, restore it now */
   760 
   761     if (ctx->currentColorWrite != ctx->currentAlphaWrite) {
   762         
   763         /* Disable any stray state we don't want */
   764 
   765         glDisable(GL_CULL_FACE);
   766         glDisable(GL_ALPHA_TEST);
   767         glDisableClientState(GL_NORMAL_ARRAY);
   768         glDisableClientState(GL_COLOR_ARRAY);
   769         glDisable(GL_LIGHTING);
   770         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
   771         glDepthMask(GL_FALSE);
   772         glDepthFunc(GL_ALWAYS);
   773         m3gDisableTextures();
   774         M3G_ASSERT_GL;
   775     
   776         /* Bind the default texture and set up screen space rendering */
   777         
   778         glActiveTexture(GL_TEXTURE0);
   779         glBindTexture(GL_TEXTURE_2D, 0);
   780         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   781         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   782         M3G_ASSERT_GL;
   783 
   784         glScissor(ctx->clip.x0, ctx->clip.y0,
   785                   ctx->clip.x1 - ctx->clip.x0, ctx->clip.y1 - ctx->clip.y0);
   786         m3gPushScreenSpace(ctx, M3G_FALSE);
   787         glViewport(0, 0, ctx->target.width, ctx->target.height);
   788         glMatrixMode(GL_PROJECTION);
   789         glOrthox(0, ctx->target.width << 16,
   790                  0, ctx->target.height << 16,
   791                  -1 << 16, 1 << 16);
   792         glMatrixMode(GL_MODELVIEW);
   793             
   794         /* Set up texture and vertex coordinate arrays */
   795 
   796         glClientActiveTexture(GL_TEXTURE0);
   797         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   798         glEnableClientState(GL_VERTEX_ARRAY);
   799         glMatrixMode(GL_TEXTURE);
   800         glLoadIdentity();
   801         glMatrixMode(GL_MODELVIEW);
   802         M3G_ASSERT_GL;
   803 
   804         /* Blend the texture with the frame buffer */
   805         {
   806             static const M3Gbyte tc[8] = { 0, 0, 0, 1, 1, 0, 1, 1 };
   807             GLshort pos[8];
   808             
   809             GLfixed cm = (GLfixed)(ctx->currentColorWrite ? 0 : 1 << 16);
   810             GLfixed am = (GLfixed)(ctx->currentAlphaWrite ? 0 : 1 << 16);
   811 
   812             glVertexPointer(2, GL_SHORT, 0, pos);
   813             glTexCoordPointer(2, GL_BYTE, 0, tc);
   814                 
   815             pos[0] = (GLshort) ctx->clip.x0;
   816             pos[1] = (GLshort) ctx->clip.y0;
   817             pos[2] = pos[0];
   818             pos[3] = (GLshort) (pos[1] + pow2Height);
   819             pos[4] = (GLshort) (pos[0] + pow2Width);
   820             pos[5] = pos[1];
   821             pos[6] = pos[4];
   822             pos[7] = pos[3];
   823             
   824             glEnable(GL_BLEND);
   825             glColor4x(cm, cm, cm, am);
   826 
   827             /* Zero the masked channels */
   828             
   829             glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
   830             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
   831 
   832             /* Add the masked channels from the stored texture */
   833             
   834             glEnable(GL_TEXTURE_2D);
   835             glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   836             glBlendFunc(GL_ONE, GL_ONE);
   837             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
   838         }
   839             
   840         /* Restore the mandatory state */
   841             
   842         glScissor(ctx->scissor.x, ctx->scissor.y,
   843                   ctx->scissor.width, ctx->scissor.height);
   844         glViewport(ctx->viewport.x, ctx->viewport.y,
   845                    ctx->viewport.width, ctx->viewport.height);
   846         m3gPopSpace(ctx);
   847     }
   848     
   849     /* Copy the current clip rectangle into the default texture if
   850      * we're going to be rendering with unsupported masks in effect */
   851     
   852     if (newColorWrite != newAlphaWrite) {
   853         GLenum err;
   854             
   855         glBindTexture(GL_TEXTURE_2D, 0);
   856         M3G_ASSERT_GL;
   857             
   858         glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
   859                          ctx->clip.x0, ctx->clip.y0,
   860                          pow2Width, pow2Height,
   861                          0);
   862         err = glGetError();
   863         if (err == GL_INVALID_OPERATION) {
   864             /* Incompatible FB format -- must be GL_RGB then */
   865             glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
   866                              ctx->clip.x0, ctx->clip.y0,
   867                              pow2Width, pow2Height,
   868                              0);
   869             err = glGetError();
   870         }
   871         if (err == GL_OUT_OF_MEMORY) {
   872             m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
   873         }
   874         M3G_ASSERT(!err);
   875     }
   876     else {
   877         
   878         /* Texture not needed for now, so allow GL to free some
   879          * resources */
   880         
   881         glTexImage2D(GL_TEXTURE_2D, 0,
   882                      GL_RGBA,
   883                      1, 1,
   884                      0,
   885                      GL_RGBA, GL_UNSIGNED_BYTE, NULL);
   886     }
   887     
   888     ctx->currentColorWrite = newColorWrite;
   889     ctx->currentAlphaWrite = newAlphaWrite;
   890 }
   891 
   892 /*!
   893  * \internal
   894  * \brief Sets the GL to input screen space coordinates
   895  *
   896  * This pushes the current modelview and projection matrices into the
   897  * matrix stack, then sets up an orthogonal projection and an identity
   898  * modelview matrix.
   899  * 
   900  * \param ctx the rendering context
   901  * \param realPixels M3G_TRUE to use actual pixel coordinates,
   902  * M3G_FALSE to use normalized device coordinates
   903  */
   904 static void m3gPushScreenSpace(RenderContext *ctx, M3Gbool realPixels)
   905 {
   906     M3G_VALIDATE_OBJECT(ctx);
   907     
   908     glMatrixMode(GL_PROJECTION);
   909     glPushMatrix();
   910     glLoadIdentity();
   911     if (realPixels) {
   912         int w = ctx->viewport.width;
   913         int h = ctx->viewport.height;
   914         glOrthox(0, w << 16, 0, h << 16, -1 << 16, 1 << 16);
   915     }
   916     glMatrixMode(GL_MODELVIEW);
   917     glPushMatrix();
   918     glLoadIdentity();
   919 }
   920 
   921 /*!
   922  * \internal
   923  * \brief Restores the projection and modelview matrix modified by
   924  * m3gPushScreenSpace
   925  */
   926 static void m3gPopSpace(RenderContext *ctx)
   927 {
   928     M3G_VALIDATE_OBJECT(ctx);
   929     
   930     M3G_UNREF(ctx);
   931     glMatrixMode(GL_PROJECTION);
   932     glPopMatrix();
   933     glMatrixMode(GL_MODELVIEW);
   934     glPopMatrix();
   935 }
   936 
   937 /*!
   938  * \internal
   939  * \brief Clears the current buffer(s)
   940  */
   941 static void m3gClearInternal(RenderContext *ctx, Background *bg)
   942 {
   943     m3gMakeCurrent(ctx);
   944     
   945     /* If buffered rendering, copy data to the back buffer at this
   946      * point if we're not clearing the whole clip rectangle */
   947     
   948     if (ctx->target.buffered && !ctx->backBuffer.contentsValid) {
   949         if (ctx->scissor.x > ctx->clip.x0 || ctx->scissor.y > ctx->clip.y0 ||
   950             ctx->scissor.x + ctx->scissor.width < ctx->clip.x1 ||
   951             ctx->scissor.y + ctx->scissor.height < ctx->clip.y1) {
   952             m3gUpdateBackBuffer(ctx);
   953         }
   954     }
   955 
   956     if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
   957         m3gUpdateColorMaskStatus(ctx, M3G_TRUE, M3G_TRUE);
   958     }
   959 
   960     glDepthMask(GL_TRUE);
   961     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, m3gGetAlphaWrite(ctx));
   962     glDepthRangef(ctx->depthNear, ctx->depthFar);
   963     glViewport(ctx->viewport.x, ctx->viewport.y,
   964                ctx->viewport.width, ctx->viewport.height);
   965     glScissor(ctx->scissor.x, ctx->scissor.y,
   966               ctx->scissor.width, ctx->scissor.height);
   967 
   968     /* Touch the background image to make sure it's created prior to
   969      * locking memory for rendering */
   970     
   971     if (bg != NULL && bg->image != NULL) {
   972         if (!m3gGetPowerOfTwoImage(bg->image)) {
   973             return; /* out of memory */
   974         }
   975     }
   976 
   977     /* All clear for clearing... */
   978     
   979     m3gLockFrameBuffer(ctx);
   980     
   981     if (bg != NULL) {
   982         m3gApplyBackground(ctx, bg);
   983         if (ctx->target.buffered && bg->colorClearEnable) {
   984             ctx->backBuffer.contentsValid = M3G_TRUE;
   985         }
   986     }
   987     else {
   988         glClearColorx(0, 0, 0, 0);
   989         glClearDepthx(1 << 16);
   990         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
   991         if (ctx->target.buffered) {
   992             ctx->backBuffer.contentsValid = M3G_TRUE;
   993         }
   994     }
   995         
   996     m3gReleaseFrameBuffer(ctx);
   997 }
   998 
   999 /*!
  1000  * \internal
  1001  * \brief Draws a batch of primitives
  1002  *
  1003  * This is the place most rendering commands are eventually routed to;
  1004  * sprites and backgrounds are the only exception to this. We assume
  1005  * that all eror checking has been performed at this point.
  1006  */
  1007 static void m3gDrawMesh(RenderContext *ctx,
  1008                         const VertexBuffer *vb,
  1009                         const IndexBuffer *ib,
  1010                         const Appearance *app,
  1011                         const M3GMatrix *modelTransform,
  1012                         M3Gint alphaFactor,
  1013                         M3Gint scope)
  1014 {
  1015     M3G_VALIDATE_OBJECT(ctx);
  1016     M3G_VALIDATE_OBJECT(vb);
  1017     M3G_VALIDATE_OBJECT(ib);
  1018     M3G_VALIDATE_OBJECT(app);
  1019 
  1020     /* Check whether we need to use alternate rendering to get
  1021      * two-sided lighting */
  1022     if (m3gGetTwoSidedLightingWorkaround(M3G_INTERFACE(ctx))) {
  1023         if (m3gSplitDrawMesh(ctx, vb, ib, app, modelTransform, alphaFactor, scope)) {
  1024             return;
  1025         }
  1026     }
  1027 
  1028     M3G_ASSERT(m3gInRange(alphaFactor, 0, 0x10000));
  1029     
  1030     if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
  1031 		m3gUpdateColorMaskStatus(ctx, m3gColorMask(app), m3gAlphaMask(app));
  1032     }
  1033     
  1034     /* Load lights */
  1035     
  1036     m3gApplyLights(ctx, scope);
  1037     
  1038     /* Apply the extra modeling transformation if present */
  1039     
  1040     if (modelTransform != NULL) {
  1041 		float transform[16];
  1042 		m3gGetMatrixColumns(modelTransform, transform);
  1043         
  1044         glPushMatrix();
  1045         glMultMatrixf(transform);
  1046     }
  1047 
  1048     /* Check whether we need to create an alpha-factored color cache
  1049      * for the vertex buffer; this requires unlocking the frame buffer
  1050      * for a while, and we may even run out of memory in the process,
  1051      * but we still need to exit with the frame buffer lock and the
  1052      * matrix stack in the expected state */
  1053 
  1054     if (alphaFactor < 0x10000 && !m3gValidateAlphaCache(vb)) {
  1055         M3Gbool ok;
  1056         m3gReleaseFrameBuffer(ctx);
  1057         ok = m3gCreateAlphaColorCache(vb->colors);
  1058         m3gLockFrameBuffer(ctx);
  1059         if (!ok) {
  1060             goto RestoreModelview; /* let's just skip the drawing part */
  1061         }
  1062     }
  1063 
  1064 #   if defined(M3G_NGL_TEXTURE_API)
  1065     /* Similarly to the alpha cache above, also check whether any
  1066      * textures may need to allocate mipmaps at this point */
  1067     {
  1068         M3Gint i;
  1069         for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
  1070             Texture *tex = app->texture[i];
  1071             if (tex && !m3gValidateTextureMipmapping(tex)) {
  1072                 M3Gbool ok;
  1073                 m3gReleaseFrameBuffer(ctx);
  1074                 ok = m3gValidateMipmapMemory(m3gGetTextureImage(tex));
  1075                 m3gLockFrameBuffer(ctx);
  1076                 if (!ok) {
  1077                     goto RestoreModelview;
  1078                 }
  1079             }
  1080         }
  1081     }
  1082 #   endif
  1083     
  1084     /* Load up the rest of the stuff we need for rendering; note that
  1085      * the vertex buffer scale and bias apply to the texture matrix
  1086      * from the appearance object, so they need to be applied last */
  1087     
  1088     m3gApplyAppearance(app, ctx, alphaFactor);
  1089     m3gLockVertexBuffer(vb, alphaFactor);
  1090     m3gApplyScaleAndBias(vb);
  1091     
  1092     /* All ready, render and then release the stuff we bound above */
  1093     
  1094     m3gSendIndexBuffer(ib);
  1095     m3gReleaseVertexBuffer(vb);
  1096     m3gReleaseTextures(app);
  1097 
  1098     /* Restore viewing-only modelview if changed */
  1099 
  1100 RestoreModelview:
  1101     if (modelTransform != NULL) {
  1102         glPopMatrix();
  1103     }
  1104 }
  1105 
  1106 /*!
  1107  * \internal
  1108  * \brief Validates background format against current target
  1109  *
  1110  * \retval M3G_TRUE valid format
  1111  * \retval M3G_FALSE invalid format
  1112  */
  1113 static M3Gbool m3gValidateBackground(RenderContext *ctx, Background *bg)
  1114 {
  1115     /* Check that source image and target formats match */
  1116     if (bg != NULL && bg->image != NULL) {
  1117         M3GPixelFormat boundFormat =
  1118             (ctx->target.type == SURFACE_IMAGE)
  1119             ? m3gPixelFormat(((const Image *)ctx->target.handle)->format)
  1120             : ctx->target.format;
  1121         if (ctx->target.type == SURFACE_IMAGE && boundFormat == M3G_RGBA8) {
  1122             return (m3gGetFormat(bg->image) == M3G_RGBA);
  1123         }
  1124         else {
  1125             return (m3gGetFormat(bg->image) == M3G_RGB);
  1126         }
  1127     }
  1128 
  1129     return M3G_TRUE;
  1130 }
  1131 
  1132 /*----------------------------------------------------------------------
  1133  * Virtual function table
  1134  *--------------------------------------------------------------------*/
  1135 
  1136 static const ObjectVFTable m3gvf_RenderContext = {
  1137     NULL, /* ApplyAnimation */
  1138     NULL, /* IsCompatible */
  1139     NULL, /* UpdateProperty */
  1140     NULL, /* GetReference */
  1141     NULL, /* find */
  1142     NULL, /* CreateClone */
  1143     m3gDestroyContext
  1144 };
  1145 
  1146 
  1147 /*----------------------------------------------------------------------
  1148  * Public API
  1149  *--------------------------------------------------------------------*/
  1150 
  1151 /*!
  1152  * \brief Creates and initializes a new rendering context
  1153  *
  1154  * \param bufferBits buffer bitmask
  1155  * \param width maximum width of context
  1156  * \param height maximum height of context
  1157  * \param modeBits hint bitmask
  1158  * \param mem pointer to memory block to allocate from
  1159  */
  1160 /*@access M3GInterface@*/
  1161 /*@access M3GRenderContext@*/
  1162 /*@only@*/
  1163 M3G_API M3GRenderContext m3gCreateContext(M3GInterface interface)/*@*/
  1164 {
  1165     Interface *m3g = (Interface*) interface;
  1166     M3G_VALIDATE_INTERFACE(m3g);
  1167         
  1168     {
  1169         RenderContext *ctx =
  1170             (RenderContext*) m3gAllocZ(m3g, (int) sizeof(RenderContext));
  1171         if (ctx == NULL) {
  1172             return NULL; /* m3gAlloc automatically raises out-of-mem */
  1173         }
  1174 
  1175 		ctx->renderQueue = m3gCreateRenderQueue(m3g);
  1176         if (ctx->renderQueue == NULL) {
  1177             m3gFree(m3g, ctx);
  1178             return NULL;
  1179         }
  1180         ctx->bufferBits = M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT;
  1181         ctx->depthNear = 0.0f;
  1182         ctx->depthFar = 1.0f;
  1183 
  1184         m3gInitObject(&ctx->object, m3g, M3G_CLASS_RENDER_CONTEXT);
  1185 
  1186 		m3gSetAlphaWrite(ctx, M3G_TRUE);
  1187 
  1188         if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
  1189             ctx->currentColorWrite = M3G_TRUE;
  1190             ctx->currentAlphaWrite = m3gGetAlphaWrite(ctx);
  1191         }
  1192         
  1193 		return (M3GRenderContext)ctx;
  1194     }
  1195 }
  1196 
  1197 /*!
  1198  * \brief Sets the buffers to use for subsequent rendering
  1199  */
  1200 M3G_API M3Gbool m3gSetRenderBuffers(M3GRenderContext hCtx,
  1201                                     M3Gbitmask bufferBits)
  1202 {
  1203     RenderContext *ctx = (RenderContext *) hCtx;
  1204     M3G_VALIDATE_OBJECT(ctx);
  1205 
  1206     if ((bufferBits & ~(M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT|M3G_STENCIL_BUFFER_BIT|M3G_MULTISAMPLE_BUFFER_BIT)) != 0) {
  1207         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
  1208         return M3G_FALSE;
  1209     }
  1210     ctx->bufferBits = bufferBits;
  1211     return M3G_TRUE;
  1212 }
  1213 
  1214 /*!
  1215  * \brief Sets the rendering quality hints to use for subsequent
  1216  * rendering
  1217  *
  1218  * \note This may not take effect before the target is released and
  1219  * rebound
  1220  */
  1221 M3G_API M3Gbool m3gSetRenderHints(M3GRenderContext hCtx, M3Gbitmask modeBits)
  1222 {
  1223     RenderContext *ctx = (RenderContext *) hCtx;
  1224     M3G_VALIDATE_OBJECT(ctx);
  1225 
  1226     if ((modeBits & ~(M3G_OVERWRITE_BIT|M3G_ANTIALIAS_BIT|M3G_DITHER_BIT|M3G_TRUECOLOR_BIT)) != 0) {
  1227         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
  1228         return M3G_FALSE;
  1229     }
  1230     
  1231     /* Disable features not supported in the current configuration */
  1232 
  1233     if (M3G_SUPPORT_ANTIALIASING == M3G_FALSE ||
  1234         !m3gIsAntialiasingSupported(M3G_INTERFACE(ctx))) {
  1235         modeBits &= ~M3G_ANTIALIAS_BIT;
  1236     }
  1237     if (M3G_SUPPORT_DITHERING == M3G_FALSE) {
  1238         modeBits &= ~M3G_DITHER_BIT;
  1239     }
  1240     if (M3G_SUPPORT_TRUE_COLOR == M3G_FALSE) {
  1241         modeBits &= ~M3G_TRUECOLOR_BIT;
  1242     }
  1243 
  1244     ctx->modeBits = modeBits;
  1245     return M3G_TRUE;
  1246 }
  1247 
  1248 M3G_API void m3gBindImageTarget(M3GRenderContext hCtx, M3GImage hImage)
  1249 {
  1250     RenderContext *ctx = (RenderContext *) hCtx;
  1251     Image *img = (Image *) hImage;
  1252     M3G_VALIDATE_OBJECT(ctx);
  1253     M3G_VALIDATE_OBJECT(img);
  1254 
  1255     M3G_LOG1(M3G_LOG_RENDERING, "Binding image target 0x%08X\n",
  1256              (unsigned) img);
  1257 
  1258     /* Check for image-specific errors */
  1259     
  1260     if ((img->flags & M3G_DYNAMIC) == 0
  1261         || !m3gValidTargetFormat(img->internalFormat)) {
  1262         
  1263         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
  1264         return;
  1265     }
  1266 
  1267     /* Do the generic checking and set-up */
  1268     
  1269     if (!m3gBindRenderTarget(ctx,
  1270                              SURFACE_IMAGE,
  1271                              img->width, img->height,
  1272                              img->internalFormat,
  1273                              (M3Guint) hImage)) {
  1274         return; /* appropriate error raised automatically */
  1275     }
  1276 
  1277     /* Set up image-specific parameters */
  1278     
  1279 #   if defined(M3G_NGL_CONTEXT_API)
  1280     ctx->target.stride = m3gGetImageStride(img);
  1281     ctx->target.pixels = NULL;
  1282 #   endif
  1283     
  1284     m3gAddRef((Object*) img);
  1285 }
  1286 
  1287 /*!
  1288  */
  1289 M3G_API M3Guint m3gGetUserHandle(M3GRenderContext hCtx)
  1290 {
  1291     RenderContext *ctx = (RenderContext *) hCtx;
  1292     M3G_VALIDATE_OBJECT(ctx);
  1293 
  1294     if (ctx->target.type == SURFACE_MEMORY) {
  1295         return ctx->target.handle;
  1296     }
  1297     return 0;
  1298 }
  1299 
  1300 /*!
  1301  */
  1302 M3G_API void m3gSetUserData(M3GRenderContext hCtx, M3Guint hData)
  1303 {
  1304     RenderContext *ctx = (RenderContext *) hCtx;
  1305     M3G_VALIDATE_OBJECT(ctx);
  1306     ctx->target.userData = hData;
  1307 }
  1308 
  1309 /*!
  1310  */
  1311 M3G_API M3Guint m3gGetUserData(M3GRenderContext hCtx)
  1312 {
  1313     RenderContext *ctx = (RenderContext *) hCtx;
  1314     M3G_VALIDATE_OBJECT(ctx);
  1315     return ctx->target.userData;
  1316 }
  1317 
  1318 /*!
  1319  * \brief Clears the current buffer(s)
  1320  */
  1321 M3G_API void m3gClear(M3GRenderContext context, M3GBackground hBackground)
  1322 {
  1323     RenderContext *ctx = (RenderContext*) context;
  1324     Background *bg = (Background *) hBackground;
  1325     M3G_VALIDATE_OBJECT(ctx);
  1326 
  1327     M3G_LOG(M3G_LOG_STAGES, "Clearing frame buffer\n");
  1328     
  1329     /* Check errors */
  1330     
  1331     if (ctx->target.type == SURFACE_NONE) {
  1332         m3gRaiseError(M3G_INTERFACE(context), M3G_INVALID_OPERATION);
  1333         return;
  1334     }
  1335 
  1336     if(m3gValidateBackground(ctx, bg)) {
  1337         m3gClearInternal(ctx, bg);
  1338     }
  1339     else {
  1340         m3gRaiseError(M3G_INTERFACE(bg), M3G_INVALID_VALUE);
  1341     }
  1342 }
  1343 
  1344 /*!
  1345  * \brief Release the currently bound color buffer
  1346  *
  1347  * Flushes all rendering and commits the final result to the currently
  1348  * bound target color buffer. Any changes to the target buffer since
  1349  * it was bound may be overwritten.
  1350  */
  1351 M3G_API void m3gReleaseTarget(M3GRenderContext context)
  1352 {
  1353     RenderContext *ctx = (RenderContext*) context;
  1354     M3G_VALIDATE_OBJECT(ctx);
  1355 
  1356     M3G_LOG(M3G_LOG_RENDERING, "Releasing target\n");
  1357     
  1358     if (ctx->target.type == SURFACE_NONE) {
  1359         return;
  1360     }
  1361     
  1362     m3gMakeCurrent(ctx);
  1363 
  1364     if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
  1365         m3gUpdateColorMaskStatus(ctx, M3G_TRUE, M3G_TRUE);
  1366     }
  1367 
  1368     glFinish();
  1369 
  1370     /* Update the real target if we rendered into the back buffer */
  1371     
  1372     if (ctx->target.buffered) {
  1373         m3gUpdateTargetBuffer(ctx);
  1374     }
  1375 
  1376     /* Invalidate Image targets so that mipmap levels and/or OpenGL
  1377      * texture objects are updated accordingly */
  1378     
  1379     if (ctx->target.type == SURFACE_IMAGE) {
  1380         Image *img = (Image *) ctx->target.handle;
  1381         M3G_VALIDATE_OBJECT(img);
  1382         m3gInvalidateImage(img);
  1383         m3gDeleteRef((Object*) img);
  1384     }
  1385 
  1386     /* Swap in case we rendered onto a double-buffered surface,
  1387      * release any GL resources that might have been release since the
  1388      * last time we rendered, then release the GL context so we don't
  1389      * hog resources */
  1390 #   if !defined(M3G_NGL_CONTEXT_API)
  1391     if (ctx->target.type == SURFACE_WINDOW) {
  1392         m3gSwapBuffers(ctx->target.surface);
  1393     }
  1394 #   endif
  1395     m3gCollectGLObjects(M3G_INTERFACE(ctx));
  1396 #   if !defined(M3G_NGL_CONTEXT_API)
  1397     m3gMakeGLCurrent(NULL);
  1398     ctx->target.surface = NULL;
  1399 #   else
  1400     if (ctx->target.type == SURFACE_MEMORY && ctx->target.pixels == NULL) {
  1401         m3gSignalTargetRelease(M3G_INTERFACE(ctx), ctx->target.handle);
  1402     }
  1403 #   endif
  1404 
  1405     ctx->target.type = SURFACE_NONE;
  1406     ctx->renderQueue->root = NULL;
  1407 
  1408 #   if (M3G_PROFILE_LOG_INTERVAL > 0)
  1409     m3gLogProfileCounters(M3G_INTERFACE(ctx));
  1410 #   endif
  1411 }
  1412 
  1413 /*!
  1414  * \brief Sets a camera for this context
  1415  */
  1416 M3G_API void m3gSetCamera(M3GRenderContext context,
  1417                           M3GCamera hCamera,
  1418                           M3GMatrix *transform)
  1419 {
  1420 	Matrix m;
  1421     RenderContext *ctx = (RenderContext*) context;
  1422 	const Camera *camera = (Camera *)hCamera;
  1423 
  1424     M3G_VALIDATE_OBJECT(ctx);
  1425 
  1426     M3G_ASSIGN_REF(ctx->camera, camera);
  1427 
  1428 	if (transform != NULL) {
  1429 		if (!m3gMatrixInverse(&m, transform)) {
  1430             m3gRaiseError(M3G_INTERFACE(ctx), M3G_ARITHMETIC_ERROR);
  1431             return;
  1432         }
  1433 	}
  1434 	else {
  1435 		m3gIdentityMatrix(&m);
  1436 	}
  1437 
  1438 	m3gGetMatrixColumns(&m, ctx->viewTransform);
  1439 
  1440     ctx->lastScope = 0;
  1441 }
  1442 
  1443 /*!
  1444  * \brief Adds a light to the light array for this context
  1445  */
  1446 M3G_API M3Gint m3gAddLight(M3GRenderContext hCtx,
  1447                            M3GLight hLight,
  1448                            const M3GMatrix *transform)
  1449 {
  1450     RenderContext *ctx = (RenderContext *)hCtx;
  1451     Light *light = (Light *)hLight;
  1452     M3G_VALIDATE_OBJECT(ctx);
  1453 
  1454     if (light == NULL) {
  1455         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
  1456         return -1;
  1457     }
  1458     else {
  1459         LightManager *mgr = &ctx->lightManager;
  1460         M3G_VALIDATE_OBJECT(light);
  1461         ctx->lastScope = 0;
  1462         return m3gInsertLight(mgr, light, transform, M3G_INTERFACE(ctx));
  1463     }
  1464 }
  1465 
  1466 /**
  1467  * \brief Sets a light for this context
  1468  */
  1469 M3G_API void m3gSetLight(M3GRenderContext context,
  1470                          M3Gint lightIndex,
  1471                          M3GLight hLight,
  1472                          const M3GMatrix *transform)
  1473 {
  1474     RenderContext *ctx = (RenderContext*) context;
  1475 	Light *light = (Light *)hLight;
  1476     M3G_VALIDATE_OBJECT(ctx);
  1477 
  1478 	/* Check for invalid arguments */
  1479 	if (lightIndex < 0 || lightIndex >= m3gLightArraySize(&ctx->lightManager)) {
  1480 		m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_INDEX);
  1481         return;
  1482 	}
  1483 
  1484     ctx->lastScope = 0;
  1485     m3gReplaceLight(&ctx->lightManager, lightIndex, light, transform);
  1486 }
  1487 
  1488 /*!
  1489  * \brief Removes all lights from this context
  1490  */
  1491 M3G_API void m3gClearLights(M3GRenderContext context)
  1492 {
  1493     RenderContext *ctx = (RenderContext *)context;
  1494     M3G_VALIDATE_OBJECT(ctx);
  1495     ctx->lastScope = 0;
  1496     m3gClearLights2(&ctx->lightManager);
  1497 }
  1498 
  1499 /*!
  1500  * \brief Sets the viewport
  1501  *
  1502  */
  1503 M3G_API void m3gSetViewport(M3GRenderContext hCtx,
  1504                             M3Gint x, M3Gint y,
  1505                             M3Gint width, M3Gint height)
  1506 {
  1507     RenderContext *ctx = (RenderContext *)hCtx;
  1508     M3G_VALIDATE_OBJECT(ctx);
  1509 
  1510     /* Note that the error checking here differs from that specified
  1511      * for the Java API; this is to avoid complications when setting
  1512      * from BindTarget where the clip rectangle may be zero.
  1513      * Additional checks are performed in the Java glue code. */
  1514     
  1515     if (width < 0 || height < 0) {
  1516         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
  1517         return;
  1518     }
  1519     
  1520     width = M3G_MIN(width, M3G_MAX_VIEWPORT_DIMENSION);
  1521     height = M3G_MIN(height, M3G_MAX_VIEWPORT_DIMENSION);
  1522     
  1523     ctx->viewport.x = x;
  1524     ctx->viewport.y = ctx->target.height - (y + height);
  1525     ctx->viewport.width = width;
  1526     ctx->viewport.height = height;
  1527     m3gUpdateScissor(ctx);
  1528 }
  1529 
  1530 
  1531 /*!
  1532  * \brief Gets the viewport
  1533  */
  1534 M3G_API void m3gGetViewport(M3GRenderContext hCtx,
  1535                             M3Gint *x, M3Gint *y,
  1536                             M3Gint *width, M3Gint *height)
  1537 {
  1538     RenderContext *ctx = (RenderContext *)hCtx;
  1539     M3G_VALIDATE_OBJECT(ctx);
  1540 
  1541     *x = ctx->viewport.x;
  1542     *y = ctx->target.height - (ctx->viewport.y + ctx->viewport.height);
  1543     *width = ctx->viewport.width;
  1544     *height = ctx->viewport.height;
  1545 }
  1546 
  1547 /*!
  1548  * \brief Sets the scissor rectangle
  1549  */
  1550 M3G_API void m3gSetClipRect(M3GRenderContext hCtx,
  1551                             M3Gint x, M3Gint y,
  1552                             M3Gint width, M3Gint height)
  1553 {
  1554     RenderContext *ctx = (RenderContext *)hCtx;
  1555     M3G_VALIDATE_OBJECT(ctx);
  1556 
  1557     if (width < 0 || height < 0) {
  1558         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
  1559         return;
  1560     }
  1561     ctx->clip.x0 = x;
  1562     ctx->clip.y0 = ctx->target.height - (y + height);
  1563     ctx->clip.x1 = x + width;
  1564     ctx->clip.y1 = ctx->clip.y0 + height;
  1565     m3gValidateClipRect(ctx);
  1566     m3gUpdateScissor(ctx);
  1567 }
  1568 
  1569 /*!
  1570  * \brief Sets the physical display area
  1571  *
  1572  * The display are is normally set to the full rendering targte size
  1573  * in m3gBindTarget, but this function allows overriding the default
  1574  * setting.
  1575  *
  1576  * Any pixels outside of the display area can be discarded for
  1577  * performance.  The origin is assumed to be in the top-left corner of
  1578  * the rendering target.
  1579  */
  1580 M3G_API void m3gSetDisplayArea(M3GRenderContext hCtx,
  1581                                M3Gint width, M3Gint height)
  1582 {
  1583     RenderContext *ctx = (RenderContext*) hCtx;
  1584     M3G_VALIDATE_OBJECT(ctx);
  1585     
  1586     ctx->display.width = M3G_MIN(width, ctx->target.width);
  1587     ctx->display.height = M3G_MIN(height, ctx->target.height);
  1588 }
  1589 
  1590 /*!
  1591  * \brief Sets depth range
  1592  * 
  1593  */
  1594 M3G_API void m3gSetDepthRange(M3GRenderContext hCtx,
  1595                               M3Gfloat depthNear, M3Gfloat depthFar)
  1596 {
  1597     RenderContext *ctx = (RenderContext *)hCtx;
  1598     M3G_VALIDATE_OBJECT(ctx);
  1599 	
  1600 	if (depthNear < 0 || depthNear > 1.0f || depthFar < 0 || depthFar > 1.0f) {
  1601 		m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
  1602         return;
  1603     }
  1604 
  1605 	ctx->depthNear = depthNear;
  1606 	ctx->depthFar = depthFar;
  1607 }
  1608 
  1609 /*!
  1610  * \brief Gets depth range
  1611  * 
  1612  */
  1613 M3G_API void m3gGetDepthRange(M3GRenderContext hCtx,
  1614                               M3Gfloat *depthNear, M3Gfloat *depthFar)
  1615 {
  1616     RenderContext *ctx = (RenderContext *)hCtx;
  1617     M3G_VALIDATE_OBJECT(ctx);
  1618 	
  1619 	*depthNear = ctx->depthNear;
  1620 	*depthFar= ctx->depthFar;
  1621 }
  1622 
  1623 /*!
  1624  * \brief Gets current view transform
  1625  * 
  1626  */
  1627 
  1628 M3G_API void m3gGetViewTransform(M3GRenderContext hCtx,
  1629                                  M3GMatrix *transform)
  1630 {
  1631     RenderContext *ctx = (RenderContext *)hCtx;
  1632     M3G_VALIDATE_OBJECT(ctx);
  1633     m3gSetMatrixColumns(transform, ctx->viewTransform);
  1634     m3gInvertMatrix(transform); /*lint !e534 always invertible */
  1635 }
  1636 
  1637 /*!
  1638  * \brief Gets current Camera
  1639  * 
  1640  */
  1641 
  1642 M3G_API M3GCamera m3gGetCamera(M3GRenderContext hCtx)
  1643 {
  1644     RenderContext *ctx = (RenderContext *)hCtx;
  1645     M3G_VALIDATE_OBJECT(ctx);
  1646     return (M3GCamera) ctx->camera;
  1647 }
  1648 
  1649 /*!
  1650  * \brief Gets light transform of given light
  1651  * 
  1652  */
  1653 
  1654 M3G_API M3GLight m3gGetLightTransform (M3GRenderContext hCtx,
  1655                                        M3Gint lightIndex, M3GMatrix *transform)
  1656 {
  1657     RenderContext *ctx = (RenderContext *)hCtx;
  1658     M3G_VALIDATE_OBJECT(ctx);
  1659     return m3gGetLightTransformInternal(&ctx->lightManager, lightIndex, transform);
  1660 }
  1661 
  1662 /*!
  1663  * \brief Gets light count
  1664  * 
  1665  */
  1666 
  1667 M3G_API M3Gsizei m3gGetLightCount (M3GRenderContext hCtx)
  1668 {
  1669     RenderContext *ctx = (RenderContext *)hCtx;
  1670     M3G_VALIDATE_OBJECT(ctx);
  1671     return m3gLightArraySize(&ctx->lightManager);
  1672 }
  1673 
  1674 /*!
  1675  * \brief Renders a world
  1676  *
  1677  */
  1678 M3G_API void m3gRenderWorld(M3GRenderContext context, M3GWorld hWorld)
  1679 {
  1680 	Camera *camera;
  1681     RenderContext *ctx = (RenderContext*) context;
  1682 	World *world = (World *) hWorld;
  1683 
  1684     M3G_LOG1(M3G_LOG_STAGES, "Rendering World 0x%08X\n", (unsigned) world);
  1685     
  1686     M3G_VALIDATE_OBJECT(ctx);
  1687     M3G_VALIDATE_OBJECT(world);
  1688 
  1689 	camera = m3gGetActiveCamera(world);
  1690 
  1691     /* Check for errors */
  1692     
  1693     if (ctx->target.type == SURFACE_NONE) {
  1694         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
  1695         return;
  1696     }
  1697     
  1698 	if (camera == NULL ||
  1699         !m3gIsChildOf((Node *)world, (Node *)camera)) {
  1700 		m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
  1701         return;
  1702 	}
  1703 
  1704     /* Exit if the camera will show nothing (zero view volume) */
  1705     
  1706     if (!m3gValidProjection(camera)) {
  1707         return;
  1708     }
  1709 
  1710     /* Override the currently set viewing transformation with identity
  1711      * (will fix this before we return) */
  1712     
  1713 	m3gSetCamera(ctx, camera, NULL);
  1714 
  1715     if (m3gValidateBackground(ctx, world->background)) {
  1716     	m3gClearInternal(ctx, world->background);
  1717     }
  1718     else {
  1719         m3gRaiseError(M3G_INTERFACE(world), M3G_INVALID_OPERATION);
  1720         return;
  1721     }
  1722 
  1723     /* All clear for rendering */
  1724     
  1725     M3G_LOG(M3G_LOG_RENDERING, "Rendering: start\n");    
  1726     M3G_ASSERT(ctx->renderQueue->root == NULL);
  1727     M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
  1728     
  1729     if (m3gValidateNode((Node*) world, NODE_RENDER_BIT, camera->node.scope)) {
  1730         M3Gbool setup;
  1731         SetupRenderState s;
  1732         M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
  1733 
  1734         /* We start the traversal from the camera, so set the initial
  1735          * camera-space transformation to identity */
  1736         
  1737         m3gIdentityMatrix(&s.toCamera);
  1738         s.cullMask = CULLMASK_ALL;
  1739         
  1740         m3gClearLights2(&ctx->lightManager);
  1741         
  1742         ctx->renderQueue->root = (Node *)world;
  1743         ctx->renderQueue->scope = camera->node.scope;
  1744         ctx->renderQueue->lightManager = &ctx->lightManager;
  1745         ctx->renderQueue->camera = camera;
  1746 
  1747         M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
  1748         
  1749         setup = M3G_VFUNC(Node, camera, setupRender)((Node *) camera,
  1750                                                      NULL,
  1751                                                      &s,
  1752                                                      ctx->renderQueue);
  1753         M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
  1754         M3G_LOG(M3G_LOG_RENDERING, "Rendering: commit\n");
  1755         M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
  1756 
  1757         if (setup) {
  1758             m3gInitRender(ctx, RENDER_WORLD);
  1759             m3gLockFrameBuffer(ctx);
  1760             m3gCommit(ctx->renderQueue, ctx);
  1761             m3gReleaseFrameBuffer(ctx);
  1762         }
  1763         
  1764         M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
  1765 
  1766         /* Fix light and camera transformations to be relative to world
  1767          * space on exit */
  1768     
  1769         if (setup) {
  1770             Matrix m;
  1771             if (m3gGetTransformTo((Node*) world, (Node*) camera, &m)) {
  1772                 m3gGetMatrixColumns(&m, ctx->viewTransform);
  1773                 if (m3gInvertMatrix(&m)) {
  1774                     m3gTransformLights(&ctx->lightManager, &m);
  1775                 }
  1776                 else {
  1777                     M3G_ASSERT(M3G_FALSE);
  1778                 }
  1779             }
  1780             else {
  1781                 M3G_ASSERT(M3G_FALSE);
  1782             }
  1783         }
  1784     }
  1785     
  1786 	m3gClearRenderQueue(ctx->renderQueue);
  1787     M3G_LOG(M3G_LOG_RENDERING, "Rendering: end\n");
  1788 }
  1789 
  1790 /*!
  1791  * \brief Renders a node or subtree
  1792  */
  1793 M3G_API void m3gRenderNode(M3GRenderContext context,
  1794                            M3GNode hNode,
  1795                            const M3GMatrix *transform)
  1796 {
  1797     RenderContext *ctx = (RenderContext*) context;
  1798     Node *node = (Node *) hNode;
  1799 
  1800     M3G_LOG1(M3G_LOG_STAGES, "Rendering Node 0x%08X\n", (unsigned) node);
  1801     
  1802     M3G_VALIDATE_OBJECT(ctx);
  1803     M3G_VALIDATE_OBJECT(node);
  1804 
  1805     /* Check for errors */
  1806     
  1807     if (node == NULL) {
  1808         m3gRaiseError(M3G_INTERFACE(ctx), M3G_NULL_POINTER);
  1809         return;
  1810     }
  1811 
  1812     if (ctx->target.type == SURFACE_NONE || ctx->camera == NULL) {
  1813         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
  1814         return;
  1815     }
  1816 
  1817     /* Exit if the camera will show nothing (zero view volume) */
  1818     
  1819     if (!m3gValidProjection(ctx->camera)) {
  1820         return;
  1821     }
  1822 
  1823     /* All clear, draw away */
  1824 
  1825     M3G_LOG(M3G_LOG_RENDERING, "Rendering: start\n");
  1826 	M3G_ASSERT(ctx->renderQueue->root == NULL);
  1827     M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
  1828     
  1829     if (m3gValidateNode(node, NODE_RENDER_BIT, ctx->camera->node.scope)) {
  1830         M3Gbool setup;
  1831         SetupRenderState s;
  1832         M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
  1833         
  1834         s.cullMask = CULLMASK_ALL;
  1835 
  1836         /* We start the traversal from world space, so preload the
  1837          * current camera-space transformation to get camera-space
  1838          * meshes and correct view frustum culling */
  1839         
  1840         m3gSetMatrixColumns(&s.toCamera, ctx->viewTransform);
  1841         if (transform) {
  1842             m3gMulMatrix(&s.toCamera, transform);
  1843         }
  1844         ctx->renderQueue->root = (Node *) node;
  1845         ctx->renderQueue->scope = ctx->camera->node.scope;
  1846         ctx->renderQueue->lightManager = NULL;
  1847         ctx->renderQueue->camera = ctx->camera;
  1848         
  1849         M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
  1850         
  1851         setup = M3G_VFUNC(Node, node, setupRender)(node,
  1852                                                    NULL,
  1853                                                    &s,
  1854                                                    ctx->renderQueue);
  1855         M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
  1856         M3G_LOG(M3G_LOG_RENDERING, "Rendering: commit\n");
  1857         M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
  1858         
  1859         if (setup) {
  1860             m3gInitRender(ctx, RENDER_NODES);
  1861             m3gLockFrameBuffer(ctx);
  1862     		m3gCommit(ctx->renderQueue, ctx);
  1863             m3gReleaseFrameBuffer(ctx);
  1864         }
  1865         
  1866         M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
  1867 	}
  1868 
  1869 	m3gClearRenderQueue(ctx->renderQueue);
  1870     
  1871     M3G_LOG(M3G_LOG_RENDERING, "Rendering: end\n");
  1872 }
  1873 
  1874 /*!
  1875  * \brief Render a set of primitives
  1876  * 
  1877  */
  1878 M3G_API void m3gRender(M3GRenderContext context,
  1879                        M3GVertexBuffer hVertices,
  1880                        M3GIndexBuffer hIndices,
  1881                        M3GAppearance hAppearance,
  1882                        const M3GMatrix *transformMatrix,
  1883                        M3Gfloat alphaFactor,
  1884                        M3Gint scope)
  1885 {
  1886     RenderContext *ctx = (RenderContext *) context;
  1887     const VertexBuffer *vb = (const VertexBuffer *) hVertices;
  1888     const IndexBuffer *ib = (const IndexBuffer *) hIndices;
  1889     const Appearance *app = (const Appearance *) hAppearance;
  1890     M3G_VALIDATE_OBJECT(ctx);
  1891 
  1892     M3G_LOG1(M3G_LOG_STAGES, "Rendering vertex buffer 0x%08X\n",
  1893              (unsigned) vb);
  1894     
  1895     /* Check validity of input */
  1896     
  1897     if (ctx->target.type == SURFACE_NONE || ctx->camera == NULL) {
  1898         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
  1899         return;
  1900     }
  1901     
  1902     /* Quick exit if out of scope or zero view volume */
  1903 
  1904     if ((scope & ctx->camera->node.scope) == 0
  1905         || !m3gValidProjection(ctx->camera)) {
  1906         return;
  1907     }
  1908 
  1909     if (vb == NULL || ib == NULL || app == NULL) {
  1910         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT);
  1911         return;
  1912     }
  1913 
  1914     if (!m3gValidateVertexBuffer(vb, app, m3gGetMaxIndex(ib))) {
  1915         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
  1916         return;
  1917     }
  1918 
  1919     /* Everything checks out, so draw */
  1920 
  1921     M3G_LOG(M3G_LOG_RENDERING, "Rendering: start immediate\n");
  1922     
  1923     m3gInitRender(ctx, RENDER_IMMEDIATE);
  1924     m3gLockFrameBuffer(ctx);
  1925     m3gDrawMesh(ctx,
  1926                 vb, ib, app,
  1927                 transformMatrix,
  1928                 m3gRoundToInt(
  1929                     m3gMul(alphaFactor,
  1930                            (M3Gfloat)(1 << NODE_ALPHA_FACTOR_BITS))),
  1931                 scope);
  1932     m3gReleaseFrameBuffer(ctx);
  1933     
  1934     M3G_LOG(M3G_LOG_RENDERING, "Rendering: end immediate\n");
  1935 }
  1936