static void set_clip_planes (CoglFramebuffer *framebuffer, CoglMatrixEntry *modelview_entry, float x_1, float y_1, float x_2, float y_2) { CoglMatrix modelview_matrix; CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglMatrix projection_matrix; CoglMatrix modelview_projection; float signed_area; float vertex_tl[4] = { x_1, y_1, 0, 1.0 }; float vertex_tr[4] = { x_2, y_1, 0, 1.0 }; float vertex_bl[4] = { x_1, y_2, 0, 1.0 }; float vertex_br[4] = { x_2, y_2, 0, 1.0 }; cogl_matrix_stack_get (projection_stack, &projection_matrix); cogl_matrix_entry_get (modelview_entry, &modelview_matrix); cogl_matrix_multiply (&modelview_projection, &projection_matrix, &modelview_matrix); project_vertex (&modelview_projection, vertex_tl); project_vertex (&modelview_projection, vertex_tr); project_vertex (&modelview_projection, vertex_bl); project_vertex (&modelview_projection, vertex_br); /* Calculate the signed area of the polygon formed by the four vertices so that we can know its orientation */ signed_area = (vertex_tl[0] * (vertex_tr[1] - vertex_bl[1]) + vertex_tr[0] * (vertex_br[1] - vertex_tl[1]) + vertex_br[0] * (vertex_bl[1] - vertex_tr[1]) + vertex_bl[0] * (vertex_tl[1] - vertex_br[1])); /* Set the clip planes to form lines between all of the vertices using the same orientation as we calculated */ if (signed_area > 0.0f) { /* counter-clockwise */ set_clip_plane (framebuffer, GL_CLIP_PLANE0, vertex_tl, vertex_bl); set_clip_plane (framebuffer, GL_CLIP_PLANE1, vertex_bl, vertex_br); set_clip_plane (framebuffer, GL_CLIP_PLANE2, vertex_br, vertex_tr); set_clip_plane (framebuffer, GL_CLIP_PLANE3, vertex_tr, vertex_tl); } else { /* clockwise */ set_clip_plane (framebuffer, GL_CLIP_PLANE0, vertex_tl, vertex_tr); set_clip_plane (framebuffer, GL_CLIP_PLANE1, vertex_tr, vertex_br); set_clip_plane (framebuffer, GL_CLIP_PLANE2, vertex_br, vertex_bl); set_clip_plane (framebuffer, GL_CLIP_PLANE3, vertex_bl, vertex_tl); } }
CoglClipStack * _cogl_clip_stack_push_primitive (CoglClipStack *stack, CoglPrimitive *primitive, float bounds_x1, float bounds_y1, float bounds_x2, float bounds_y2, CoglMatrixEntry *modelview_entry, CoglMatrixEntry *projection_entry, const float *viewport) { CoglClipStackPrimitive *entry; CoglMatrix modelview; CoglMatrix projection; float transformed_corners[8]; entry = _cogl_clip_stack_push_entry (stack, sizeof (CoglClipStackPrimitive), COGL_CLIP_STACK_PRIMITIVE); entry->primitive = cogl_object_ref (primitive); entry->matrix_entry = cogl_matrix_entry_ref (modelview_entry); entry->bounds_x1 = bounds_x1; entry->bounds_y1 = bounds_y1; entry->bounds_x2 = bounds_x2; entry->bounds_y2 = bounds_y2; cogl_matrix_entry_get (modelview_entry, &modelview); cogl_matrix_entry_get (projection_entry, &projection); get_transformed_corners (bounds_x1, bounds_y1, bounds_x2, bounds_y2, &modelview, &projection, viewport, transformed_corners); /* NB: this is referring to the bounds in window coordinates as opposed * to the bounds above in primitive local coordinates. */ _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, transformed_corners); return (CoglClipStack *) entry; }
void _cogl_matrix_entry_flush_to_gl_builtins (CoglContext *ctx, CoglMatrixEntry *entry, CoglMatrixMode mode, CoglFramebuffer *framebuffer, CoglBool disable_flip) { g_assert (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)); #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) { CoglBool needs_flip; CoglMatrixEntryCache *cache; if (mode == COGL_MATRIX_PROJECTION) { /* Because Cogl defines texture coordinates to have a top left * origin and because offscreen framebuffers may be used for * rendering to textures we always render upside down to * offscreen buffers. Also for some backends we need to render * onscreen buffers upside-down too. */ if (disable_flip) needs_flip = FALSE; else needs_flip = cogl_is_offscreen (framebuffer); cache = &ctx->builtin_flushed_projection; } else { needs_flip = FALSE; if (mode == COGL_MATRIX_MODELVIEW) cache = &ctx->builtin_flushed_modelview; else cache = NULL; } /* We don't need to do anything if the state is the same */ if (!cache || _cogl_matrix_entry_cache_maybe_update (cache, entry, needs_flip)) { CoglBool is_identity; CoglMatrix matrix; if (entry->op == COGL_MATRIX_OP_LOAD_IDENTITY) is_identity = TRUE; else { is_identity = FALSE; cogl_matrix_entry_get (entry, &matrix); } if (needs_flip) { CoglMatrix flipped_matrix; cogl_matrix_multiply (&flipped_matrix, &ctx->y_flip_matrix, is_identity ? &ctx->identity_matrix : &matrix); _cogl_matrix_flush_to_gl_builtin (ctx, /* not identity */ FALSE, &flipped_matrix, mode); } else { _cogl_matrix_flush_to_gl_builtin (ctx, is_identity, &matrix, mode); } } } #endif }
/* In addition to writing the stack matrix into the give @matrix * argument this function *may* sometimes also return a pointer * to a matrix too so if we are querying the inverse matrix we * should query from the return matrix so that the result can * be cached within the stack. */ CoglMatrix * cogl_matrix_stack_get (CoglMatrixStack *stack, CoglMatrix *matrix) { return cogl_matrix_entry_get (stack->last_entry, matrix); }
/* In addition to writing the stack matrix into the give @matrix * argument this function *may* sometimes also return a pointer * to a matrix too so if we are querying the inverse matrix we * should query from the return matrix so that the result can * be cached within the stack. */ CoglMatrix * cogl_matrix_entry_get (CoglMatrixEntry *entry, CoglMatrix *matrix) { int depth; CoglMatrixEntry *current; CoglMatrixEntry **children; int i; for (depth = 0, current = entry; current; current = current->parent, depth++) { switch (current->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: cogl_matrix_init_identity (matrix); goto initialized; case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)current; *matrix = *load->matrix; goto initialized; } case COGL_MATRIX_OP_SAVE: { CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)current; if (!save->cache_valid) { CoglMagazine *matrices_magazine = cogl_matrix_stack_matrices_magazine; save->cache = _cogl_magazine_chunk_alloc (matrices_magazine); cogl_matrix_entry_get (current->parent, save->cache); save->cache_valid = TRUE; } *matrix = *save->cache; goto initialized; } default: continue; } } initialized: if (depth == 0) { switch (entry->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: case COGL_MATRIX_OP_TRANSLATE: case COGL_MATRIX_OP_ROTATE: case COGL_MATRIX_OP_ROTATE_QUATERNION: case COGL_MATRIX_OP_ROTATE_EULER: case COGL_MATRIX_OP_SCALE: case COGL_MATRIX_OP_MULTIPLY: return NULL; case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; return load->matrix; } case COGL_MATRIX_OP_SAVE: { CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry; return save->cache; } } g_warn_if_reached (); return NULL; } #ifdef COGL_ENABLE_DEBUG if (!current) { g_warning ("Inconsistent matrix stack"); return NULL; } entry->composite_gets++; #endif children = g_alloca (sizeof (CoglMatrixEntry) * depth); /* We need walk the list of entries from the init/load/save entry * back towards the leaf node but the nodes don't link to their * children so we need to re-walk them here to add to a separate * array. */ for (i = depth - 1, current = entry; i >= 0 && current; i--, current = current->parent) { children[i] = current; } #ifdef COGL_ENABLE_DEBUG if (COGL_DEBUG_ENABLED (COGL_DEBUG_PERFORMANCE) && entry->composite_gets >= 2) { COGL_NOTE (PERFORMANCE, "Re-composing a matrix stack entry multiple times"); } #endif for (i = 0; i < depth; i++) { switch (children[i]->op) { case COGL_MATRIX_OP_TRANSLATE: { CoglMatrixEntryTranslate *translate = (CoglMatrixEntryTranslate *)children[i]; cogl_matrix_translate (matrix, translate->x, translate->y, translate->z); continue; } case COGL_MATRIX_OP_ROTATE: { CoglMatrixEntryRotate *rotate= (CoglMatrixEntryRotate *)children[i]; cogl_matrix_rotate (matrix, rotate->angle, rotate->x, rotate->y, rotate->z); continue; } case COGL_MATRIX_OP_ROTATE_EULER: { CoglMatrixEntryRotateEuler *rotate = (CoglMatrixEntryRotateEuler *)children[i]; CoglEuler euler; cogl_euler_init (&euler, rotate->heading, rotate->pitch, rotate->roll); cogl_matrix_rotate_euler (matrix, &euler); continue; } case COGL_MATRIX_OP_ROTATE_QUATERNION: { CoglMatrixEntryRotateQuaternion *rotate = (CoglMatrixEntryRotateQuaternion *)children[i]; CoglQuaternion quaternion; cogl_quaternion_init_from_array (&quaternion, rotate->values); cogl_matrix_rotate_quaternion (matrix, &quaternion); continue; } case COGL_MATRIX_OP_SCALE: { CoglMatrixEntryScale *scale = (CoglMatrixEntryScale *)children[i]; cogl_matrix_scale (matrix, scale->x, scale->y, scale->z); continue; } case COGL_MATRIX_OP_MULTIPLY: { CoglMatrixEntryMultiply *multiply = (CoglMatrixEntryMultiply *)children[i]; cogl_matrix_multiply (matrix, matrix, multiply->matrix); continue; } case COGL_MATRIX_OP_LOAD_IDENTITY: case COGL_MATRIX_OP_LOAD: case COGL_MATRIX_OP_SAVE: g_warn_if_reached (); continue; } } return NULL; }
CoglClipStack * _cogl_clip_stack_push_rectangle (CoglClipStack *stack, float x_1, float y_1, float x_2, float y_2, CoglMatrixEntry *modelview_entry, CoglMatrixEntry *projection_entry, const float *viewport) { CoglClipStackRect *entry; CoglMatrix modelview; CoglMatrix projection; CoglMatrix modelview_projection; /* Corners of the given rectangle in an clockwise order: * (0, 1) (2, 3) * * * * (6, 7) (4, 5) */ float rect[] = { x_1, y_1, x_2, y_1, x_2, y_2, x_1, y_2 }; /* Make a new entry */ entry = _cogl_clip_stack_push_entry (stack, sizeof (CoglClipStackRect), COGL_CLIP_STACK_RECT); entry->x0 = x_1; entry->y0 = y_1; entry->x1 = x_2; entry->y1 = y_2; entry->matrix_entry = cogl_matrix_entry_ref (modelview_entry); cogl_matrix_entry_get (modelview_entry, &modelview); cogl_matrix_entry_get (projection_entry, &projection); cogl_matrix_multiply (&modelview_projection, &projection, &modelview); /* Technically we could avoid the viewport transform at this point * if we want to make this a bit faster. */ _cogl_transform_point (&modelview, &projection, viewport, &rect[0], &rect[1]); _cogl_transform_point (&modelview, &projection, viewport, &rect[2], &rect[3]); _cogl_transform_point (&modelview, &projection, viewport, &rect[4], &rect[5]); _cogl_transform_point (&modelview, &projection, viewport, &rect[6], &rect[7]); /* If the fully transformed rectangle isn't still axis aligned we * can't handle it using a scissor. * * We don't use an epsilon here since we only really aim to catch * simple cases where the transform doesn't leave the rectangle screen * aligned and don't mind some false positives. */ if (rect[0] != rect[6] || rect[1] != rect[3] || rect[2] != rect[4] || rect[7] != rect[5]) { entry->can_be_scissor = FALSE; _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, rect); } else { CoglClipStack *base_entry = (CoglClipStack *) entry; x_1 = rect[0]; y_1 = rect[1]; x_2 = rect[4]; y_2 = rect[5]; /* Consider that the modelview matrix may flip the rectangle * along the x or y axis... */ #define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0) if (x_1 > x_2) SWAP (x_1, x_2); if (y_1 > y_2) SWAP (y_1, y_2); #undef SWAP base_entry->bounds_x0 = COGL_UTIL_NEARBYINT (x_1); base_entry->bounds_y0 = COGL_UTIL_NEARBYINT (y_1); base_entry->bounds_x1 = COGL_UTIL_NEARBYINT (x_2); base_entry->bounds_y1 = COGL_UTIL_NEARBYINT (y_2); entry->can_be_scissor = TRUE; } return (CoglClipStack *) entry; }