First public contribution.
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
4 * This component and the accompanying materials are made available
5 * under the terms of the License "Eclipse Public License v1.0"
6 * which accompanies this distribution, and is available
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
14 * Description: Rendering context function implementations
22 * \brief Rendering context function implementations
25 #ifndef M3G_CORE_INCLUDE
26 # error included by m3g_core.c; do not compile separately.
29 #include "m3g_rendercontext.h"
30 #include "m3g_object.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"
40 /*----------------------------------------------------------------------
42 *--------------------------------------------------------------------*/
44 #if defined(M3G_NGL_CONTEXT_API)
47 * \brief Depth buffer data
54 #endif /*M3G_NGL_CONTEXT_API*/
56 #if !defined(M3G_NGL_CONTEXT_API)
57 /*! \internal \brief OpenGL rendering context record */
60 M3GPixelFormat format;
61 M3Gbitmask bufferBits;
62 M3Gbitmask surfaceTypeBits;
67 /*! \internal \brief OpenGL surface record */
70 M3Gbitmask bufferBits;
79 #endif /*!M3G_NGL_CONTEXT_API*/
82 * \internal \brief Rendering target data
87 M3GPixelFormat format;
90 /*@shared@*/ void *pixels, *lockedPixels;
97 * \brief Flag set to indicate back buffer rendering
99 * The final target is only written to, via a format
100 * conversion, when releasing the target.
107 * \brief Back color buffer data
110 # if defined(M3G_NGL_CONTEXT_API)
114 M3Gint width, height;
115 EGLSurface glSurface;
116 # endif /* M3G_NGL_CONTEXT_API */
117 M3Gbool contentsValid;
122 * \brief Rendering context data structure
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.
128 struct M3GRenderContextImpl
133 BackBuffer backBuffer;
134 # if defined(M3G_NGL_CONTEXT_API)
135 DepthBuffer depthBuffer;
138 # if !defined(M3G_NGL_CONTEXT_API)
140 /* OpenGL context and surface caches */
142 GLContextRecord glContext[M3G_MAX_GL_CONTEXTS];
143 GLSurfaceRecord glSurface[M3G_MAX_GL_SURFACES];
144 M3Guint cacheTimeStamp;
146 # endif /* M3G_NGL_CONTEXT_API */
148 /*! \internal \brief Current/last rendering mode */
151 /*! \internal \brief OpenGL viewing transformation */
152 GLfloat viewTransform[16];
154 /*! \internal \brief Current camera */
155 const Camera *camera;
157 /*! \internal \brief Light manager component */
158 LightManager lightManager;
160 /*! \internal \brief Last used scope, to speed up light selection */
166 /*! \internal \brief Clipping rectangle parameters */
167 struct { M3Gint x0, y0, x1, y1; } clip;
169 /*! \internal \brief Scissor and viewport rectangles */
170 struct { GLint x, y, width, height; } scissor, viewport;
172 /*! \internal \brief Physical display size */
173 struct { M3Gint width, height; } display;
175 M3Gbitmask bufferBits; /*!< \brief Rendering buffer bits */
176 M3Gbitmask modeBits; /*!< \brief Rendering mode bits */
178 /*! \internal \brief OpenGL subsystem initialization flag */
179 M3Gbool glInitialized;
181 /*! \internal \brief HW acceleration status flag */
184 /*! \internal \brief Render queue for this context */
185 RenderQueue *renderQueue;
187 M3Gbool currentColorWrite;
188 M3Gbool currentAlphaWrite;
194 * Rendering target types; note that the values here MUST match the
195 * respective EGL bit values
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,
212 /*----------------------------------------------------------------------
213 * Platform specific code
214 *--------------------------------------------------------------------*/
216 static M3Gbool m3gBindRenderTarget(RenderContext *ctx,
218 M3Gint width, M3Gint height,
219 M3GPixelFormat format,
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);
227 #include "m3g_rendercontext.inl"
229 /*----------------------------------------------------------------------
231 *--------------------------------------------------------------------*/
235 * \brief Rendering context destructor
238 static void m3gDestroyContext(/*@only@*/ Object *obj)
240 RenderContext *ctx = (RenderContext *) obj;
241 M3G_VALIDATE_OBJECT(ctx);
243 M3G_ASSIGN_REF(ctx->camera, NULL);
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);
250 m3gFreeObject(M3G_INTERFACE(ctx), ctx->depthBuffer.handle);
251 m3gFreeObject(M3G_INTERFACE(ctx), ctx->backBuffer.handle);
253 # else /* !M3G_NGL_CONTEXT_API */
257 for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
258 if (ctx->glContext[i].handle != 0) {
259 m3gDeleteGLContext(ctx->glContext[i].handle);
262 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
263 if (ctx->glSurface[i].handle != 0) {
264 m3gDeleteGLSurface(ctx->glSurface[i].handle);
269 # endif /* M3G_NGL_CONTEXT_API */
271 if (ctx->glInitialized) {
272 m3gShutdownGL(M3G_INTERFACE(ctx));
275 m3gDestroyLightManager(&ctx->lightManager, M3G_INTERFACE(ctx));
276 m3gDestroyRenderQueue(M3G_INTERFACE(ctx), ctx->renderQueue);
277 m3gDestroyObject(obj);
282 * \brief Resets the clipping and viewport rectangles to defaults
284 * This is called after binding a new target.
286 static void m3gResetRectangles(RenderContext *ctx)
288 int w = ctx->display.width;
289 int h = ctx->display.height;
292 ctx->clip.y0 = ctx->target.height - ctx->display.height;
294 ctx->clip.y1 = ctx->clip.y0 + h;
298 ctx->viewport.width = M3G_MIN(w, M3G_MAX_VIEWPORT_DIMENSION);
299 ctx->viewport.height = M3G_MIN(h, M3G_MAX_VIEWPORT_DIMENSION);
304 * \brief Constrains the clip rectangle to the rendering target.
306 static void m3gValidateClipRect(RenderContext *ctx)
309 int xMax = ctx->display.width;
310 int yMin = ctx->target.height - ctx->display.height;
311 int yMax = yMin + ctx->display.height;
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);
321 * \brief Computes the GL scissor rectangle
323 * The scissor rectangle is the intersection of the viewport and the
324 * clipping rectangle.
326 static void m3gUpdateScissor(RenderContext *ctx)
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;
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);
338 ctx->scissor.x = sx0;
339 ctx->scissor.y = sy0;
341 if (sx0 < sx1 && sy0 < sy1) {
342 ctx->scissor.width = sx1 - sx0;
343 ctx->scissor.height = sy1 - sy0;
346 ctx->scissor.width = ctx->scissor.height = 0;
352 * \brief Checks whether we can render in a given format
354 static M3Gbool m3gValidTargetFormat(M3GPixelFormat format)
356 return m3gInRange(format, M3G_RGB8, M3G_RGBA4);
361 * \brief Checks whether a given format has alpha
363 static M3Gbool m3gFormatHasAlpha(M3GPixelFormat format)
373 case M3G_PALETTE8_RGBA8:
382 * \brief Sets the global alpha write enable flag.
384 * Used for disabling the alpha channel writes when the rendering
385 * target is a Java MIDP Image that has an alpha channel.
387 * \param ctx the rendering context
388 * \param enable alpha write enable flag
390 M3G_API void m3gSetAlphaWrite(M3GRenderContext ctx, M3Gbool enable)
392 ctx->alphaWrite = enable;
397 * \brief Reads the global alpha write enable flag.
399 * \param ctx the rendering context
401 M3G_API M3Gbool m3gGetAlphaWrite(M3GRenderContext ctx)
403 return ctx->alphaWrite;
407 * \brief Frees all GLES resources allocated by the M3G API
408 * (EGL surfaces, contexts and texture objects).
410 * \note M3G must not be bound to any target when calling this.
413 M3G_API void m3gFreeGLESResources(M3GRenderContext ctx)
415 #ifdef M3G_ENABLE_GLES_RESOURCE_HANDLING
417 PointerArray image2DObjects;
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);
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);
430 /* Delete EGL surfaces */
431 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
432 GLSurfaceRecord *surf = &ctx->glSurface[i];
434 m3gDeleteGLSurface(surf->handle);
436 m3gZero(surf, sizeof(GLSurfaceRecord));
438 if (ctx->backBuffer.glSurface != NULL) {
439 m3gDeleteGLSurface(ctx->backBuffer.glSurface);
440 m3gZero(&ctx->backBuffer, sizeof(BackBuffer));
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);
449 m3gZero(context, sizeof(GLContextRecord));
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. */
455 m3gInitArray(&image2DObjects);
456 m3gGetObjectsWithClassID(M3G_INTERFACE(ctx), M3G_CLASS_IMAGE, &image2DObjects);
458 i = m3gArraySize(&image2DObjects);
461 Image *image = (Image*)m3gGetArrayElement(&image2DObjects, --i);
463 m3gInvalidateImage(image);
464 image->texObject = 0;
466 m3gDestroyArray(&image2DObjects, M3G_INTERFACE(ctx));
473 * \brief Sets up a new rendering target
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
482 static M3Gbool m3gBindRenderTarget(RenderContext *ctx,
484 M3Gint width, M3Gint height,
485 M3GPixelFormat format,
488 /* Check for generic errors */
490 if (ctx->target.type != SURFACE_NONE) {
491 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
494 if (!m3gValidTargetFormat(format)) {
495 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
499 /* If target width or height exceeds maximum viewport width or height
500 an exception is thrown. */
502 if (width > M3G_MAX_VIEWPORT_WIDTH ||
503 height > M3G_MAX_VIEWPORT_HEIGHT) {
504 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
508 /* Everything checks out; set up the target parameters */
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);
521 /* Invalidate lights in case we're using a different OpenGL
522 * rendering context this time around */
531 * \brief Initializes the current GL context to default settings.
533 static void m3gSetGLDefaults(void)
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);
544 * \brief Validates the buffers required for a rendering context
546 * Allocates or reallocates buffers as necessary, according to the
547 * currently set flags of the context.
549 static void m3gValidateBuffers(RenderContext *ctx)
551 M3G_VALIDATE_OBJECT(ctx);
553 /* Initialize OpenGL if not already done */
555 if (!ctx->glInitialized) {
556 m3gInitializeGL(M3G_INTERFACE(ctx));
557 ctx->glInitialized = M3G_TRUE;
560 /* Check whether we can render directly to the target or need to
561 * use a back buffer */
563 ctx->target.buffered = !m3gCanDirectRender(ctx);
564 # if defined(M3G_FORCE_BUFFERED_RENDERING)
565 ctx->target.buffered = M3G_TRUE;
568 /* If direct rendering wasn't possible, check that the back buffer
569 * for buffered rendering exists. */
571 if (ctx->target.buffered) {
572 if (!m3gValidateBackBuffer(ctx)) {
573 return; /* out of memory */
577 /* With the legacy NGL API, we also manage the depth buffer */
579 # if defined(M3G_NGL_CONTEXT_API)
580 if (!m3gValidateDepthBuffer(ctx)) {
581 return; /* out of memory */
585 /* Delay blitting from the front buffer until we know it's
586 * necessary; let's raise a flag to check that later on */
588 if (ctx->target.buffered) {
589 if (ctx->modeBits & M3G_OVERWRITE_BIT) {
590 ctx->backBuffer.contentsValid = M3G_TRUE;
593 ctx->backBuffer.contentsValid = M3G_FALSE;
600 * \brief Makes a GL context current to this thread and the currently
601 * set rendering target buffer
603 static void m3gMakeCurrent(RenderContext *ctx)
605 m3gMakeGLCurrent(ctx);
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
611 if ((ctx->bufferBits & M3G_DEPTH_BUFFER_BIT) == 0) {
612 glDisable(GL_DEPTH_TEST);
615 glEnable(GL_DEPTH_TEST);
618 /* Enable multisampling if required */
620 if (ctx->modeBits & M3G_ANTIALIAS_BIT) {
621 glEnable(GL_MULTISAMPLE);
624 glDisable(GL_MULTISAMPLE);
632 * \brief Returns the HW acceleration status of the current context
634 static M3Gbool m3gIsAccelerated(const RenderContext *ctx)
636 return ctx->accelerated;
641 * \brief Sets the currently enabled lights to the GL state
643 * \note the correct viewing matrix *must* be set prior to calling
644 * this for the lights to be transformed into eye space correctly
646 static M3G_INLINE void m3gApplyLights(RenderContext *ctx, M3Gint scope)
648 if (ctx->lastScope != scope) {
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 */
654 if (ctx->renderMode == RENDER_NODES) {
656 glLoadMatrixf(ctx->viewTransform);
659 m3gSelectGLLights(&ctx->lightManager, 8, scope, 0, 0, 0);
660 ctx->lastScope = scope;
662 if (ctx->renderMode == RENDER_NODES) {
671 * \brief Gets the current camera
673 static const Camera *m3gGetCurrentCamera(const RenderContext *ctx) {
679 * \brief Sets up some rendering parameters that
680 * do not change during scene renders.
682 static void m3gInitRender(M3GRenderContext context, M3Genum renderMode)
684 RenderContext *ctx = (RenderContext *) context;
685 M3G_VALIDATE_OBJECT(ctx);
687 m3gIncrementRenderTimeStamp(ctx);
689 m3gCollectGLObjects(M3G_INTERFACE(ctx));
691 /* If buffered rendering, blit the image to the back buffer at
694 if (ctx->target.buffered && !ctx->backBuffer.contentsValid) {
695 m3gUpdateBackBuffer(ctx);
698 /* Set up viewport and scissoring */
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);
707 /* Set up the projection and viewing transformations (static
708 * during rendering) */
710 m3gApplyProjection(ctx->camera);
711 if (renderMode == RENDER_NODES) {
715 glLoadMatrixf(ctx->viewTransform);
719 /* Invalidate any already set GL lights if rendering mode changed */
721 if (renderMode != ctx->renderMode) {
724 ctx->renderMode = renderMode;
729 * \brief A workaround for a broken implementation of glColorMask
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
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
740 static void m3gUpdateColorMaskStatus(RenderContext *ctx,
741 M3Gbool newColorWrite,
742 M3Gbool newAlphaWrite)
744 GLint pow2Width, pow2Height;
746 /* Get the global alpha write value */
747 newAlphaWrite &= m3gGetAlphaWrite(ctx);
749 /* Check that the ColorMask state is actually about to change */
751 if (ctx->currentColorWrite == newColorWrite
752 && (ctx->currentAlphaWrite == newAlphaWrite || !m3gFormatHasAlpha(ctx->target.format))) {
753 return; /* no change, quick exit */
756 pow2Width = m3gNextPowerOfTwo(ctx->clip.x1 - ctx->clip.x0);
757 pow2Height = m3gNextPowerOfTwo(ctx->clip.y1 - ctx->clip.y0);
759 /* If we previously had stored something, restore it now */
761 if (ctx->currentColorWrite != ctx->currentAlphaWrite) {
763 /* Disable any stray state we don't want */
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();
776 /* Bind the default texture and set up screen space rendering */
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);
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,
792 glMatrixMode(GL_MODELVIEW);
794 /* Set up texture and vertex coordinate arrays */
796 glClientActiveTexture(GL_TEXTURE0);
797 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
798 glEnableClientState(GL_VERTEX_ARRAY);
799 glMatrixMode(GL_TEXTURE);
801 glMatrixMode(GL_MODELVIEW);
804 /* Blend the texture with the frame buffer */
806 static const M3Gbyte tc[8] = { 0, 0, 0, 1, 1, 0, 1, 1 };
809 GLfixed cm = (GLfixed)(ctx->currentColorWrite ? 0 : 1 << 16);
810 GLfixed am = (GLfixed)(ctx->currentAlphaWrite ? 0 : 1 << 16);
812 glVertexPointer(2, GL_SHORT, 0, pos);
813 glTexCoordPointer(2, GL_BYTE, 0, tc);
815 pos[0] = (GLshort) ctx->clip.x0;
816 pos[1] = (GLshort) ctx->clip.y0;
818 pos[3] = (GLshort) (pos[1] + pow2Height);
819 pos[4] = (GLshort) (pos[0] + pow2Width);
825 glColor4x(cm, cm, cm, am);
827 /* Zero the masked channels */
829 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
830 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
832 /* Add the masked channels from the stored texture */
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);
840 /* Restore the mandatory state */
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);
849 /* Copy the current clip rectangle into the default texture if
850 * we're going to be rendering with unsupported masks in effect */
852 if (newColorWrite != newAlphaWrite) {
855 glBindTexture(GL_TEXTURE_2D, 0);
858 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
859 ctx->clip.x0, ctx->clip.y0,
860 pow2Width, pow2Height,
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,
871 if (err == GL_OUT_OF_MEMORY) {
872 m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
878 /* Texture not needed for now, so allow GL to free some
881 glTexImage2D(GL_TEXTURE_2D, 0,
885 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
888 ctx->currentColorWrite = newColorWrite;
889 ctx->currentAlphaWrite = newAlphaWrite;
894 * \brief Sets the GL to input screen space coordinates
896 * This pushes the current modelview and projection matrices into the
897 * matrix stack, then sets up an orthogonal projection and an identity
900 * \param ctx the rendering context
901 * \param realPixels M3G_TRUE to use actual pixel coordinates,
902 * M3G_FALSE to use normalized device coordinates
904 static void m3gPushScreenSpace(RenderContext *ctx, M3Gbool realPixels)
906 M3G_VALIDATE_OBJECT(ctx);
908 glMatrixMode(GL_PROJECTION);
912 int w = ctx->viewport.width;
913 int h = ctx->viewport.height;
914 glOrthox(0, w << 16, 0, h << 16, -1 << 16, 1 << 16);
916 glMatrixMode(GL_MODELVIEW);
923 * \brief Restores the projection and modelview matrix modified by
926 static void m3gPopSpace(RenderContext *ctx)
928 M3G_VALIDATE_OBJECT(ctx);
931 glMatrixMode(GL_PROJECTION);
933 glMatrixMode(GL_MODELVIEW);
939 * \brief Clears the current buffer(s)
941 static void m3gClearInternal(RenderContext *ctx, Background *bg)
945 /* If buffered rendering, copy data to the back buffer at this
946 * point if we're not clearing the whole clip rectangle */
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);
956 if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
957 m3gUpdateColorMaskStatus(ctx, M3G_TRUE, M3G_TRUE);
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);
968 /* Touch the background image to make sure it's created prior to
969 * locking memory for rendering */
971 if (bg != NULL && bg->image != NULL) {
972 if (!m3gGetPowerOfTwoImage(bg->image)) {
973 return; /* out of memory */
977 /* All clear for clearing... */
979 m3gLockFrameBuffer(ctx);
982 m3gApplyBackground(ctx, bg);
983 if (ctx->target.buffered && bg->colorClearEnable) {
984 ctx->backBuffer.contentsValid = M3G_TRUE;
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;
996 m3gReleaseFrameBuffer(ctx);
1001 * \brief Draws a batch of primitives
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.
1007 static void m3gDrawMesh(RenderContext *ctx,
1008 const VertexBuffer *vb,
1009 const IndexBuffer *ib,
1010 const Appearance *app,
1011 const M3GMatrix *modelTransform,
1015 M3G_VALIDATE_OBJECT(ctx);
1016 M3G_VALIDATE_OBJECT(vb);
1017 M3G_VALIDATE_OBJECT(ib);
1018 M3G_VALIDATE_OBJECT(app);
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)) {
1028 M3G_ASSERT(m3gInRange(alphaFactor, 0, 0x10000));
1030 if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
1031 m3gUpdateColorMaskStatus(ctx, m3gColorMask(app), m3gAlphaMask(app));
1036 m3gApplyLights(ctx, scope);
1038 /* Apply the extra modeling transformation if present */
1040 if (modelTransform != NULL) {
1041 float transform[16];
1042 m3gGetMatrixColumns(modelTransform, transform);
1045 glMultMatrixf(transform);
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 */
1054 if (alphaFactor < 0x10000 && !m3gValidateAlphaCache(vb)) {
1056 m3gReleaseFrameBuffer(ctx);
1057 ok = m3gCreateAlphaColorCache(vb->colors);
1058 m3gLockFrameBuffer(ctx);
1060 goto RestoreModelview; /* let's just skip the drawing part */
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 */
1069 for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
1070 Texture *tex = app->texture[i];
1071 if (tex && !m3gValidateTextureMipmapping(tex)) {
1073 m3gReleaseFrameBuffer(ctx);
1074 ok = m3gValidateMipmapMemory(m3gGetTextureImage(tex));
1075 m3gLockFrameBuffer(ctx);
1077 goto RestoreModelview;
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 */
1088 m3gApplyAppearance(app, ctx, alphaFactor);
1089 m3gLockVertexBuffer(vb, alphaFactor);
1090 m3gApplyScaleAndBias(vb);
1092 /* All ready, render and then release the stuff we bound above */
1094 m3gSendIndexBuffer(ib);
1095 m3gReleaseVertexBuffer(vb);
1096 m3gReleaseTextures(app);
1098 /* Restore viewing-only modelview if changed */
1101 if (modelTransform != NULL) {
1108 * \brief Validates background format against current target
1110 * \retval M3G_TRUE valid format
1111 * \retval M3G_FALSE invalid format
1113 static M3Gbool m3gValidateBackground(RenderContext *ctx, Background *bg)
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);
1125 return (m3gGetFormat(bg->image) == M3G_RGB);
1132 /*----------------------------------------------------------------------
1133 * Virtual function table
1134 *--------------------------------------------------------------------*/
1136 static const ObjectVFTable m3gvf_RenderContext = {
1137 NULL, /* ApplyAnimation */
1138 NULL, /* IsCompatible */
1139 NULL, /* UpdateProperty */
1140 NULL, /* GetReference */
1142 NULL, /* CreateClone */
1147 /*----------------------------------------------------------------------
1149 *--------------------------------------------------------------------*/
1152 * \brief Creates and initializes a new rendering context
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
1160 /*@access M3GInterface@*/
1161 /*@access M3GRenderContext@*/
1163 M3G_API M3GRenderContext m3gCreateContext(M3GInterface interface)/*@*/
1165 Interface *m3g = (Interface*) interface;
1166 M3G_VALIDATE_INTERFACE(m3g);
1169 RenderContext *ctx =
1170 (RenderContext*) m3gAllocZ(m3g, (int) sizeof(RenderContext));
1172 return NULL; /* m3gAlloc automatically raises out-of-mem */
1175 ctx->renderQueue = m3gCreateRenderQueue(m3g);
1176 if (ctx->renderQueue == NULL) {
1180 ctx->bufferBits = M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT;
1181 ctx->depthNear = 0.0f;
1182 ctx->depthFar = 1.0f;
1184 m3gInitObject(&ctx->object, m3g, M3G_CLASS_RENDER_CONTEXT);
1186 m3gSetAlphaWrite(ctx, M3G_TRUE);
1188 if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
1189 ctx->currentColorWrite = M3G_TRUE;
1190 ctx->currentAlphaWrite = m3gGetAlphaWrite(ctx);
1193 return (M3GRenderContext)ctx;
1198 * \brief Sets the buffers to use for subsequent rendering
1200 M3G_API M3Gbool m3gSetRenderBuffers(M3GRenderContext hCtx,
1201 M3Gbitmask bufferBits)
1203 RenderContext *ctx = (RenderContext *) hCtx;
1204 M3G_VALIDATE_OBJECT(ctx);
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);
1210 ctx->bufferBits = bufferBits;
1215 * \brief Sets the rendering quality hints to use for subsequent
1218 * \note This may not take effect before the target is released and
1221 M3G_API M3Gbool m3gSetRenderHints(M3GRenderContext hCtx, M3Gbitmask modeBits)
1223 RenderContext *ctx = (RenderContext *) hCtx;
1224 M3G_VALIDATE_OBJECT(ctx);
1226 if ((modeBits & ~(M3G_OVERWRITE_BIT|M3G_ANTIALIAS_BIT|M3G_DITHER_BIT|M3G_TRUECOLOR_BIT)) != 0) {
1227 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1231 /* Disable features not supported in the current configuration */
1233 if (M3G_SUPPORT_ANTIALIASING == M3G_FALSE ||
1234 !m3gIsAntialiasingSupported(M3G_INTERFACE(ctx))) {
1235 modeBits &= ~M3G_ANTIALIAS_BIT;
1237 if (M3G_SUPPORT_DITHERING == M3G_FALSE) {
1238 modeBits &= ~M3G_DITHER_BIT;
1240 if (M3G_SUPPORT_TRUE_COLOR == M3G_FALSE) {
1241 modeBits &= ~M3G_TRUECOLOR_BIT;
1244 ctx->modeBits = modeBits;
1248 M3G_API void m3gBindImageTarget(M3GRenderContext hCtx, M3GImage hImage)
1250 RenderContext *ctx = (RenderContext *) hCtx;
1251 Image *img = (Image *) hImage;
1252 M3G_VALIDATE_OBJECT(ctx);
1253 M3G_VALIDATE_OBJECT(img);
1255 M3G_LOG1(M3G_LOG_RENDERING, "Binding image target 0x%08X\n",
1258 /* Check for image-specific errors */
1260 if ((img->flags & M3G_DYNAMIC) == 0
1261 || !m3gValidTargetFormat(img->internalFormat)) {
1263 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
1267 /* Do the generic checking and set-up */
1269 if (!m3gBindRenderTarget(ctx,
1271 img->width, img->height,
1272 img->internalFormat,
1273 (M3Guint) hImage)) {
1274 return; /* appropriate error raised automatically */
1277 /* Set up image-specific parameters */
1279 # if defined(M3G_NGL_CONTEXT_API)
1280 ctx->target.stride = m3gGetImageStride(img);
1281 ctx->target.pixels = NULL;
1284 m3gAddRef((Object*) img);
1289 M3G_API M3Guint m3gGetUserHandle(M3GRenderContext hCtx)
1291 RenderContext *ctx = (RenderContext *) hCtx;
1292 M3G_VALIDATE_OBJECT(ctx);
1294 if (ctx->target.type == SURFACE_MEMORY) {
1295 return ctx->target.handle;
1302 M3G_API void m3gSetUserData(M3GRenderContext hCtx, M3Guint hData)
1304 RenderContext *ctx = (RenderContext *) hCtx;
1305 M3G_VALIDATE_OBJECT(ctx);
1306 ctx->target.userData = hData;
1311 M3G_API M3Guint m3gGetUserData(M3GRenderContext hCtx)
1313 RenderContext *ctx = (RenderContext *) hCtx;
1314 M3G_VALIDATE_OBJECT(ctx);
1315 return ctx->target.userData;
1319 * \brief Clears the current buffer(s)
1321 M3G_API void m3gClear(M3GRenderContext context, M3GBackground hBackground)
1323 RenderContext *ctx = (RenderContext*) context;
1324 Background *bg = (Background *) hBackground;
1325 M3G_VALIDATE_OBJECT(ctx);
1327 M3G_LOG(M3G_LOG_STAGES, "Clearing frame buffer\n");
1331 if (ctx->target.type == SURFACE_NONE) {
1332 m3gRaiseError(M3G_INTERFACE(context), M3G_INVALID_OPERATION);
1336 if(m3gValidateBackground(ctx, bg)) {
1337 m3gClearInternal(ctx, bg);
1340 m3gRaiseError(M3G_INTERFACE(bg), M3G_INVALID_VALUE);
1345 * \brief Release the currently bound color buffer
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.
1351 M3G_API void m3gReleaseTarget(M3GRenderContext context)
1353 RenderContext *ctx = (RenderContext*) context;
1354 M3G_VALIDATE_OBJECT(ctx);
1356 M3G_LOG(M3G_LOG_RENDERING, "Releasing target\n");
1358 if (ctx->target.type == SURFACE_NONE) {
1362 m3gMakeCurrent(ctx);
1364 if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
1365 m3gUpdateColorMaskStatus(ctx, M3G_TRUE, M3G_TRUE);
1370 /* Update the real target if we rendered into the back buffer */
1372 if (ctx->target.buffered) {
1373 m3gUpdateTargetBuffer(ctx);
1376 /* Invalidate Image targets so that mipmap levels and/or OpenGL
1377 * texture objects are updated accordingly */
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);
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
1390 # if !defined(M3G_NGL_CONTEXT_API)
1391 if (ctx->target.type == SURFACE_WINDOW) {
1392 m3gSwapBuffers(ctx->target.surface);
1395 m3gCollectGLObjects(M3G_INTERFACE(ctx));
1396 # if !defined(M3G_NGL_CONTEXT_API)
1397 m3gMakeGLCurrent(NULL);
1398 ctx->target.surface = NULL;
1400 if (ctx->target.type == SURFACE_MEMORY && ctx->target.pixels == NULL) {
1401 m3gSignalTargetRelease(M3G_INTERFACE(ctx), ctx->target.handle);
1405 ctx->target.type = SURFACE_NONE;
1406 ctx->renderQueue->root = NULL;
1408 # if (M3G_PROFILE_LOG_INTERVAL > 0)
1409 m3gLogProfileCounters(M3G_INTERFACE(ctx));
1414 * \brief Sets a camera for this context
1416 M3G_API void m3gSetCamera(M3GRenderContext context,
1418 M3GMatrix *transform)
1421 RenderContext *ctx = (RenderContext*) context;
1422 const Camera *camera = (Camera *)hCamera;
1424 M3G_VALIDATE_OBJECT(ctx);
1426 M3G_ASSIGN_REF(ctx->camera, camera);
1428 if (transform != NULL) {
1429 if (!m3gMatrixInverse(&m, transform)) {
1430 m3gRaiseError(M3G_INTERFACE(ctx), M3G_ARITHMETIC_ERROR);
1435 m3gIdentityMatrix(&m);
1438 m3gGetMatrixColumns(&m, ctx->viewTransform);
1444 * \brief Adds a light to the light array for this context
1446 M3G_API M3Gint m3gAddLight(M3GRenderContext hCtx,
1448 const M3GMatrix *transform)
1450 RenderContext *ctx = (RenderContext *)hCtx;
1451 Light *light = (Light *)hLight;
1452 M3G_VALIDATE_OBJECT(ctx);
1454 if (light == NULL) {
1455 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1459 LightManager *mgr = &ctx->lightManager;
1460 M3G_VALIDATE_OBJECT(light);
1462 return m3gInsertLight(mgr, light, transform, M3G_INTERFACE(ctx));
1467 * \brief Sets a light for this context
1469 M3G_API void m3gSetLight(M3GRenderContext context,
1472 const M3GMatrix *transform)
1474 RenderContext *ctx = (RenderContext*) context;
1475 Light *light = (Light *)hLight;
1476 M3G_VALIDATE_OBJECT(ctx);
1478 /* Check for invalid arguments */
1479 if (lightIndex < 0 || lightIndex >= m3gLightArraySize(&ctx->lightManager)) {
1480 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_INDEX);
1485 m3gReplaceLight(&ctx->lightManager, lightIndex, light, transform);
1489 * \brief Removes all lights from this context
1491 M3G_API void m3gClearLights(M3GRenderContext context)
1493 RenderContext *ctx = (RenderContext *)context;
1494 M3G_VALIDATE_OBJECT(ctx);
1496 m3gClearLights2(&ctx->lightManager);
1500 * \brief Sets the viewport
1503 M3G_API void m3gSetViewport(M3GRenderContext hCtx,
1505 M3Gint width, M3Gint height)
1507 RenderContext *ctx = (RenderContext *)hCtx;
1508 M3G_VALIDATE_OBJECT(ctx);
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. */
1515 if (width < 0 || height < 0) {
1516 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1520 width = M3G_MIN(width, M3G_MAX_VIEWPORT_DIMENSION);
1521 height = M3G_MIN(height, M3G_MAX_VIEWPORT_DIMENSION);
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);
1532 * \brief Gets the viewport
1534 M3G_API void m3gGetViewport(M3GRenderContext hCtx,
1535 M3Gint *x, M3Gint *y,
1536 M3Gint *width, M3Gint *height)
1538 RenderContext *ctx = (RenderContext *)hCtx;
1539 M3G_VALIDATE_OBJECT(ctx);
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;
1548 * \brief Sets the scissor rectangle
1550 M3G_API void m3gSetClipRect(M3GRenderContext hCtx,
1552 M3Gint width, M3Gint height)
1554 RenderContext *ctx = (RenderContext *)hCtx;
1555 M3G_VALIDATE_OBJECT(ctx);
1557 if (width < 0 || height < 0) {
1558 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
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);
1570 * \brief Sets the physical display area
1572 * The display are is normally set to the full rendering targte size
1573 * in m3gBindTarget, but this function allows overriding the default
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.
1580 M3G_API void m3gSetDisplayArea(M3GRenderContext hCtx,
1581 M3Gint width, M3Gint height)
1583 RenderContext *ctx = (RenderContext*) hCtx;
1584 M3G_VALIDATE_OBJECT(ctx);
1586 ctx->display.width = M3G_MIN(width, ctx->target.width);
1587 ctx->display.height = M3G_MIN(height, ctx->target.height);
1591 * \brief Sets depth range
1594 M3G_API void m3gSetDepthRange(M3GRenderContext hCtx,
1595 M3Gfloat depthNear, M3Gfloat depthFar)
1597 RenderContext *ctx = (RenderContext *)hCtx;
1598 M3G_VALIDATE_OBJECT(ctx);
1600 if (depthNear < 0 || depthNear > 1.0f || depthFar < 0 || depthFar > 1.0f) {
1601 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1605 ctx->depthNear = depthNear;
1606 ctx->depthFar = depthFar;
1610 * \brief Gets depth range
1613 M3G_API void m3gGetDepthRange(M3GRenderContext hCtx,
1614 M3Gfloat *depthNear, M3Gfloat *depthFar)
1616 RenderContext *ctx = (RenderContext *)hCtx;
1617 M3G_VALIDATE_OBJECT(ctx);
1619 *depthNear = ctx->depthNear;
1620 *depthFar= ctx->depthFar;
1624 * \brief Gets current view transform
1628 M3G_API void m3gGetViewTransform(M3GRenderContext hCtx,
1629 M3GMatrix *transform)
1631 RenderContext *ctx = (RenderContext *)hCtx;
1632 M3G_VALIDATE_OBJECT(ctx);
1633 m3gSetMatrixColumns(transform, ctx->viewTransform);
1634 m3gInvertMatrix(transform); /*lint !e534 always invertible */
1638 * \brief Gets current Camera
1642 M3G_API M3GCamera m3gGetCamera(M3GRenderContext hCtx)
1644 RenderContext *ctx = (RenderContext *)hCtx;
1645 M3G_VALIDATE_OBJECT(ctx);
1646 return (M3GCamera) ctx->camera;
1650 * \brief Gets light transform of given light
1654 M3G_API M3GLight m3gGetLightTransform (M3GRenderContext hCtx,
1655 M3Gint lightIndex, M3GMatrix *transform)
1657 RenderContext *ctx = (RenderContext *)hCtx;
1658 M3G_VALIDATE_OBJECT(ctx);
1659 return m3gGetLightTransformInternal(&ctx->lightManager, lightIndex, transform);
1663 * \brief Gets light count
1667 M3G_API M3Gsizei m3gGetLightCount (M3GRenderContext hCtx)
1669 RenderContext *ctx = (RenderContext *)hCtx;
1670 M3G_VALIDATE_OBJECT(ctx);
1671 return m3gLightArraySize(&ctx->lightManager);
1675 * \brief Renders a world
1678 M3G_API void m3gRenderWorld(M3GRenderContext context, M3GWorld hWorld)
1681 RenderContext *ctx = (RenderContext*) context;
1682 World *world = (World *) hWorld;
1684 M3G_LOG1(M3G_LOG_STAGES, "Rendering World 0x%08X\n", (unsigned) world);
1686 M3G_VALIDATE_OBJECT(ctx);
1687 M3G_VALIDATE_OBJECT(world);
1689 camera = m3gGetActiveCamera(world);
1691 /* Check for errors */
1693 if (ctx->target.type == SURFACE_NONE) {
1694 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1698 if (camera == NULL ||
1699 !m3gIsChildOf((Node *)world, (Node *)camera)) {
1700 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1704 /* Exit if the camera will show nothing (zero view volume) */
1706 if (!m3gValidProjection(camera)) {
1710 /* Override the currently set viewing transformation with identity
1711 * (will fix this before we return) */
1713 m3gSetCamera(ctx, camera, NULL);
1715 if (m3gValidateBackground(ctx, world->background)) {
1716 m3gClearInternal(ctx, world->background);
1719 m3gRaiseError(M3G_INTERFACE(world), M3G_INVALID_OPERATION);
1723 /* All clear for rendering */
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);
1729 if (m3gValidateNode((Node*) world, NODE_RENDER_BIT, camera->node.scope)) {
1732 M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
1734 /* We start the traversal from the camera, so set the initial
1735 * camera-space transformation to identity */
1737 m3gIdentityMatrix(&s.toCamera);
1738 s.cullMask = CULLMASK_ALL;
1740 m3gClearLights2(&ctx->lightManager);
1742 ctx->renderQueue->root = (Node *)world;
1743 ctx->renderQueue->scope = camera->node.scope;
1744 ctx->renderQueue->lightManager = &ctx->lightManager;
1745 ctx->renderQueue->camera = camera;
1747 M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
1749 setup = M3G_VFUNC(Node, camera, setupRender)((Node *) camera,
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);
1758 m3gInitRender(ctx, RENDER_WORLD);
1759 m3gLockFrameBuffer(ctx);
1760 m3gCommit(ctx->renderQueue, ctx);
1761 m3gReleaseFrameBuffer(ctx);
1764 M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
1766 /* Fix light and camera transformations to be relative to world
1771 if (m3gGetTransformTo((Node*) world, (Node*) camera, &m)) {
1772 m3gGetMatrixColumns(&m, ctx->viewTransform);
1773 if (m3gInvertMatrix(&m)) {
1774 m3gTransformLights(&ctx->lightManager, &m);
1777 M3G_ASSERT(M3G_FALSE);
1781 M3G_ASSERT(M3G_FALSE);
1786 m3gClearRenderQueue(ctx->renderQueue);
1787 M3G_LOG(M3G_LOG_RENDERING, "Rendering: end\n");
1791 * \brief Renders a node or subtree
1793 M3G_API void m3gRenderNode(M3GRenderContext context,
1795 const M3GMatrix *transform)
1797 RenderContext *ctx = (RenderContext*) context;
1798 Node *node = (Node *) hNode;
1800 M3G_LOG1(M3G_LOG_STAGES, "Rendering Node 0x%08X\n", (unsigned) node);
1802 M3G_VALIDATE_OBJECT(ctx);
1803 M3G_VALIDATE_OBJECT(node);
1805 /* Check for errors */
1808 m3gRaiseError(M3G_INTERFACE(ctx), M3G_NULL_POINTER);
1812 if (ctx->target.type == SURFACE_NONE || ctx->camera == NULL) {
1813 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1817 /* Exit if the camera will show nothing (zero view volume) */
1819 if (!m3gValidProjection(ctx->camera)) {
1823 /* All clear, draw away */
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);
1829 if (m3gValidateNode(node, NODE_RENDER_BIT, ctx->camera->node.scope)) {
1832 M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
1834 s.cullMask = CULLMASK_ALL;
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 */
1840 m3gSetMatrixColumns(&s.toCamera, ctx->viewTransform);
1842 m3gMulMatrix(&s.toCamera, transform);
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;
1849 M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
1851 setup = M3G_VFUNC(Node, node, setupRender)(node,
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);
1860 m3gInitRender(ctx, RENDER_NODES);
1861 m3gLockFrameBuffer(ctx);
1862 m3gCommit(ctx->renderQueue, ctx);
1863 m3gReleaseFrameBuffer(ctx);
1866 M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
1869 m3gClearRenderQueue(ctx->renderQueue);
1871 M3G_LOG(M3G_LOG_RENDERING, "Rendering: end\n");
1875 * \brief Render a set of primitives
1878 M3G_API void m3gRender(M3GRenderContext context,
1879 M3GVertexBuffer hVertices,
1880 M3GIndexBuffer hIndices,
1881 M3GAppearance hAppearance,
1882 const M3GMatrix *transformMatrix,
1883 M3Gfloat alphaFactor,
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);
1892 M3G_LOG1(M3G_LOG_STAGES, "Rendering vertex buffer 0x%08X\n",
1895 /* Check validity of input */
1897 if (ctx->target.type == SURFACE_NONE || ctx->camera == NULL) {
1898 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1902 /* Quick exit if out of scope or zero view volume */
1904 if ((scope & ctx->camera->node.scope) == 0
1905 || !m3gValidProjection(ctx->camera)) {
1909 if (vb == NULL || ib == NULL || app == NULL) {
1910 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT);
1914 if (!m3gValidateVertexBuffer(vb, app, m3gGetMaxIndex(ib))) {
1915 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1919 /* Everything checks out, so draw */
1921 M3G_LOG(M3G_LOG_RENDERING, "Rendering: start immediate\n");
1923 m3gInitRender(ctx, RENDER_IMMEDIATE);
1924 m3gLockFrameBuffer(ctx);
1930 (M3Gfloat)(1 << NODE_ALPHA_FACTOR_BITS))),
1932 m3gReleaseFrameBuffer(ctx);
1934 M3G_LOG(M3G_LOG_RENDERING, "Rendering: end immediate\n");