1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/graphics/m3g/m3gcore11/src/m3g_rendercontext.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1936 @@
1.4 +/*
1.5 +* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
1.6 +* All rights reserved.
1.7 +* This component and the accompanying materials are made available
1.8 +* under the terms of the License "Eclipse Public License v1.0"
1.9 +* which accompanies this distribution, and is available
1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.11 +*
1.12 +* Initial Contributors:
1.13 +* Nokia Corporation - initial contribution.
1.14 +*
1.15 +* Contributors:
1.16 +*
1.17 +* Description: Rendering context function implementations
1.18 +*
1.19 +*/
1.20 +
1.21 +
1.22 +/*!
1.23 + * \internal
1.24 + * \file
1.25 + * \brief Rendering context function implementations
1.26 + */
1.27 +
1.28 +#ifndef M3G_CORE_INCLUDE
1.29 +# error included by m3g_core.c; do not compile separately.
1.30 +#endif
1.31 +
1.32 +#include "m3g_rendercontext.h"
1.33 +#include "m3g_object.h"
1.34 +
1.35 +#include "m3g_gl.h"
1.36 +#include "m3g_memory.h"
1.37 +#include "m3g_appearance.h"
1.38 +#include "m3g_indexbuffer.h"
1.39 +#include "m3g_lightmanager.h"
1.40 +#include "m3g_vertexbuffer.h"
1.41 +#include "m3g_world.h"
1.42 +
1.43 +/*----------------------------------------------------------------------
1.44 + * Private data types
1.45 + *--------------------------------------------------------------------*/
1.46 +
1.47 +#if defined(M3G_NGL_CONTEXT_API)
1.48 +/*!
1.49 + * \internal
1.50 + * \brief Depth buffer data
1.51 + */
1.52 +typedef struct
1.53 +{
1.54 + M3GMemObject handle;
1.55 + M3Gsizei size;
1.56 +} DepthBuffer;
1.57 +#endif /*M3G_NGL_CONTEXT_API*/
1.58 +
1.59 +#if !defined(M3G_NGL_CONTEXT_API)
1.60 +/*! \internal \brief OpenGL rendering context record */
1.61 +typedef struct {
1.62 + EGLContext handle;
1.63 + M3GPixelFormat format;
1.64 + M3Gbitmask bufferBits;
1.65 + M3Gbitmask surfaceTypeBits;
1.66 + M3Gbitmask modeBits;
1.67 + M3Guint lastUseTime;
1.68 +} GLContextRecord;
1.69 +
1.70 +/*! \internal \brief OpenGL surface record */
1.71 +typedef struct {
1.72 + EGLSurface handle;
1.73 + M3Gbitmask bufferBits;
1.74 + M3Gbitmask type;
1.75 + M3Guint width;
1.76 + M3Guint height;
1.77 + M3Guint format;
1.78 + M3Guint targetHandle;
1.79 + void* pixels;
1.80 + M3Guint lastUseTime;
1.81 +} GLSurfaceRecord;
1.82 +#endif /*!M3G_NGL_CONTEXT_API*/
1.83 +
1.84 +/*!
1.85 + * \internal \brief Rendering target data
1.86 + */
1.87 +typedef struct
1.88 +{
1.89 + M3Gbitmask type;
1.90 + M3GPixelFormat format;
1.91 + M3Gint width, height;
1.92 + M3Guint stride;
1.93 + /*@shared@*/ void *pixels, *lockedPixels;
1.94 + EGLSurface surface;
1.95 + M3Guint handle;
1.96 + M3Guint userData;
1.97 +
1.98 + /*!
1.99 + * \internal
1.100 + * \brief Flag set to indicate back buffer rendering
1.101 + *
1.102 + * The final target is only written to, via a format
1.103 + * conversion, when releasing the target.
1.104 + */
1.105 + M3Gbool buffered;
1.106 +} RenderTarget;
1.107 +
1.108 +/*!
1.109 + * \internal
1.110 + * \brief Back color buffer data
1.111 + */
1.112 +typedef struct {
1.113 +# if defined(M3G_NGL_CONTEXT_API)
1.114 + M3GMemObject handle;
1.115 + M3Gsizei size;
1.116 +# else
1.117 + M3Gint width, height;
1.118 + EGLSurface glSurface;
1.119 +# endif /* M3G_NGL_CONTEXT_API */
1.120 + M3Gbool contentsValid;
1.121 +} BackBuffer;
1.122 +
1.123 +/*!
1.124 + * \internal
1.125 + * \brief Rendering context data structure
1.126 + *
1.127 + * This includes data related to a specific rendering context,
1.128 + * including e.g. viewport settings, and active lights and
1.129 + * camera. This is equivalent to the Graphics3D class in the Java API.
1.130 + */
1.131 +struct M3GRenderContextImpl
1.132 +{
1.133 + Object object;
1.134 +
1.135 + RenderTarget target;
1.136 + BackBuffer backBuffer;
1.137 +# if defined(M3G_NGL_CONTEXT_API)
1.138 + DepthBuffer depthBuffer;
1.139 +# endif
1.140 +
1.141 +# if !defined(M3G_NGL_CONTEXT_API)
1.142 +
1.143 + /* OpenGL context and surface caches */
1.144 +
1.145 + GLContextRecord glContext[M3G_MAX_GL_CONTEXTS];
1.146 + GLSurfaceRecord glSurface[M3G_MAX_GL_SURFACES];
1.147 + M3Guint cacheTimeStamp;
1.148 +
1.149 +# endif /* M3G_NGL_CONTEXT_API */
1.150 +
1.151 + /*! \internal \brief Current/last rendering mode */
1.152 + M3Genum renderMode;
1.153 +
1.154 + /*! \internal \brief OpenGL viewing transformation */
1.155 + GLfloat viewTransform[16];
1.156 +
1.157 + /*! \internal \brief Current camera */
1.158 + const Camera *camera;
1.159 +
1.160 + /*! \internal \brief Light manager component */
1.161 + LightManager lightManager;
1.162 +
1.163 + /*! \internal \brief Last used scope, to speed up light selection */
1.164 + M3Gint lastScope;
1.165 +
1.166 + M3Gfloat depthNear;
1.167 + M3Gfloat depthFar;
1.168 +
1.169 + /*! \internal \brief Clipping rectangle parameters */
1.170 + struct { M3Gint x0, y0, x1, y1; } clip;
1.171 +
1.172 + /*! \internal \brief Scissor and viewport rectangles */
1.173 + struct { GLint x, y, width, height; } scissor, viewport;
1.174 +
1.175 + /*! \internal \brief Physical display size */
1.176 + struct { M3Gint width, height; } display;
1.177 +
1.178 + M3Gbitmask bufferBits; /*!< \brief Rendering buffer bits */
1.179 + M3Gbitmask modeBits; /*!< \brief Rendering mode bits */
1.180 +
1.181 + /*! \internal \brief OpenGL subsystem initialization flag */
1.182 + M3Gbool glInitialized;
1.183 +
1.184 + /*! \internal \brief HW acceleration status flag */
1.185 + M3Gbool accelerated;
1.186 +
1.187 + /*! \internal \brief Render queue for this context */
1.188 + RenderQueue *renderQueue;
1.189 +
1.190 + M3Gbool currentColorWrite;
1.191 + M3Gbool currentAlphaWrite;
1.192 + M3Gbool inSplitDraw;
1.193 + M3Gbool alphaWrite;
1.194 +};
1.195 +
1.196 +/*
1.197 + * Rendering target types; note that the values here MUST match the
1.198 + * respective EGL bit values
1.199 + */
1.200 +enum SurfaceType {
1.201 + SURFACE_NONE = 0,
1.202 + SURFACE_IMAGE = 0x01, /* EGL_PBUFFER_BIT */
1.203 + SURFACE_BITMAP = 0x02, /* EGL_PIXMAP_BIT */
1.204 + SURFACE_WINDOW = 0x04, /* EGL_WINDOW_BIT */
1.205 + SURFACE_MEMORY = SURFACE_IMAGE | SURFACE_BITMAP | SURFACE_WINDOW,
1.206 + SURFACE_EGL = 0x80
1.207 +};
1.208 +
1.209 +enum RenderMode {
1.210 + RENDER_IMMEDIATE,
1.211 + RENDER_NODES,
1.212 + RENDER_WORLD
1.213 +};
1.214 +
1.215 +/*----------------------------------------------------------------------
1.216 + * Platform specific code
1.217 + *--------------------------------------------------------------------*/
1.218 +
1.219 +static M3Gbool m3gBindRenderTarget(RenderContext *ctx,
1.220 + M3Genum targetType,
1.221 + M3Gint width, M3Gint height,
1.222 + M3GPixelFormat format,
1.223 + M3Guint handle);
1.224 +static void m3gResetRectangles(RenderContext *ctx);
1.225 +static void m3gSetGLDefaults(void);
1.226 +static void m3gUpdateScissor(RenderContext *ctx);
1.227 +static void m3gValidateBuffers(RenderContext *ctx);
1.228 +static M3Gbool m3gValidTargetFormat(M3GPixelFormat format);
1.229 +
1.230 +#include "m3g_rendercontext.inl"
1.231 +
1.232 +/*----------------------------------------------------------------------
1.233 + * Internal functions
1.234 + *--------------------------------------------------------------------*/
1.235 +
1.236 +/*!
1.237 + * \internal
1.238 + * \brief Rendering context destructor
1.239 + *
1.240 + */
1.241 +static void m3gDestroyContext(/*@only@*/ Object *obj)
1.242 +{
1.243 + RenderContext *ctx = (RenderContext *) obj;
1.244 + M3G_VALIDATE_OBJECT(ctx);
1.245 +
1.246 + M3G_ASSIGN_REF(ctx->camera, NULL);
1.247 +
1.248 +# if defined(M3G_NGL_CONTEXT_API)
1.249 + if (ctx->target.type == SURFACE_MEMORY && ctx->target.pixels == NULL) {
1.250 + m3gSignalTargetRelease(M3G_INTERFACE(ctx), ctx->target.handle);
1.251 + }
1.252 +
1.253 + m3gFreeObject(M3G_INTERFACE(ctx), ctx->depthBuffer.handle);
1.254 + m3gFreeObject(M3G_INTERFACE(ctx), ctx->backBuffer.handle);
1.255 +
1.256 +# else /* !M3G_NGL_CONTEXT_API */
1.257 +
1.258 + {
1.259 + int i;
1.260 + for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
1.261 + if (ctx->glContext[i].handle != 0) {
1.262 + m3gDeleteGLContext(ctx->glContext[i].handle);
1.263 + }
1.264 + }
1.265 + for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
1.266 + if (ctx->glSurface[i].handle != 0) {
1.267 + m3gDeleteGLSurface(ctx->glSurface[i].handle);
1.268 + }
1.269 + }
1.270 + }
1.271 +
1.272 +# endif /* M3G_NGL_CONTEXT_API */
1.273 +
1.274 + if (ctx->glInitialized) {
1.275 + m3gShutdownGL(M3G_INTERFACE(ctx));
1.276 + }
1.277 +
1.278 + m3gDestroyLightManager(&ctx->lightManager, M3G_INTERFACE(ctx));
1.279 + m3gDestroyRenderQueue(M3G_INTERFACE(ctx), ctx->renderQueue);
1.280 + m3gDestroyObject(obj);
1.281 +}
1.282 +
1.283 +/*!
1.284 + * \internal
1.285 + * \brief Resets the clipping and viewport rectangles to defaults
1.286 + *
1.287 + * This is called after binding a new target.
1.288 + */
1.289 +static void m3gResetRectangles(RenderContext *ctx)
1.290 +{
1.291 + int w = ctx->display.width;
1.292 + int h = ctx->display.height;
1.293 +
1.294 + ctx->clip.x0 = 0;
1.295 + ctx->clip.y0 = ctx->target.height - ctx->display.height;
1.296 + ctx->clip.x1 = w;
1.297 + ctx->clip.y1 = ctx->clip.y0 + h;
1.298 +
1.299 + ctx->viewport.x = 0;
1.300 + ctx->viewport.y = 0;
1.301 + ctx->viewport.width = M3G_MIN(w, M3G_MAX_VIEWPORT_DIMENSION);
1.302 + ctx->viewport.height = M3G_MIN(h, M3G_MAX_VIEWPORT_DIMENSION);
1.303 +}
1.304 +
1.305 +/*!
1.306 + * \internal
1.307 + * \brief Constrains the clip rectangle to the rendering target.
1.308 + */
1.309 +static void m3gValidateClipRect(RenderContext *ctx)
1.310 +{
1.311 + int xMin = 0;
1.312 + int xMax = ctx->display.width;
1.313 + int yMin = ctx->target.height - ctx->display.height;
1.314 + int yMax = yMin + ctx->display.height;
1.315 +
1.316 + ctx->clip.x0 = m3gClampInt(ctx->clip.x0, xMin, xMax);
1.317 + ctx->clip.y0 = m3gClampInt(ctx->clip.y0, yMin, yMax);
1.318 + ctx->clip.x1 = m3gClampInt(ctx->clip.x1, xMin, xMax);
1.319 + ctx->clip.y1 = m3gClampInt(ctx->clip.y1, yMin, yMax);
1.320 +}
1.321 +
1.322 +/*!
1.323 + * \internal
1.324 + * \brief Computes the GL scissor rectangle
1.325 + *
1.326 + * The scissor rectangle is the intersection of the viewport and the
1.327 + * clipping rectangle.
1.328 + */
1.329 +static void m3gUpdateScissor(RenderContext *ctx)
1.330 +{
1.331 + int sx0 = ctx->viewport.x;
1.332 + int sy0 = ctx->viewport.y;
1.333 + int sx1 = sx0 + ctx->viewport.width;
1.334 + int sy1 = sy0 + ctx->viewport.height;
1.335 +
1.336 + sx0 = M3G_MAX(sx0, ctx->clip.x0);
1.337 + sy0 = M3G_MAX(sy0, ctx->clip.y0);
1.338 + sx1 = M3G_MIN(sx1, ctx->clip.x1);
1.339 + sy1 = M3G_MIN(sy1, ctx->clip.y1);
1.340 +
1.341 + ctx->scissor.x = sx0;
1.342 + ctx->scissor.y = sy0;
1.343 +
1.344 + if (sx0 < sx1 && sy0 < sy1) {
1.345 + ctx->scissor.width = sx1 - sx0;
1.346 + ctx->scissor.height = sy1 - sy0;
1.347 + }
1.348 + else {
1.349 + ctx->scissor.width = ctx->scissor.height = 0;
1.350 + }
1.351 +}
1.352 +
1.353 +/*!
1.354 + * \internal
1.355 + * \brief Checks whether we can render in a given format
1.356 + */
1.357 +static M3Gbool m3gValidTargetFormat(M3GPixelFormat format)
1.358 +{
1.359 + return m3gInRange(format, M3G_RGB8, M3G_RGBA4);
1.360 +}
1.361 +
1.362 +/*!
1.363 + * \internal
1.364 + * \brief Checks whether a given format has alpha
1.365 + */
1.366 +static M3Gbool m3gFormatHasAlpha(M3GPixelFormat format)
1.367 +{
1.368 + switch (format) {
1.369 + case M3G_A8:
1.370 + case M3G_LA8:
1.371 + case M3G_LA4:
1.372 + case M3G_RGBA8:
1.373 + case M3G_BGRA8:
1.374 + case M3G_RGBA4:
1.375 + case M3G_RGB5A1:
1.376 + case M3G_PALETTE8_RGBA8:
1.377 + return M3G_TRUE;
1.378 + default:
1.379 + return M3G_FALSE;
1.380 + }
1.381 +}
1.382 +
1.383 +/*!
1.384 + * \internal
1.385 + * \brief Sets the global alpha write enable flag.
1.386 + *
1.387 + * Used for disabling the alpha channel writes when the rendering
1.388 + * target is a Java MIDP Image that has an alpha channel.
1.389 + *
1.390 + * \param ctx the rendering context
1.391 + * \param enable alpha write enable flag
1.392 + */
1.393 +M3G_API void m3gSetAlphaWrite(M3GRenderContext ctx, M3Gbool enable)
1.394 +{
1.395 + ctx->alphaWrite = enable;
1.396 +}
1.397 +
1.398 +/*!
1.399 + * \internal
1.400 + * \brief Reads the global alpha write enable flag.
1.401 + *
1.402 + * \param ctx the rendering context
1.403 + */
1.404 +M3G_API M3Gbool m3gGetAlphaWrite(M3GRenderContext ctx)
1.405 +{
1.406 + return ctx->alphaWrite;
1.407 +}
1.408 +
1.409 +/*!
1.410 + * \brief Frees all GLES resources allocated by the M3G API
1.411 + * (EGL surfaces, contexts and texture objects).
1.412 + *
1.413 + * \note M3G must not be bound to any target when calling this.
1.414 + *
1.415 + */
1.416 +M3G_API void m3gFreeGLESResources(M3GRenderContext ctx)
1.417 +{
1.418 +#ifdef M3G_ENABLE_GLES_RESOURCE_HANDLING
1.419 +
1.420 + PointerArray image2DObjects;
1.421 + M3Gint i;
1.422 +
1.423 + /* M3G must not be bound to a rendering target at this point. */
1.424 + if (ctx->target.type != SURFACE_NONE) {
1.425 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1.426 + }
1.427 +
1.428 + /* EGL might not be initialized yet, so do it here just in case. */
1.429 + eglInitialize(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL);
1.430 + eglBindAPI(EGL_OPENGL_ES_API);
1.431 + eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL, NULL);
1.432 +
1.433 + /* Delete EGL surfaces */
1.434 + for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
1.435 + GLSurfaceRecord *surf = &ctx->glSurface[i];
1.436 + if (surf->handle) {
1.437 + m3gDeleteGLSurface(surf->handle);
1.438 + }
1.439 + m3gZero(surf, sizeof(GLSurfaceRecord));
1.440 + }
1.441 + if (ctx->backBuffer.glSurface != NULL) {
1.442 + m3gDeleteGLSurface(ctx->backBuffer.glSurface);
1.443 + m3gZero(&ctx->backBuffer, sizeof(BackBuffer));
1.444 + }
1.445 +
1.446 + /* Delete EGL contexts */
1.447 + for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
1.448 + GLContextRecord *context = &ctx->glContext[i];
1.449 + if (context->handle) {
1.450 + m3gDeleteGLContext(context->handle);
1.451 + }
1.452 + m3gZero(context, sizeof(GLContextRecord));
1.453 + }
1.454 +
1.455 + /* Delete references to GLES texture objects from all live Image2D objects.
1.456 + Texture objects themselves have already been destroyed with the last GL context. */
1.457 +
1.458 + m3gInitArray(&image2DObjects);
1.459 + m3gGetObjectsWithClassID(M3G_INTERFACE(ctx), M3G_CLASS_IMAGE, &image2DObjects);
1.460 +
1.461 + i = m3gArraySize(&image2DObjects);
1.462 +
1.463 + while (i > 0) {
1.464 + Image *image = (Image*)m3gGetArrayElement(&image2DObjects, --i);
1.465 +
1.466 + m3gInvalidateImage(image);
1.467 + image->texObject = 0;
1.468 + }
1.469 + m3gDestroyArray(&image2DObjects, M3G_INTERFACE(ctx));
1.470 +#endif
1.471 +}
1.472 +
1.473 +
1.474 +/*!
1.475 + * \internal
1.476 + * \brief Sets up a new rendering target
1.477 + *
1.478 + * \param ctx the rendering context
1.479 + * \param targetType rendering target type
1.480 + * \param width width of the target
1.481 + * \param height height of the target
1.482 + * \param format target pixel format
1.483 + * \param handle user object handle
1.484 + */
1.485 +static M3Gbool m3gBindRenderTarget(RenderContext *ctx,
1.486 + M3Genum targetType,
1.487 + M3Gint width, M3Gint height,
1.488 + M3GPixelFormat format,
1.489 + M3Guint handle)
1.490 +{
1.491 + /* Check for generic errors */
1.492 +
1.493 + if (ctx->target.type != SURFACE_NONE) {
1.494 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1.495 + return M3G_FALSE;
1.496 + }
1.497 + if (!m3gValidTargetFormat(format)) {
1.498 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
1.499 + return M3G_FALSE;
1.500 + }
1.501 +
1.502 + /* If target width or height exceeds maximum viewport width or height
1.503 + an exception is thrown. */
1.504 +
1.505 + if (width > M3G_MAX_VIEWPORT_WIDTH ||
1.506 + height > M3G_MAX_VIEWPORT_HEIGHT) {
1.507 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
1.508 + return M3G_FALSE;
1.509 + }
1.510 +
1.511 + /* Everything checks out; set up the target parameters */
1.512 +
1.513 + ctx->target.type = targetType;
1.514 + ctx->target.width = width;
1.515 + ctx->target.height = height;
1.516 + ctx->display.width = width;
1.517 + ctx->display.height = height;
1.518 + ctx->target.format = format;
1.519 + ctx->target.handle = handle;
1.520 + m3gResetRectangles(ctx);
1.521 + m3gUpdateScissor(ctx);
1.522 + m3gValidateBuffers(ctx);
1.523 +
1.524 + /* Invalidate lights in case we're using a different OpenGL
1.525 + * rendering context this time around */
1.526 +
1.527 + ctx->lastScope = 0;
1.528 +
1.529 + return M3G_TRUE;
1.530 +}
1.531 +
1.532 +/*!
1.533 + * \internal
1.534 + * \brief Initializes the current GL context to default settings.
1.535 + */
1.536 +static void m3gSetGLDefaults(void)
1.537 +{
1.538 + static const GLfloat black[] = {0.f, 0.f, 0.f, 0.f};
1.539 + glEnable(GL_NORMALIZE);
1.540 + glEnable(GL_SCISSOR_TEST);
1.541 + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, black);
1.542 + glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1.543 +}
1.544 +
1.545 +/*!
1.546 + * \internal
1.547 + * \brief Validates the buffers required for a rendering context
1.548 + *
1.549 + * Allocates or reallocates buffers as necessary, according to the
1.550 + * currently set flags of the context.
1.551 + */
1.552 +static void m3gValidateBuffers(RenderContext *ctx)
1.553 +{
1.554 + M3G_VALIDATE_OBJECT(ctx);
1.555 +
1.556 + /* Initialize OpenGL if not already done */
1.557 +
1.558 + if (!ctx->glInitialized) {
1.559 + m3gInitializeGL(M3G_INTERFACE(ctx));
1.560 + ctx->glInitialized = M3G_TRUE;
1.561 + }
1.562 +
1.563 + /* Check whether we can render directly to the target or need to
1.564 + * use a back buffer */
1.565 +
1.566 + ctx->target.buffered = !m3gCanDirectRender(ctx);
1.567 +# if defined(M3G_FORCE_BUFFERED_RENDERING)
1.568 + ctx->target.buffered = M3G_TRUE;
1.569 +# endif
1.570 +
1.571 + /* If direct rendering wasn't possible, check that the back buffer
1.572 + * for buffered rendering exists. */
1.573 +
1.574 + if (ctx->target.buffered) {
1.575 + if (!m3gValidateBackBuffer(ctx)) {
1.576 + return; /* out of memory */
1.577 + }
1.578 + }
1.579 +
1.580 + /* With the legacy NGL API, we also manage the depth buffer */
1.581 +
1.582 +# if defined(M3G_NGL_CONTEXT_API)
1.583 + if (!m3gValidateDepthBuffer(ctx)) {
1.584 + return; /* out of memory */
1.585 + }
1.586 +# endif
1.587 +
1.588 + /* Delay blitting from the front buffer until we know it's
1.589 + * necessary; let's raise a flag to check that later on */
1.590 +
1.591 + if (ctx->target.buffered) {
1.592 + if (ctx->modeBits & M3G_OVERWRITE_BIT) {
1.593 + ctx->backBuffer.contentsValid = M3G_TRUE;
1.594 + }
1.595 + else {
1.596 + ctx->backBuffer.contentsValid = M3G_FALSE;
1.597 + }
1.598 + }
1.599 +}
1.600 +
1.601 +/*!
1.602 + * \internal
1.603 + * \brief Makes a GL context current to this thread and the currently
1.604 + * set rendering target buffer
1.605 + */
1.606 +static void m3gMakeCurrent(RenderContext *ctx)
1.607 +{
1.608 + m3gMakeGLCurrent(ctx);
1.609 +
1.610 + /* Note that the depth buffer may in some cases exist even if not
1.611 + * explicitly requested, so we need to disable the depth test just
1.612 + * in case */
1.613 +
1.614 + if ((ctx->bufferBits & M3G_DEPTH_BUFFER_BIT) == 0) {
1.615 + glDisable(GL_DEPTH_TEST);
1.616 + }
1.617 + else {
1.618 + glEnable(GL_DEPTH_TEST);
1.619 + }
1.620 +
1.621 + /* Enable multisampling if required */
1.622 +
1.623 + if (ctx->modeBits & M3G_ANTIALIAS_BIT) {
1.624 + glEnable(GL_MULTISAMPLE);
1.625 + }
1.626 + else {
1.627 + glDisable(GL_MULTISAMPLE);
1.628 + }
1.629 +
1.630 + M3G_ASSERT_GL;
1.631 +}
1.632 +
1.633 +/*!
1.634 + * \internal
1.635 + * \brief Returns the HW acceleration status of the current context
1.636 + */
1.637 +static M3Gbool m3gIsAccelerated(const RenderContext *ctx)
1.638 +{
1.639 + return ctx->accelerated;
1.640 +}
1.641 +
1.642 +/*!
1.643 + * \internal
1.644 + * \brief Sets the currently enabled lights to the GL state
1.645 + *
1.646 + * \note the correct viewing matrix *must* be set prior to calling
1.647 + * this for the lights to be transformed into eye space correctly
1.648 + */
1.649 +static M3G_INLINE void m3gApplyLights(RenderContext *ctx, M3Gint scope)
1.650 +{
1.651 + if (ctx->lastScope != scope) {
1.652 +
1.653 + /* If coming from RenderNode, we have the geometry in camera
1.654 + * space but the lights in world space, so we need to apply
1.655 + * the viewing matrix to the lights only */
1.656 +
1.657 + if (ctx->renderMode == RENDER_NODES) {
1.658 + glPushMatrix();
1.659 + glLoadMatrixf(ctx->viewTransform);
1.660 + }
1.661 +
1.662 + m3gSelectGLLights(&ctx->lightManager, 8, scope, 0, 0, 0);
1.663 + ctx->lastScope = scope;
1.664 +
1.665 + if (ctx->renderMode == RENDER_NODES) {
1.666 + glPopMatrix();
1.667 + }
1.668 + }
1.669 + M3G_ASSERT_GL;
1.670 +}
1.671 +
1.672 +/*!
1.673 + * \internal
1.674 + * \brief Gets the current camera
1.675 + */
1.676 +static const Camera *m3gGetCurrentCamera(const RenderContext *ctx) {
1.677 + return ctx->camera;
1.678 +}
1.679 +
1.680 +/*!
1.681 + * \internal
1.682 + * \brief Sets up some rendering parameters that
1.683 + * do not change during scene renders.
1.684 + */
1.685 +static void m3gInitRender(M3GRenderContext context, M3Genum renderMode)
1.686 +{
1.687 + RenderContext *ctx = (RenderContext *) context;
1.688 + M3G_VALIDATE_OBJECT(ctx);
1.689 +
1.690 + m3gIncrementRenderTimeStamp(ctx);
1.691 + m3gMakeCurrent(ctx);
1.692 + m3gCollectGLObjects(M3G_INTERFACE(ctx));
1.693 +
1.694 + /* If buffered rendering, blit the image to the back buffer at
1.695 + * this point */
1.696 +
1.697 + if (ctx->target.buffered && !ctx->backBuffer.contentsValid) {
1.698 + m3gUpdateBackBuffer(ctx);
1.699 + }
1.700 +
1.701 + /* Set up viewport and scissoring */
1.702 +
1.703 + glViewport(ctx->viewport.x, ctx->viewport.y,
1.704 + ctx->viewport.width, ctx->viewport.height);
1.705 + glDepthRangef(ctx->depthNear, ctx->depthFar);
1.706 + glScissor(ctx->scissor.x, ctx->scissor.y,
1.707 + ctx->scissor.width, ctx->scissor.height);
1.708 + M3G_ASSERT_GL;
1.709 +
1.710 + /* Set up the projection and viewing transformations (static
1.711 + * during rendering) */
1.712 +
1.713 + m3gApplyProjection(ctx->camera);
1.714 + if (renderMode == RENDER_NODES) {
1.715 + glLoadIdentity();
1.716 + }
1.717 + else {
1.718 + glLoadMatrixf(ctx->viewTransform);
1.719 + }
1.720 + M3G_ASSERT_GL;
1.721 +
1.722 + /* Invalidate any already set GL lights if rendering mode changed */
1.723 +
1.724 + if (renderMode != ctx->renderMode) {
1.725 + ctx->lastScope = 0;
1.726 + }
1.727 + ctx->renderMode = renderMode;
1.728 +}
1.729 +
1.730 +/*!
1.731 + * \internal
1.732 + * \brief A workaround for a broken implementation of glColorMask
1.733 + *
1.734 + * Saves the framebuffer in the OpenGL default texture each time the
1.735 + * color mask changes, for restoring later. Not very pretty, but
1.736 + * works as long as the default texture is not touched in between --
1.737 + * currently, we only touch that when copying to and from the back
1.738 + * buffer.
1.739 + *
1.740 + * \param newColorWrite the color mask state we're about to change to
1.741 + * \param newAlphaWrite the alpha write state we're about to change to
1.742 + */
1.743 +static void m3gUpdateColorMaskStatus(RenderContext *ctx,
1.744 + M3Gbool newColorWrite,
1.745 + M3Gbool newAlphaWrite)
1.746 +{
1.747 + GLint pow2Width, pow2Height;
1.748 +
1.749 + /* Get the global alpha write value */
1.750 + newAlphaWrite &= m3gGetAlphaWrite(ctx);
1.751 +
1.752 + /* Check that the ColorMask state is actually about to change */
1.753 +
1.754 + if (ctx->currentColorWrite == newColorWrite
1.755 + && (ctx->currentAlphaWrite == newAlphaWrite || !m3gFormatHasAlpha(ctx->target.format))) {
1.756 + return; /* no change, quick exit */
1.757 + }
1.758 +
1.759 + pow2Width = m3gNextPowerOfTwo(ctx->clip.x1 - ctx->clip.x0);
1.760 + pow2Height = m3gNextPowerOfTwo(ctx->clip.y1 - ctx->clip.y0);
1.761 +
1.762 + /* If we previously had stored something, restore it now */
1.763 +
1.764 + if (ctx->currentColorWrite != ctx->currentAlphaWrite) {
1.765 +
1.766 + /* Disable any stray state we don't want */
1.767 +
1.768 + glDisable(GL_CULL_FACE);
1.769 + glDisable(GL_ALPHA_TEST);
1.770 + glDisableClientState(GL_NORMAL_ARRAY);
1.771 + glDisableClientState(GL_COLOR_ARRAY);
1.772 + glDisable(GL_LIGHTING);
1.773 + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1.774 + glDepthMask(GL_FALSE);
1.775 + glDepthFunc(GL_ALWAYS);
1.776 + m3gDisableTextures();
1.777 + M3G_ASSERT_GL;
1.778 +
1.779 + /* Bind the default texture and set up screen space rendering */
1.780 +
1.781 + glActiveTexture(GL_TEXTURE0);
1.782 + glBindTexture(GL_TEXTURE_2D, 0);
1.783 + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1.784 + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1.785 + M3G_ASSERT_GL;
1.786 +
1.787 + glScissor(ctx->clip.x0, ctx->clip.y0,
1.788 + ctx->clip.x1 - ctx->clip.x0, ctx->clip.y1 - ctx->clip.y0);
1.789 + m3gPushScreenSpace(ctx, M3G_FALSE);
1.790 + glViewport(0, 0, ctx->target.width, ctx->target.height);
1.791 + glMatrixMode(GL_PROJECTION);
1.792 + glOrthox(0, ctx->target.width << 16,
1.793 + 0, ctx->target.height << 16,
1.794 + -1 << 16, 1 << 16);
1.795 + glMatrixMode(GL_MODELVIEW);
1.796 +
1.797 + /* Set up texture and vertex coordinate arrays */
1.798 +
1.799 + glClientActiveTexture(GL_TEXTURE0);
1.800 + glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1.801 + glEnableClientState(GL_VERTEX_ARRAY);
1.802 + glMatrixMode(GL_TEXTURE);
1.803 + glLoadIdentity();
1.804 + glMatrixMode(GL_MODELVIEW);
1.805 + M3G_ASSERT_GL;
1.806 +
1.807 + /* Blend the texture with the frame buffer */
1.808 + {
1.809 + static const M3Gbyte tc[8] = { 0, 0, 0, 1, 1, 0, 1, 1 };
1.810 + GLshort pos[8];
1.811 +
1.812 + GLfixed cm = (GLfixed)(ctx->currentColorWrite ? 0 : 1 << 16);
1.813 + GLfixed am = (GLfixed)(ctx->currentAlphaWrite ? 0 : 1 << 16);
1.814 +
1.815 + glVertexPointer(2, GL_SHORT, 0, pos);
1.816 + glTexCoordPointer(2, GL_BYTE, 0, tc);
1.817 +
1.818 + pos[0] = (GLshort) ctx->clip.x0;
1.819 + pos[1] = (GLshort) ctx->clip.y0;
1.820 + pos[2] = pos[0];
1.821 + pos[3] = (GLshort) (pos[1] + pow2Height);
1.822 + pos[4] = (GLshort) (pos[0] + pow2Width);
1.823 + pos[5] = pos[1];
1.824 + pos[6] = pos[4];
1.825 + pos[7] = pos[3];
1.826 +
1.827 + glEnable(GL_BLEND);
1.828 + glColor4x(cm, cm, cm, am);
1.829 +
1.830 + /* Zero the masked channels */
1.831 +
1.832 + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1.833 + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1.834 +
1.835 + /* Add the masked channels from the stored texture */
1.836 +
1.837 + glEnable(GL_TEXTURE_2D);
1.838 + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1.839 + glBlendFunc(GL_ONE, GL_ONE);
1.840 + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1.841 + }
1.842 +
1.843 + /* Restore the mandatory state */
1.844 +
1.845 + glScissor(ctx->scissor.x, ctx->scissor.y,
1.846 + ctx->scissor.width, ctx->scissor.height);
1.847 + glViewport(ctx->viewport.x, ctx->viewport.y,
1.848 + ctx->viewport.width, ctx->viewport.height);
1.849 + m3gPopSpace(ctx);
1.850 + }
1.851 +
1.852 + /* Copy the current clip rectangle into the default texture if
1.853 + * we're going to be rendering with unsupported masks in effect */
1.854 +
1.855 + if (newColorWrite != newAlphaWrite) {
1.856 + GLenum err;
1.857 +
1.858 + glBindTexture(GL_TEXTURE_2D, 0);
1.859 + M3G_ASSERT_GL;
1.860 +
1.861 + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1.862 + ctx->clip.x0, ctx->clip.y0,
1.863 + pow2Width, pow2Height,
1.864 + 0);
1.865 + err = glGetError();
1.866 + if (err == GL_INVALID_OPERATION) {
1.867 + /* Incompatible FB format -- must be GL_RGB then */
1.868 + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
1.869 + ctx->clip.x0, ctx->clip.y0,
1.870 + pow2Width, pow2Height,
1.871 + 0);
1.872 + err = glGetError();
1.873 + }
1.874 + if (err == GL_OUT_OF_MEMORY) {
1.875 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
1.876 + }
1.877 + M3G_ASSERT(!err);
1.878 + }
1.879 + else {
1.880 +
1.881 + /* Texture not needed for now, so allow GL to free some
1.882 + * resources */
1.883 +
1.884 + glTexImage2D(GL_TEXTURE_2D, 0,
1.885 + GL_RGBA,
1.886 + 1, 1,
1.887 + 0,
1.888 + GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1.889 + }
1.890 +
1.891 + ctx->currentColorWrite = newColorWrite;
1.892 + ctx->currentAlphaWrite = newAlphaWrite;
1.893 +}
1.894 +
1.895 +/*!
1.896 + * \internal
1.897 + * \brief Sets the GL to input screen space coordinates
1.898 + *
1.899 + * This pushes the current modelview and projection matrices into the
1.900 + * matrix stack, then sets up an orthogonal projection and an identity
1.901 + * modelview matrix.
1.902 + *
1.903 + * \param ctx the rendering context
1.904 + * \param realPixels M3G_TRUE to use actual pixel coordinates,
1.905 + * M3G_FALSE to use normalized device coordinates
1.906 + */
1.907 +static void m3gPushScreenSpace(RenderContext *ctx, M3Gbool realPixels)
1.908 +{
1.909 + M3G_VALIDATE_OBJECT(ctx);
1.910 +
1.911 + glMatrixMode(GL_PROJECTION);
1.912 + glPushMatrix();
1.913 + glLoadIdentity();
1.914 + if (realPixels) {
1.915 + int w = ctx->viewport.width;
1.916 + int h = ctx->viewport.height;
1.917 + glOrthox(0, w << 16, 0, h << 16, -1 << 16, 1 << 16);
1.918 + }
1.919 + glMatrixMode(GL_MODELVIEW);
1.920 + glPushMatrix();
1.921 + glLoadIdentity();
1.922 +}
1.923 +
1.924 +/*!
1.925 + * \internal
1.926 + * \brief Restores the projection and modelview matrix modified by
1.927 + * m3gPushScreenSpace
1.928 + */
1.929 +static void m3gPopSpace(RenderContext *ctx)
1.930 +{
1.931 + M3G_VALIDATE_OBJECT(ctx);
1.932 +
1.933 + M3G_UNREF(ctx);
1.934 + glMatrixMode(GL_PROJECTION);
1.935 + glPopMatrix();
1.936 + glMatrixMode(GL_MODELVIEW);
1.937 + glPopMatrix();
1.938 +}
1.939 +
1.940 +/*!
1.941 + * \internal
1.942 + * \brief Clears the current buffer(s)
1.943 + */
1.944 +static void m3gClearInternal(RenderContext *ctx, Background *bg)
1.945 +{
1.946 + m3gMakeCurrent(ctx);
1.947 +
1.948 + /* If buffered rendering, copy data to the back buffer at this
1.949 + * point if we're not clearing the whole clip rectangle */
1.950 +
1.951 + if (ctx->target.buffered && !ctx->backBuffer.contentsValid) {
1.952 + if (ctx->scissor.x > ctx->clip.x0 || ctx->scissor.y > ctx->clip.y0 ||
1.953 + ctx->scissor.x + ctx->scissor.width < ctx->clip.x1 ||
1.954 + ctx->scissor.y + ctx->scissor.height < ctx->clip.y1) {
1.955 + m3gUpdateBackBuffer(ctx);
1.956 + }
1.957 + }
1.958 +
1.959 + if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
1.960 + m3gUpdateColorMaskStatus(ctx, M3G_TRUE, M3G_TRUE);
1.961 + }
1.962 +
1.963 + glDepthMask(GL_TRUE);
1.964 + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, m3gGetAlphaWrite(ctx));
1.965 + glDepthRangef(ctx->depthNear, ctx->depthFar);
1.966 + glViewport(ctx->viewport.x, ctx->viewport.y,
1.967 + ctx->viewport.width, ctx->viewport.height);
1.968 + glScissor(ctx->scissor.x, ctx->scissor.y,
1.969 + ctx->scissor.width, ctx->scissor.height);
1.970 +
1.971 + /* Touch the background image to make sure it's created prior to
1.972 + * locking memory for rendering */
1.973 +
1.974 + if (bg != NULL && bg->image != NULL) {
1.975 + if (!m3gGetPowerOfTwoImage(bg->image)) {
1.976 + return; /* out of memory */
1.977 + }
1.978 + }
1.979 +
1.980 + /* All clear for clearing... */
1.981 +
1.982 + m3gLockFrameBuffer(ctx);
1.983 +
1.984 + if (bg != NULL) {
1.985 + m3gApplyBackground(ctx, bg);
1.986 + if (ctx->target.buffered && bg->colorClearEnable) {
1.987 + ctx->backBuffer.contentsValid = M3G_TRUE;
1.988 + }
1.989 + }
1.990 + else {
1.991 + glClearColorx(0, 0, 0, 0);
1.992 + glClearDepthx(1 << 16);
1.993 + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
1.994 + if (ctx->target.buffered) {
1.995 + ctx->backBuffer.contentsValid = M3G_TRUE;
1.996 + }
1.997 + }
1.998 +
1.999 + m3gReleaseFrameBuffer(ctx);
1.1000 +}
1.1001 +
1.1002 +/*!
1.1003 + * \internal
1.1004 + * \brief Draws a batch of primitives
1.1005 + *
1.1006 + * This is the place most rendering commands are eventually routed to;
1.1007 + * sprites and backgrounds are the only exception to this. We assume
1.1008 + * that all eror checking has been performed at this point.
1.1009 + */
1.1010 +static void m3gDrawMesh(RenderContext *ctx,
1.1011 + const VertexBuffer *vb,
1.1012 + const IndexBuffer *ib,
1.1013 + const Appearance *app,
1.1014 + const M3GMatrix *modelTransform,
1.1015 + M3Gint alphaFactor,
1.1016 + M3Gint scope)
1.1017 +{
1.1018 + M3G_VALIDATE_OBJECT(ctx);
1.1019 + M3G_VALIDATE_OBJECT(vb);
1.1020 + M3G_VALIDATE_OBJECT(ib);
1.1021 + M3G_VALIDATE_OBJECT(app);
1.1022 +
1.1023 + /* Check whether we need to use alternate rendering to get
1.1024 + * two-sided lighting */
1.1025 + if (m3gGetTwoSidedLightingWorkaround(M3G_INTERFACE(ctx))) {
1.1026 + if (m3gSplitDrawMesh(ctx, vb, ib, app, modelTransform, alphaFactor, scope)) {
1.1027 + return;
1.1028 + }
1.1029 + }
1.1030 +
1.1031 + M3G_ASSERT(m3gInRange(alphaFactor, 0, 0x10000));
1.1032 +
1.1033 + if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
1.1034 + m3gUpdateColorMaskStatus(ctx, m3gColorMask(app), m3gAlphaMask(app));
1.1035 + }
1.1036 +
1.1037 + /* Load lights */
1.1038 +
1.1039 + m3gApplyLights(ctx, scope);
1.1040 +
1.1041 + /* Apply the extra modeling transformation if present */
1.1042 +
1.1043 + if (modelTransform != NULL) {
1.1044 + float transform[16];
1.1045 + m3gGetMatrixColumns(modelTransform, transform);
1.1046 +
1.1047 + glPushMatrix();
1.1048 + glMultMatrixf(transform);
1.1049 + }
1.1050 +
1.1051 + /* Check whether we need to create an alpha-factored color cache
1.1052 + * for the vertex buffer; this requires unlocking the frame buffer
1.1053 + * for a while, and we may even run out of memory in the process,
1.1054 + * but we still need to exit with the frame buffer lock and the
1.1055 + * matrix stack in the expected state */
1.1056 +
1.1057 + if (alphaFactor < 0x10000 && !m3gValidateAlphaCache(vb)) {
1.1058 + M3Gbool ok;
1.1059 + m3gReleaseFrameBuffer(ctx);
1.1060 + ok = m3gCreateAlphaColorCache(vb->colors);
1.1061 + m3gLockFrameBuffer(ctx);
1.1062 + if (!ok) {
1.1063 + goto RestoreModelview; /* let's just skip the drawing part */
1.1064 + }
1.1065 + }
1.1066 +
1.1067 +# if defined(M3G_NGL_TEXTURE_API)
1.1068 + /* Similarly to the alpha cache above, also check whether any
1.1069 + * textures may need to allocate mipmaps at this point */
1.1070 + {
1.1071 + M3Gint i;
1.1072 + for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
1.1073 + Texture *tex = app->texture[i];
1.1074 + if (tex && !m3gValidateTextureMipmapping(tex)) {
1.1075 + M3Gbool ok;
1.1076 + m3gReleaseFrameBuffer(ctx);
1.1077 + ok = m3gValidateMipmapMemory(m3gGetTextureImage(tex));
1.1078 + m3gLockFrameBuffer(ctx);
1.1079 + if (!ok) {
1.1080 + goto RestoreModelview;
1.1081 + }
1.1082 + }
1.1083 + }
1.1084 + }
1.1085 +# endif
1.1086 +
1.1087 + /* Load up the rest of the stuff we need for rendering; note that
1.1088 + * the vertex buffer scale and bias apply to the texture matrix
1.1089 + * from the appearance object, so they need to be applied last */
1.1090 +
1.1091 + m3gApplyAppearance(app, ctx, alphaFactor);
1.1092 + m3gLockVertexBuffer(vb, alphaFactor);
1.1093 + m3gApplyScaleAndBias(vb);
1.1094 +
1.1095 + /* All ready, render and then release the stuff we bound above */
1.1096 +
1.1097 + m3gSendIndexBuffer(ib);
1.1098 + m3gReleaseVertexBuffer(vb);
1.1099 + m3gReleaseTextures(app);
1.1100 +
1.1101 + /* Restore viewing-only modelview if changed */
1.1102 +
1.1103 +RestoreModelview:
1.1104 + if (modelTransform != NULL) {
1.1105 + glPopMatrix();
1.1106 + }
1.1107 +}
1.1108 +
1.1109 +/*!
1.1110 + * \internal
1.1111 + * \brief Validates background format against current target
1.1112 + *
1.1113 + * \retval M3G_TRUE valid format
1.1114 + * \retval M3G_FALSE invalid format
1.1115 + */
1.1116 +static M3Gbool m3gValidateBackground(RenderContext *ctx, Background *bg)
1.1117 +{
1.1118 + /* Check that source image and target formats match */
1.1119 + if (bg != NULL && bg->image != NULL) {
1.1120 + M3GPixelFormat boundFormat =
1.1121 + (ctx->target.type == SURFACE_IMAGE)
1.1122 + ? m3gPixelFormat(((const Image *)ctx->target.handle)->format)
1.1123 + : ctx->target.format;
1.1124 + if (ctx->target.type == SURFACE_IMAGE && boundFormat == M3G_RGBA8) {
1.1125 + return (m3gGetFormat(bg->image) == M3G_RGBA);
1.1126 + }
1.1127 + else {
1.1128 + return (m3gGetFormat(bg->image) == M3G_RGB);
1.1129 + }
1.1130 + }
1.1131 +
1.1132 + return M3G_TRUE;
1.1133 +}
1.1134 +
1.1135 +/*----------------------------------------------------------------------
1.1136 + * Virtual function table
1.1137 + *--------------------------------------------------------------------*/
1.1138 +
1.1139 +static const ObjectVFTable m3gvf_RenderContext = {
1.1140 + NULL, /* ApplyAnimation */
1.1141 + NULL, /* IsCompatible */
1.1142 + NULL, /* UpdateProperty */
1.1143 + NULL, /* GetReference */
1.1144 + NULL, /* find */
1.1145 + NULL, /* CreateClone */
1.1146 + m3gDestroyContext
1.1147 +};
1.1148 +
1.1149 +
1.1150 +/*----------------------------------------------------------------------
1.1151 + * Public API
1.1152 + *--------------------------------------------------------------------*/
1.1153 +
1.1154 +/*!
1.1155 + * \brief Creates and initializes a new rendering context
1.1156 + *
1.1157 + * \param bufferBits buffer bitmask
1.1158 + * \param width maximum width of context
1.1159 + * \param height maximum height of context
1.1160 + * \param modeBits hint bitmask
1.1161 + * \param mem pointer to memory block to allocate from
1.1162 + */
1.1163 +/*@access M3GInterface@*/
1.1164 +/*@access M3GRenderContext@*/
1.1165 +/*@only@*/
1.1166 +M3G_API M3GRenderContext m3gCreateContext(M3GInterface interface)/*@*/
1.1167 +{
1.1168 + Interface *m3g = (Interface*) interface;
1.1169 + M3G_VALIDATE_INTERFACE(m3g);
1.1170 +
1.1171 + {
1.1172 + RenderContext *ctx =
1.1173 + (RenderContext*) m3gAllocZ(m3g, (int) sizeof(RenderContext));
1.1174 + if (ctx == NULL) {
1.1175 + return NULL; /* m3gAlloc automatically raises out-of-mem */
1.1176 + }
1.1177 +
1.1178 + ctx->renderQueue = m3gCreateRenderQueue(m3g);
1.1179 + if (ctx->renderQueue == NULL) {
1.1180 + m3gFree(m3g, ctx);
1.1181 + return NULL;
1.1182 + }
1.1183 + ctx->bufferBits = M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT;
1.1184 + ctx->depthNear = 0.0f;
1.1185 + ctx->depthFar = 1.0f;
1.1186 +
1.1187 + m3gInitObject(&ctx->object, m3g, M3G_CLASS_RENDER_CONTEXT);
1.1188 +
1.1189 + m3gSetAlphaWrite(ctx, M3G_TRUE);
1.1190 +
1.1191 + if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
1.1192 + ctx->currentColorWrite = M3G_TRUE;
1.1193 + ctx->currentAlphaWrite = m3gGetAlphaWrite(ctx);
1.1194 + }
1.1195 +
1.1196 + return (M3GRenderContext)ctx;
1.1197 + }
1.1198 +}
1.1199 +
1.1200 +/*!
1.1201 + * \brief Sets the buffers to use for subsequent rendering
1.1202 + */
1.1203 +M3G_API M3Gbool m3gSetRenderBuffers(M3GRenderContext hCtx,
1.1204 + M3Gbitmask bufferBits)
1.1205 +{
1.1206 + RenderContext *ctx = (RenderContext *) hCtx;
1.1207 + M3G_VALIDATE_OBJECT(ctx);
1.1208 +
1.1209 + if ((bufferBits & ~(M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT|M3G_STENCIL_BUFFER_BIT|M3G_MULTISAMPLE_BUFFER_BIT)) != 0) {
1.1210 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1.1211 + return M3G_FALSE;
1.1212 + }
1.1213 + ctx->bufferBits = bufferBits;
1.1214 + return M3G_TRUE;
1.1215 +}
1.1216 +
1.1217 +/*!
1.1218 + * \brief Sets the rendering quality hints to use for subsequent
1.1219 + * rendering
1.1220 + *
1.1221 + * \note This may not take effect before the target is released and
1.1222 + * rebound
1.1223 + */
1.1224 +M3G_API M3Gbool m3gSetRenderHints(M3GRenderContext hCtx, M3Gbitmask modeBits)
1.1225 +{
1.1226 + RenderContext *ctx = (RenderContext *) hCtx;
1.1227 + M3G_VALIDATE_OBJECT(ctx);
1.1228 +
1.1229 + if ((modeBits & ~(M3G_OVERWRITE_BIT|M3G_ANTIALIAS_BIT|M3G_DITHER_BIT|M3G_TRUECOLOR_BIT)) != 0) {
1.1230 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1.1231 + return M3G_FALSE;
1.1232 + }
1.1233 +
1.1234 + /* Disable features not supported in the current configuration */
1.1235 +
1.1236 + if (M3G_SUPPORT_ANTIALIASING == M3G_FALSE ||
1.1237 + !m3gIsAntialiasingSupported(M3G_INTERFACE(ctx))) {
1.1238 + modeBits &= ~M3G_ANTIALIAS_BIT;
1.1239 + }
1.1240 + if (M3G_SUPPORT_DITHERING == M3G_FALSE) {
1.1241 + modeBits &= ~M3G_DITHER_BIT;
1.1242 + }
1.1243 + if (M3G_SUPPORT_TRUE_COLOR == M3G_FALSE) {
1.1244 + modeBits &= ~M3G_TRUECOLOR_BIT;
1.1245 + }
1.1246 +
1.1247 + ctx->modeBits = modeBits;
1.1248 + return M3G_TRUE;
1.1249 +}
1.1250 +
1.1251 +M3G_API void m3gBindImageTarget(M3GRenderContext hCtx, M3GImage hImage)
1.1252 +{
1.1253 + RenderContext *ctx = (RenderContext *) hCtx;
1.1254 + Image *img = (Image *) hImage;
1.1255 + M3G_VALIDATE_OBJECT(ctx);
1.1256 + M3G_VALIDATE_OBJECT(img);
1.1257 +
1.1258 + M3G_LOG1(M3G_LOG_RENDERING, "Binding image target 0x%08X\n",
1.1259 + (unsigned) img);
1.1260 +
1.1261 + /* Check for image-specific errors */
1.1262 +
1.1263 + if ((img->flags & M3G_DYNAMIC) == 0
1.1264 + || !m3gValidTargetFormat(img->internalFormat)) {
1.1265 +
1.1266 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_ENUM);
1.1267 + return;
1.1268 + }
1.1269 +
1.1270 + /* Do the generic checking and set-up */
1.1271 +
1.1272 + if (!m3gBindRenderTarget(ctx,
1.1273 + SURFACE_IMAGE,
1.1274 + img->width, img->height,
1.1275 + img->internalFormat,
1.1276 + (M3Guint) hImage)) {
1.1277 + return; /* appropriate error raised automatically */
1.1278 + }
1.1279 +
1.1280 + /* Set up image-specific parameters */
1.1281 +
1.1282 +# if defined(M3G_NGL_CONTEXT_API)
1.1283 + ctx->target.stride = m3gGetImageStride(img);
1.1284 + ctx->target.pixels = NULL;
1.1285 +# endif
1.1286 +
1.1287 + m3gAddRef((Object*) img);
1.1288 +}
1.1289 +
1.1290 +/*!
1.1291 + */
1.1292 +M3G_API M3Guint m3gGetUserHandle(M3GRenderContext hCtx)
1.1293 +{
1.1294 + RenderContext *ctx = (RenderContext *) hCtx;
1.1295 + M3G_VALIDATE_OBJECT(ctx);
1.1296 +
1.1297 + if (ctx->target.type == SURFACE_MEMORY) {
1.1298 + return ctx->target.handle;
1.1299 + }
1.1300 + return 0;
1.1301 +}
1.1302 +
1.1303 +/*!
1.1304 + */
1.1305 +M3G_API void m3gSetUserData(M3GRenderContext hCtx, M3Guint hData)
1.1306 +{
1.1307 + RenderContext *ctx = (RenderContext *) hCtx;
1.1308 + M3G_VALIDATE_OBJECT(ctx);
1.1309 + ctx->target.userData = hData;
1.1310 +}
1.1311 +
1.1312 +/*!
1.1313 + */
1.1314 +M3G_API M3Guint m3gGetUserData(M3GRenderContext hCtx)
1.1315 +{
1.1316 + RenderContext *ctx = (RenderContext *) hCtx;
1.1317 + M3G_VALIDATE_OBJECT(ctx);
1.1318 + return ctx->target.userData;
1.1319 +}
1.1320 +
1.1321 +/*!
1.1322 + * \brief Clears the current buffer(s)
1.1323 + */
1.1324 +M3G_API void m3gClear(M3GRenderContext context, M3GBackground hBackground)
1.1325 +{
1.1326 + RenderContext *ctx = (RenderContext*) context;
1.1327 + Background *bg = (Background *) hBackground;
1.1328 + M3G_VALIDATE_OBJECT(ctx);
1.1329 +
1.1330 + M3G_LOG(M3G_LOG_STAGES, "Clearing frame buffer\n");
1.1331 +
1.1332 + /* Check errors */
1.1333 +
1.1334 + if (ctx->target.type == SURFACE_NONE) {
1.1335 + m3gRaiseError(M3G_INTERFACE(context), M3G_INVALID_OPERATION);
1.1336 + return;
1.1337 + }
1.1338 +
1.1339 + if(m3gValidateBackground(ctx, bg)) {
1.1340 + m3gClearInternal(ctx, bg);
1.1341 + }
1.1342 + else {
1.1343 + m3gRaiseError(M3G_INTERFACE(bg), M3G_INVALID_VALUE);
1.1344 + }
1.1345 +}
1.1346 +
1.1347 +/*!
1.1348 + * \brief Release the currently bound color buffer
1.1349 + *
1.1350 + * Flushes all rendering and commits the final result to the currently
1.1351 + * bound target color buffer. Any changes to the target buffer since
1.1352 + * it was bound may be overwritten.
1.1353 + */
1.1354 +M3G_API void m3gReleaseTarget(M3GRenderContext context)
1.1355 +{
1.1356 + RenderContext *ctx = (RenderContext*) context;
1.1357 + M3G_VALIDATE_OBJECT(ctx);
1.1358 +
1.1359 + M3G_LOG(M3G_LOG_RENDERING, "Releasing target\n");
1.1360 +
1.1361 + if (ctx->target.type == SURFACE_NONE) {
1.1362 + return;
1.1363 + }
1.1364 +
1.1365 + m3gMakeCurrent(ctx);
1.1366 +
1.1367 + if (m3gGetColorMaskWorkaround(M3G_INTERFACE(ctx))) {
1.1368 + m3gUpdateColorMaskStatus(ctx, M3G_TRUE, M3G_TRUE);
1.1369 + }
1.1370 +
1.1371 + glFinish();
1.1372 +
1.1373 + /* Update the real target if we rendered into the back buffer */
1.1374 +
1.1375 + if (ctx->target.buffered) {
1.1376 + m3gUpdateTargetBuffer(ctx);
1.1377 + }
1.1378 +
1.1379 + /* Invalidate Image targets so that mipmap levels and/or OpenGL
1.1380 + * texture objects are updated accordingly */
1.1381 +
1.1382 + if (ctx->target.type == SURFACE_IMAGE) {
1.1383 + Image *img = (Image *) ctx->target.handle;
1.1384 + M3G_VALIDATE_OBJECT(img);
1.1385 + m3gInvalidateImage(img);
1.1386 + m3gDeleteRef((Object*) img);
1.1387 + }
1.1388 +
1.1389 + /* Swap in case we rendered onto a double-buffered surface,
1.1390 + * release any GL resources that might have been release since the
1.1391 + * last time we rendered, then release the GL context so we don't
1.1392 + * hog resources */
1.1393 +# if !defined(M3G_NGL_CONTEXT_API)
1.1394 + if (ctx->target.type == SURFACE_WINDOW) {
1.1395 + m3gSwapBuffers(ctx->target.surface);
1.1396 + }
1.1397 +# endif
1.1398 + m3gCollectGLObjects(M3G_INTERFACE(ctx));
1.1399 +# if !defined(M3G_NGL_CONTEXT_API)
1.1400 + m3gMakeGLCurrent(NULL);
1.1401 + ctx->target.surface = NULL;
1.1402 +# else
1.1403 + if (ctx->target.type == SURFACE_MEMORY && ctx->target.pixels == NULL) {
1.1404 + m3gSignalTargetRelease(M3G_INTERFACE(ctx), ctx->target.handle);
1.1405 + }
1.1406 +# endif
1.1407 +
1.1408 + ctx->target.type = SURFACE_NONE;
1.1409 + ctx->renderQueue->root = NULL;
1.1410 +
1.1411 +# if (M3G_PROFILE_LOG_INTERVAL > 0)
1.1412 + m3gLogProfileCounters(M3G_INTERFACE(ctx));
1.1413 +# endif
1.1414 +}
1.1415 +
1.1416 +/*!
1.1417 + * \brief Sets a camera for this context
1.1418 + */
1.1419 +M3G_API void m3gSetCamera(M3GRenderContext context,
1.1420 + M3GCamera hCamera,
1.1421 + M3GMatrix *transform)
1.1422 +{
1.1423 + Matrix m;
1.1424 + RenderContext *ctx = (RenderContext*) context;
1.1425 + const Camera *camera = (Camera *)hCamera;
1.1426 +
1.1427 + M3G_VALIDATE_OBJECT(ctx);
1.1428 +
1.1429 + M3G_ASSIGN_REF(ctx->camera, camera);
1.1430 +
1.1431 + if (transform != NULL) {
1.1432 + if (!m3gMatrixInverse(&m, transform)) {
1.1433 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_ARITHMETIC_ERROR);
1.1434 + return;
1.1435 + }
1.1436 + }
1.1437 + else {
1.1438 + m3gIdentityMatrix(&m);
1.1439 + }
1.1440 +
1.1441 + m3gGetMatrixColumns(&m, ctx->viewTransform);
1.1442 +
1.1443 + ctx->lastScope = 0;
1.1444 +}
1.1445 +
1.1446 +/*!
1.1447 + * \brief Adds a light to the light array for this context
1.1448 + */
1.1449 +M3G_API M3Gint m3gAddLight(M3GRenderContext hCtx,
1.1450 + M3GLight hLight,
1.1451 + const M3GMatrix *transform)
1.1452 +{
1.1453 + RenderContext *ctx = (RenderContext *)hCtx;
1.1454 + Light *light = (Light *)hLight;
1.1455 + M3G_VALIDATE_OBJECT(ctx);
1.1456 +
1.1457 + if (light == NULL) {
1.1458 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1.1459 + return -1;
1.1460 + }
1.1461 + else {
1.1462 + LightManager *mgr = &ctx->lightManager;
1.1463 + M3G_VALIDATE_OBJECT(light);
1.1464 + ctx->lastScope = 0;
1.1465 + return m3gInsertLight(mgr, light, transform, M3G_INTERFACE(ctx));
1.1466 + }
1.1467 +}
1.1468 +
1.1469 +/**
1.1470 + * \brief Sets a light for this context
1.1471 + */
1.1472 +M3G_API void m3gSetLight(M3GRenderContext context,
1.1473 + M3Gint lightIndex,
1.1474 + M3GLight hLight,
1.1475 + const M3GMatrix *transform)
1.1476 +{
1.1477 + RenderContext *ctx = (RenderContext*) context;
1.1478 + Light *light = (Light *)hLight;
1.1479 + M3G_VALIDATE_OBJECT(ctx);
1.1480 +
1.1481 + /* Check for invalid arguments */
1.1482 + if (lightIndex < 0 || lightIndex >= m3gLightArraySize(&ctx->lightManager)) {
1.1483 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_INDEX);
1.1484 + return;
1.1485 + }
1.1486 +
1.1487 + ctx->lastScope = 0;
1.1488 + m3gReplaceLight(&ctx->lightManager, lightIndex, light, transform);
1.1489 +}
1.1490 +
1.1491 +/*!
1.1492 + * \brief Removes all lights from this context
1.1493 + */
1.1494 +M3G_API void m3gClearLights(M3GRenderContext context)
1.1495 +{
1.1496 + RenderContext *ctx = (RenderContext *)context;
1.1497 + M3G_VALIDATE_OBJECT(ctx);
1.1498 + ctx->lastScope = 0;
1.1499 + m3gClearLights2(&ctx->lightManager);
1.1500 +}
1.1501 +
1.1502 +/*!
1.1503 + * \brief Sets the viewport
1.1504 + *
1.1505 + */
1.1506 +M3G_API void m3gSetViewport(M3GRenderContext hCtx,
1.1507 + M3Gint x, M3Gint y,
1.1508 + M3Gint width, M3Gint height)
1.1509 +{
1.1510 + RenderContext *ctx = (RenderContext *)hCtx;
1.1511 + M3G_VALIDATE_OBJECT(ctx);
1.1512 +
1.1513 + /* Note that the error checking here differs from that specified
1.1514 + * for the Java API; this is to avoid complications when setting
1.1515 + * from BindTarget where the clip rectangle may be zero.
1.1516 + * Additional checks are performed in the Java glue code. */
1.1517 +
1.1518 + if (width < 0 || height < 0) {
1.1519 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1.1520 + return;
1.1521 + }
1.1522 +
1.1523 + width = M3G_MIN(width, M3G_MAX_VIEWPORT_DIMENSION);
1.1524 + height = M3G_MIN(height, M3G_MAX_VIEWPORT_DIMENSION);
1.1525 +
1.1526 + ctx->viewport.x = x;
1.1527 + ctx->viewport.y = ctx->target.height - (y + height);
1.1528 + ctx->viewport.width = width;
1.1529 + ctx->viewport.height = height;
1.1530 + m3gUpdateScissor(ctx);
1.1531 +}
1.1532 +
1.1533 +
1.1534 +/*!
1.1535 + * \brief Gets the viewport
1.1536 + */
1.1537 +M3G_API void m3gGetViewport(M3GRenderContext hCtx,
1.1538 + M3Gint *x, M3Gint *y,
1.1539 + M3Gint *width, M3Gint *height)
1.1540 +{
1.1541 + RenderContext *ctx = (RenderContext *)hCtx;
1.1542 + M3G_VALIDATE_OBJECT(ctx);
1.1543 +
1.1544 + *x = ctx->viewport.x;
1.1545 + *y = ctx->target.height - (ctx->viewport.y + ctx->viewport.height);
1.1546 + *width = ctx->viewport.width;
1.1547 + *height = ctx->viewport.height;
1.1548 +}
1.1549 +
1.1550 +/*!
1.1551 + * \brief Sets the scissor rectangle
1.1552 + */
1.1553 +M3G_API void m3gSetClipRect(M3GRenderContext hCtx,
1.1554 + M3Gint x, M3Gint y,
1.1555 + M3Gint width, M3Gint height)
1.1556 +{
1.1557 + RenderContext *ctx = (RenderContext *)hCtx;
1.1558 + M3G_VALIDATE_OBJECT(ctx);
1.1559 +
1.1560 + if (width < 0 || height < 0) {
1.1561 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1.1562 + return;
1.1563 + }
1.1564 + ctx->clip.x0 = x;
1.1565 + ctx->clip.y0 = ctx->target.height - (y + height);
1.1566 + ctx->clip.x1 = x + width;
1.1567 + ctx->clip.y1 = ctx->clip.y0 + height;
1.1568 + m3gValidateClipRect(ctx);
1.1569 + m3gUpdateScissor(ctx);
1.1570 +}
1.1571 +
1.1572 +/*!
1.1573 + * \brief Sets the physical display area
1.1574 + *
1.1575 + * The display are is normally set to the full rendering targte size
1.1576 + * in m3gBindTarget, but this function allows overriding the default
1.1577 + * setting.
1.1578 + *
1.1579 + * Any pixels outside of the display area can be discarded for
1.1580 + * performance. The origin is assumed to be in the top-left corner of
1.1581 + * the rendering target.
1.1582 + */
1.1583 +M3G_API void m3gSetDisplayArea(M3GRenderContext hCtx,
1.1584 + M3Gint width, M3Gint height)
1.1585 +{
1.1586 + RenderContext *ctx = (RenderContext*) hCtx;
1.1587 + M3G_VALIDATE_OBJECT(ctx);
1.1588 +
1.1589 + ctx->display.width = M3G_MIN(width, ctx->target.width);
1.1590 + ctx->display.height = M3G_MIN(height, ctx->target.height);
1.1591 +}
1.1592 +
1.1593 +/*!
1.1594 + * \brief Sets depth range
1.1595 + *
1.1596 + */
1.1597 +M3G_API void m3gSetDepthRange(M3GRenderContext hCtx,
1.1598 + M3Gfloat depthNear, M3Gfloat depthFar)
1.1599 +{
1.1600 + RenderContext *ctx = (RenderContext *)hCtx;
1.1601 + M3G_VALIDATE_OBJECT(ctx);
1.1602 +
1.1603 + if (depthNear < 0 || depthNear > 1.0f || depthFar < 0 || depthFar > 1.0f) {
1.1604 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_VALUE);
1.1605 + return;
1.1606 + }
1.1607 +
1.1608 + ctx->depthNear = depthNear;
1.1609 + ctx->depthFar = depthFar;
1.1610 +}
1.1611 +
1.1612 +/*!
1.1613 + * \brief Gets depth range
1.1614 + *
1.1615 + */
1.1616 +M3G_API void m3gGetDepthRange(M3GRenderContext hCtx,
1.1617 + M3Gfloat *depthNear, M3Gfloat *depthFar)
1.1618 +{
1.1619 + RenderContext *ctx = (RenderContext *)hCtx;
1.1620 + M3G_VALIDATE_OBJECT(ctx);
1.1621 +
1.1622 + *depthNear = ctx->depthNear;
1.1623 + *depthFar= ctx->depthFar;
1.1624 +}
1.1625 +
1.1626 +/*!
1.1627 + * \brief Gets current view transform
1.1628 + *
1.1629 + */
1.1630 +
1.1631 +M3G_API void m3gGetViewTransform(M3GRenderContext hCtx,
1.1632 + M3GMatrix *transform)
1.1633 +{
1.1634 + RenderContext *ctx = (RenderContext *)hCtx;
1.1635 + M3G_VALIDATE_OBJECT(ctx);
1.1636 + m3gSetMatrixColumns(transform, ctx->viewTransform);
1.1637 + m3gInvertMatrix(transform); /*lint !e534 always invertible */
1.1638 +}
1.1639 +
1.1640 +/*!
1.1641 + * \brief Gets current Camera
1.1642 + *
1.1643 + */
1.1644 +
1.1645 +M3G_API M3GCamera m3gGetCamera(M3GRenderContext hCtx)
1.1646 +{
1.1647 + RenderContext *ctx = (RenderContext *)hCtx;
1.1648 + M3G_VALIDATE_OBJECT(ctx);
1.1649 + return (M3GCamera) ctx->camera;
1.1650 +}
1.1651 +
1.1652 +/*!
1.1653 + * \brief Gets light transform of given light
1.1654 + *
1.1655 + */
1.1656 +
1.1657 +M3G_API M3GLight m3gGetLightTransform (M3GRenderContext hCtx,
1.1658 + M3Gint lightIndex, M3GMatrix *transform)
1.1659 +{
1.1660 + RenderContext *ctx = (RenderContext *)hCtx;
1.1661 + M3G_VALIDATE_OBJECT(ctx);
1.1662 + return m3gGetLightTransformInternal(&ctx->lightManager, lightIndex, transform);
1.1663 +}
1.1664 +
1.1665 +/*!
1.1666 + * \brief Gets light count
1.1667 + *
1.1668 + */
1.1669 +
1.1670 +M3G_API M3Gsizei m3gGetLightCount (M3GRenderContext hCtx)
1.1671 +{
1.1672 + RenderContext *ctx = (RenderContext *)hCtx;
1.1673 + M3G_VALIDATE_OBJECT(ctx);
1.1674 + return m3gLightArraySize(&ctx->lightManager);
1.1675 +}
1.1676 +
1.1677 +/*!
1.1678 + * \brief Renders a world
1.1679 + *
1.1680 + */
1.1681 +M3G_API void m3gRenderWorld(M3GRenderContext context, M3GWorld hWorld)
1.1682 +{
1.1683 + Camera *camera;
1.1684 + RenderContext *ctx = (RenderContext*) context;
1.1685 + World *world = (World *) hWorld;
1.1686 +
1.1687 + M3G_LOG1(M3G_LOG_STAGES, "Rendering World 0x%08X\n", (unsigned) world);
1.1688 +
1.1689 + M3G_VALIDATE_OBJECT(ctx);
1.1690 + M3G_VALIDATE_OBJECT(world);
1.1691 +
1.1692 + camera = m3gGetActiveCamera(world);
1.1693 +
1.1694 + /* Check for errors */
1.1695 +
1.1696 + if (ctx->target.type == SURFACE_NONE) {
1.1697 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1.1698 + return;
1.1699 + }
1.1700 +
1.1701 + if (camera == NULL ||
1.1702 + !m3gIsChildOf((Node *)world, (Node *)camera)) {
1.1703 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1.1704 + return;
1.1705 + }
1.1706 +
1.1707 + /* Exit if the camera will show nothing (zero view volume) */
1.1708 +
1.1709 + if (!m3gValidProjection(camera)) {
1.1710 + return;
1.1711 + }
1.1712 +
1.1713 + /* Override the currently set viewing transformation with identity
1.1714 + * (will fix this before we return) */
1.1715 +
1.1716 + m3gSetCamera(ctx, camera, NULL);
1.1717 +
1.1718 + if (m3gValidateBackground(ctx, world->background)) {
1.1719 + m3gClearInternal(ctx, world->background);
1.1720 + }
1.1721 + else {
1.1722 + m3gRaiseError(M3G_INTERFACE(world), M3G_INVALID_OPERATION);
1.1723 + return;
1.1724 + }
1.1725 +
1.1726 + /* All clear for rendering */
1.1727 +
1.1728 + M3G_LOG(M3G_LOG_RENDERING, "Rendering: start\n");
1.1729 + M3G_ASSERT(ctx->renderQueue->root == NULL);
1.1730 + M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
1.1731 +
1.1732 + if (m3gValidateNode((Node*) world, NODE_RENDER_BIT, camera->node.scope)) {
1.1733 + M3Gbool setup;
1.1734 + SetupRenderState s;
1.1735 + M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
1.1736 +
1.1737 + /* We start the traversal from the camera, so set the initial
1.1738 + * camera-space transformation to identity */
1.1739 +
1.1740 + m3gIdentityMatrix(&s.toCamera);
1.1741 + s.cullMask = CULLMASK_ALL;
1.1742 +
1.1743 + m3gClearLights2(&ctx->lightManager);
1.1744 +
1.1745 + ctx->renderQueue->root = (Node *)world;
1.1746 + ctx->renderQueue->scope = camera->node.scope;
1.1747 + ctx->renderQueue->lightManager = &ctx->lightManager;
1.1748 + ctx->renderQueue->camera = camera;
1.1749 +
1.1750 + M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
1.1751 +
1.1752 + setup = M3G_VFUNC(Node, camera, setupRender)((Node *) camera,
1.1753 + NULL,
1.1754 + &s,
1.1755 + ctx->renderQueue);
1.1756 + M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
1.1757 + M3G_LOG(M3G_LOG_RENDERING, "Rendering: commit\n");
1.1758 + M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
1.1759 +
1.1760 + if (setup) {
1.1761 + m3gInitRender(ctx, RENDER_WORLD);
1.1762 + m3gLockFrameBuffer(ctx);
1.1763 + m3gCommit(ctx->renderQueue, ctx);
1.1764 + m3gReleaseFrameBuffer(ctx);
1.1765 + }
1.1766 +
1.1767 + M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
1.1768 +
1.1769 + /* Fix light and camera transformations to be relative to world
1.1770 + * space on exit */
1.1771 +
1.1772 + if (setup) {
1.1773 + Matrix m;
1.1774 + if (m3gGetTransformTo((Node*) world, (Node*) camera, &m)) {
1.1775 + m3gGetMatrixColumns(&m, ctx->viewTransform);
1.1776 + if (m3gInvertMatrix(&m)) {
1.1777 + m3gTransformLights(&ctx->lightManager, &m);
1.1778 + }
1.1779 + else {
1.1780 + M3G_ASSERT(M3G_FALSE);
1.1781 + }
1.1782 + }
1.1783 + else {
1.1784 + M3G_ASSERT(M3G_FALSE);
1.1785 + }
1.1786 + }
1.1787 + }
1.1788 +
1.1789 + m3gClearRenderQueue(ctx->renderQueue);
1.1790 + M3G_LOG(M3G_LOG_RENDERING, "Rendering: end\n");
1.1791 +}
1.1792 +
1.1793 +/*!
1.1794 + * \brief Renders a node or subtree
1.1795 + */
1.1796 +M3G_API void m3gRenderNode(M3GRenderContext context,
1.1797 + M3GNode hNode,
1.1798 + const M3GMatrix *transform)
1.1799 +{
1.1800 + RenderContext *ctx = (RenderContext*) context;
1.1801 + Node *node = (Node *) hNode;
1.1802 +
1.1803 + M3G_LOG1(M3G_LOG_STAGES, "Rendering Node 0x%08X\n", (unsigned) node);
1.1804 +
1.1805 + M3G_VALIDATE_OBJECT(ctx);
1.1806 + M3G_VALIDATE_OBJECT(node);
1.1807 +
1.1808 + /* Check for errors */
1.1809 +
1.1810 + if (node == NULL) {
1.1811 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_NULL_POINTER);
1.1812 + return;
1.1813 + }
1.1814 +
1.1815 + if (ctx->target.type == SURFACE_NONE || ctx->camera == NULL) {
1.1816 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1.1817 + return;
1.1818 + }
1.1819 +
1.1820 + /* Exit if the camera will show nothing (zero view volume) */
1.1821 +
1.1822 + if (!m3gValidProjection(ctx->camera)) {
1.1823 + return;
1.1824 + }
1.1825 +
1.1826 + /* All clear, draw away */
1.1827 +
1.1828 + M3G_LOG(M3G_LOG_RENDERING, "Rendering: start\n");
1.1829 + M3G_ASSERT(ctx->renderQueue->root == NULL);
1.1830 + M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
1.1831 +
1.1832 + if (m3gValidateNode(node, NODE_RENDER_BIT, ctx->camera->node.scope)) {
1.1833 + M3Gbool setup;
1.1834 + SetupRenderState s;
1.1835 + M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_VALIDATE);
1.1836 +
1.1837 + s.cullMask = CULLMASK_ALL;
1.1838 +
1.1839 + /* We start the traversal from world space, so preload the
1.1840 + * current camera-space transformation to get camera-space
1.1841 + * meshes and correct view frustum culling */
1.1842 +
1.1843 + m3gSetMatrixColumns(&s.toCamera, ctx->viewTransform);
1.1844 + if (transform) {
1.1845 + m3gMulMatrix(&s.toCamera, transform);
1.1846 + }
1.1847 + ctx->renderQueue->root = (Node *) node;
1.1848 + ctx->renderQueue->scope = ctx->camera->node.scope;
1.1849 + ctx->renderQueue->lightManager = NULL;
1.1850 + ctx->renderQueue->camera = ctx->camera;
1.1851 +
1.1852 + M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
1.1853 +
1.1854 + setup = M3G_VFUNC(Node, node, setupRender)(node,
1.1855 + NULL,
1.1856 + &s,
1.1857 + ctx->renderQueue);
1.1858 + M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_SETUP);
1.1859 + M3G_LOG(M3G_LOG_RENDERING, "Rendering: commit\n");
1.1860 + M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
1.1861 +
1.1862 + if (setup) {
1.1863 + m3gInitRender(ctx, RENDER_NODES);
1.1864 + m3gLockFrameBuffer(ctx);
1.1865 + m3gCommit(ctx->renderQueue, ctx);
1.1866 + m3gReleaseFrameBuffer(ctx);
1.1867 + }
1.1868 +
1.1869 + M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_COMMIT);
1.1870 + }
1.1871 +
1.1872 + m3gClearRenderQueue(ctx->renderQueue);
1.1873 +
1.1874 + M3G_LOG(M3G_LOG_RENDERING, "Rendering: end\n");
1.1875 +}
1.1876 +
1.1877 +/*!
1.1878 + * \brief Render a set of primitives
1.1879 + *
1.1880 + */
1.1881 +M3G_API void m3gRender(M3GRenderContext context,
1.1882 + M3GVertexBuffer hVertices,
1.1883 + M3GIndexBuffer hIndices,
1.1884 + M3GAppearance hAppearance,
1.1885 + const M3GMatrix *transformMatrix,
1.1886 + M3Gfloat alphaFactor,
1.1887 + M3Gint scope)
1.1888 +{
1.1889 + RenderContext *ctx = (RenderContext *) context;
1.1890 + const VertexBuffer *vb = (const VertexBuffer *) hVertices;
1.1891 + const IndexBuffer *ib = (const IndexBuffer *) hIndices;
1.1892 + const Appearance *app = (const Appearance *) hAppearance;
1.1893 + M3G_VALIDATE_OBJECT(ctx);
1.1894 +
1.1895 + M3G_LOG1(M3G_LOG_STAGES, "Rendering vertex buffer 0x%08X\n",
1.1896 + (unsigned) vb);
1.1897 +
1.1898 + /* Check validity of input */
1.1899 +
1.1900 + if (ctx->target.type == SURFACE_NONE || ctx->camera == NULL) {
1.1901 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1.1902 + return;
1.1903 + }
1.1904 +
1.1905 + /* Quick exit if out of scope or zero view volume */
1.1906 +
1.1907 + if ((scope & ctx->camera->node.scope) == 0
1.1908 + || !m3gValidProjection(ctx->camera)) {
1.1909 + return;
1.1910 + }
1.1911 +
1.1912 + if (vb == NULL || ib == NULL || app == NULL) {
1.1913 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT);
1.1914 + return;
1.1915 + }
1.1916 +
1.1917 + if (!m3gValidateVertexBuffer(vb, app, m3gGetMaxIndex(ib))) {
1.1918 + m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OPERATION);
1.1919 + return;
1.1920 + }
1.1921 +
1.1922 + /* Everything checks out, so draw */
1.1923 +
1.1924 + M3G_LOG(M3G_LOG_RENDERING, "Rendering: start immediate\n");
1.1925 +
1.1926 + m3gInitRender(ctx, RENDER_IMMEDIATE);
1.1927 + m3gLockFrameBuffer(ctx);
1.1928 + m3gDrawMesh(ctx,
1.1929 + vb, ib, app,
1.1930 + transformMatrix,
1.1931 + m3gRoundToInt(
1.1932 + m3gMul(alphaFactor,
1.1933 + (M3Gfloat)(1 << NODE_ALPHA_FACTOR_BITS))),
1.1934 + scope);
1.1935 + m3gReleaseFrameBuffer(ctx);
1.1936 +
1.1937 + M3G_LOG(M3G_LOG_RENDERING, "Rendering: end immediate\n");
1.1938 +}
1.1939 +