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