/src/gpac/src/compositor/mpeg4_composite.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2023 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / Scene Compositor sub-project |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | |
27 | | |
28 | | #include <gpac/internal/scenegraph_dev.h> |
29 | | |
30 | | #include "nodes_stacks.h" |
31 | | #include "visual_manager.h" |
32 | | #include "texturing.h" |
33 | | |
34 | | #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR) |
35 | | |
36 | | #ifdef GPAC_USE_TINYGL |
37 | | #include <GL/oscontext.h> |
38 | | #endif |
39 | | |
40 | | typedef struct |
41 | | { |
42 | | GF_TextureHandler txh; |
43 | | Fixed sx, sy; |
44 | | /*the visual object handling the texture*/ |
45 | | GF_VisualManager *visual; |
46 | | Bool first, unsupported; |
47 | | GF_List *sensors, *previous_sensors, *temp_sensors, *temp_previous_sensors; |
48 | | GF_Node *prev_hit_appear; |
49 | | //revent recursions |
50 | | Bool in_handle_event; |
51 | | GF_TraverseState *tr_state; |
52 | | |
53 | | #ifdef GPAC_USE_TINYGL |
54 | | ostgl_context *tgl_ctx; |
55 | | #else |
56 | | Bool use_fbo; |
57 | | #endif |
58 | | } CompositeTextureStack; |
59 | | |
60 | | |
61 | | static Bool composite2d_draw_bitmap(GF_VisualManager *visual, GF_TraverseState *tr_state, struct _drawable_context *ctx) |
62 | 0 | { |
63 | 0 | u8 alpha = 0xFF; |
64 | 0 | GF_VideoSurface offscreen_dst, video_src; |
65 | 0 | GF_Window src_wnd, dst_wnd; |
66 | 0 | Bool use_blit, has_scale; |
67 | 0 | CompositeTextureStack *st; |
68 | |
|
69 | 0 | if (visual->compositor->disable_composite_blit) return 0; |
70 | | |
71 | 0 | if (!ctx->aspect.fill_texture) return 1; |
72 | 0 | if (ctx->transform.m[0]<0) return 0; |
73 | 0 | if (ctx->transform.m[4]<0) { |
74 | 0 | if (!(ctx->flags & CTX_FLIPED_COORDS)) return 0; |
75 | 0 | } else { |
76 | 0 | if (ctx->flags & CTX_FLIPED_COORDS) return 0; |
77 | 0 | } |
78 | 0 | if (ctx->transform.m[1] || ctx->transform.m[3]) return 0; |
79 | 0 | #ifndef GPAC_DISABLE_VRML |
80 | 0 | if ((ctx->flags & CTX_HAS_APPEARANCE) && ctx->appear && ((M_Appearance*)ctx->appear)->textureTransform) |
81 | 0 | return 0; |
82 | 0 | #endif |
83 | | |
84 | 0 | alpha = GF_COL_A(ctx->aspect.fill_color); |
85 | | /*THIS IS A HACK, will not work when setting filled=0, transparency and XLineProps*/ |
86 | 0 | if (!alpha) alpha = GF_COL_A(ctx->aspect.line_color); |
87 | 0 | if (!alpha) return 1; |
88 | | |
89 | 0 | st = (CompositeTextureStack *) gf_node_get_private(visual->offscreen); |
90 | |
|
91 | 0 | if (!compositor_texture_rectangles(visual, ctx->aspect.fill_texture, &ctx->bi->clip, &ctx->bi->unclip, &src_wnd, &dst_wnd, &use_blit, &has_scale)) return 1; |
92 | 0 | if (! ctx->aspect.fill_texture->data) return 0; |
93 | | |
94 | 0 | memset(&video_src, 0, sizeof(GF_VideoSurface)); |
95 | 0 | video_src.height = ctx->aspect.fill_texture->height; |
96 | 0 | video_src.width = ctx->aspect.fill_texture->width; |
97 | 0 | video_src.pitch_x = 0; |
98 | 0 | video_src.pitch_y = ctx->aspect.fill_texture->stride; |
99 | 0 | video_src.pixel_format = ctx->aspect.fill_texture->pixelformat; |
100 | 0 | #ifdef GF_SR_USE_DEPTH |
101 | 0 | if (ctx->aspect.fill_texture->pixelformat==GF_PIXEL_YUVD) video_src.pixel_format = GF_PIXEL_YUV; |
102 | 0 | #endif |
103 | 0 | video_src.video_buffer = ctx->aspect.fill_texture->data; |
104 | |
|
105 | 0 | memset(&offscreen_dst, 0, sizeof(GF_VideoSurface)); |
106 | 0 | offscreen_dst.width = st->txh.width; |
107 | 0 | offscreen_dst.height = st->txh.height; |
108 | 0 | offscreen_dst.pitch_y = st->txh.stride; |
109 | 0 | offscreen_dst.pixel_format = st->txh.pixelformat; |
110 | 0 | offscreen_dst.video_buffer = st->txh.data; |
111 | |
|
112 | 0 | gf_stretch_bits(&offscreen_dst, &video_src, &dst_wnd, &src_wnd, alpha, 0, tr_state->col_key, ctx->col_mat); |
113 | 0 | return 1; |
114 | 0 | } |
115 | | |
116 | | static void composite_traverse(GF_Node *node, void *rs, Bool is_destroy) |
117 | 0 | { |
118 | 0 | if (is_destroy) { |
119 | 0 | u32 i=0; |
120 | 0 | GF_VisualManager *a_visual; |
121 | 0 | CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(node); |
122 | | /*unregister visual*/ |
123 | 0 | gf_sc_visual_unregister(st->visual->compositor, st->visual); |
124 | | |
125 | | /*We must make sure we don't keep pointers to this composite in the different visuals. |
126 | | - we must track Appearance nodes at the compositor level to undo the textureTransform while picking |
127 | | - but we clearly don't want to track destruction of all appearance nodes just to solve this texture delete |
128 | | => remove the entire compositeTexture appearance state - this may lead to small bugs in interaction logics, however they should |
129 | | not be too damageable |
130 | | */ |
131 | 0 | st->visual->compositor->hit_appear = NULL; |
132 | 0 | st->visual->compositor->prev_hit_appear = NULL; |
133 | |
|
134 | 0 | while ( (a_visual = gf_list_enum(st->visual->compositor->visuals, &i))) { |
135 | 0 | if (a_visual->offscreen) { |
136 | 0 | CompositeTextureStack *a_st = (CompositeTextureStack *) gf_node_get_private(a_visual->offscreen); |
137 | 0 | a_st->prev_hit_appear = NULL; |
138 | 0 | } |
139 | 0 | } |
140 | |
|
141 | 0 | visual_del(st->visual); |
142 | 0 | if (st->txh.data) gf_free(st->txh.data); |
143 | | /*destroy texture*/ |
144 | 0 | gf_sc_texture_destroy(&st->txh); |
145 | | #ifdef GPAC_USE_TINYGL |
146 | | if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); |
147 | | #endif |
148 | |
|
149 | 0 | gf_list_del(st->sensors); |
150 | 0 | gf_list_del(st->previous_sensors); |
151 | |
|
152 | 0 | gf_list_del(st->tr_state->vrml_sensors); |
153 | 0 | gf_free(st->tr_state); |
154 | |
|
155 | 0 | gf_free(st); |
156 | 0 | } else { |
157 | 0 | gf_node_traverse_children(node, rs); |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | static Bool composite_do_bindable(GF_Node *n, GF_TraverseState *tr_state, Bool force_check) |
162 | 0 | { |
163 | 0 | GF_Node *btop; |
164 | 0 | Bool ret = 0; |
165 | 0 | switch (gf_node_get_tag(n)) { |
166 | | #ifndef GPAC_DISABLE_3D |
167 | | case TAG_MPEG4_CompositeTexture3D: |
168 | | { |
169 | | M_CompositeTexture3D*c3d = (M_CompositeTexture3D*)n; |
170 | | if (force_check || gf_node_dirty_get(c3d->background)) { |
171 | | gf_node_traverse(c3d->background, tr_state); |
172 | | ret = 1; |
173 | | } |
174 | | btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); |
175 | | if (btop != c3d->background) { |
176 | | gf_node_unregister(c3d->background, n); |
177 | | gf_node_register(btop, n); |
178 | | c3d->background = btop; |
179 | | gf_node_event_out(n, 5/*"background"*/); |
180 | | ret = 1; |
181 | | } |
182 | | if (force_check || gf_node_dirty_get(c3d->viewpoint)) { |
183 | | gf_node_traverse(c3d->viewpoint, tr_state); |
184 | | ret = 1; |
185 | | } |
186 | | btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); |
187 | | if (btop != c3d->viewpoint) { |
188 | | gf_node_unregister(c3d->viewpoint, n); |
189 | | gf_node_register(btop, n); |
190 | | c3d->viewpoint = btop; |
191 | | gf_node_event_out(n, 8/*"viewpoint"*/); |
192 | | ret = 1; |
193 | | } |
194 | | |
195 | | if (force_check || gf_node_dirty_get(c3d->fog)) { |
196 | | gf_node_traverse(c3d->fog, tr_state); |
197 | | ret = 1; |
198 | | } |
199 | | btop = (GF_Node*)gf_list_get(tr_state->fogs, 0); |
200 | | if (btop != c3d->fog) { |
201 | | gf_node_unregister(c3d->fog, n); |
202 | | gf_node_register(btop, n); |
203 | | c3d->fog = btop; |
204 | | gf_node_event_out(n, 6/*"fog"*/); |
205 | | ret = 1; |
206 | | } |
207 | | |
208 | | if (force_check || gf_node_dirty_get(c3d->navigationInfo)) { |
209 | | gf_node_traverse(c3d->navigationInfo, tr_state); |
210 | | ret = 1; |
211 | | } |
212 | | btop = (GF_Node*)gf_list_get(tr_state->navigations, 0); |
213 | | if (btop != c3d->navigationInfo) { |
214 | | gf_node_unregister(c3d->navigationInfo, n); |
215 | | gf_node_register(btop, n); |
216 | | c3d->navigationInfo = btop; |
217 | | gf_node_event_out(n, 7/*"navigationInfo"*/); |
218 | | ret = 1; |
219 | | } |
220 | | return ret; |
221 | | } |
222 | | #endif |
223 | 0 | case TAG_MPEG4_CompositeTexture2D: |
224 | 0 | { |
225 | 0 | M_CompositeTexture2D *c2d = (M_CompositeTexture2D*)n; |
226 | 0 | if (force_check || gf_node_dirty_get(c2d->background)) { |
227 | 0 | gf_node_traverse(c2d->background, tr_state); |
228 | 0 | ret = 1; |
229 | 0 | } |
230 | 0 | btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); |
231 | 0 | if (btop != c2d->background) { |
232 | 0 | gf_node_unregister(c2d->background, n); |
233 | 0 | gf_node_register(btop, n); |
234 | 0 | c2d->background = btop; |
235 | 0 | gf_node_event_out(n, 5/*"background"*/); |
236 | 0 | ret = 1; |
237 | 0 | } |
238 | |
|
239 | 0 | if (force_check || gf_node_dirty_get(c2d->viewport)) { |
240 | 0 | gf_node_traverse(c2d->viewport, tr_state); |
241 | 0 | ret = 1; |
242 | 0 | } |
243 | 0 | btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); |
244 | 0 | if (btop != c2d->viewport) { |
245 | 0 | gf_node_unregister(c2d->viewport, n); |
246 | 0 | gf_node_register(btop, n); |
247 | 0 | c2d->viewport = btop; |
248 | 0 | gf_node_event_out(n, 6/*"viewport"*/); |
249 | 0 | ret = 1; |
250 | 0 | } |
251 | |
|
252 | 0 | return ret; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | return 0; |
256 | 0 | } |
257 | | |
258 | | static void composite_update(GF_TextureHandler *txh) |
259 | 0 | { |
260 | 0 | s32 w, h; |
261 | 0 | GF_EVGStencil *stencil; |
262 | 0 | #ifndef GPAC_USE_GLES1X |
263 | 0 | M_Background2D *back; |
264 | 0 | #endif |
265 | 0 | GF_List *sensor_bck; |
266 | 0 | Bool invalidate_all; |
267 | 0 | u32 new_pixel_format; |
268 | 0 | GF_Compositor *compositor = (GF_Compositor *)txh->compositor; |
269 | 0 | CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(txh->owner); |
270 | |
|
271 | 0 | if (st->unsupported) return; |
272 | | |
273 | | /* |
274 | | if (compositor->recompute_ar) { |
275 | | gf_node_dirty_set(txh->owner, 0, 0); |
276 | | return; |
277 | | } |
278 | | */ |
279 | 0 | if (!compositor->rebuild_offscreen_textures && (!compositor->text_edit_changed || !st->visual->has_text_edit ) && !gf_node_dirty_get(txh->owner)) { |
280 | 0 | txh->needs_refresh = 0; |
281 | 0 | return; |
282 | 0 | } |
283 | 0 | gf_node_dirty_clear(st->txh.owner, 0); |
284 | | |
285 | | /*in OpenGL_ES, only RGBA can be safelly used with glReadPixels*/ |
286 | | #if defined(GPAC_USE_GLES1X) |
287 | | new_pixel_format = GF_PIXEL_RGBA; |
288 | | |
289 | | #else |
290 | |
|
291 | 0 | back = gf_list_get(st->visual->back_stack, 0); |
292 | 0 | if (back && back->isBound) new_pixel_format = GF_PIXEL_RGB; |
293 | 0 | else new_pixel_format = GF_PIXEL_RGBA; |
294 | |
|
295 | | #ifdef GPAC_USE_TINYGL |
296 | | /*TinyGL pixel format is fixed at compile time, we cannot override it !*/ |
297 | | if (st->visual->type_3d) new_pixel_format = GF_PIXEL_RGBA; |
298 | | |
299 | | #elif !defined(GPAC_DISABLE_3D) |
300 | | /*no alpha support in offscreen rendering*/ |
301 | | if (!compositor->visual->type_3d && !compositor->hybrid_opengl && !compositor->fbo_id && (st->visual->type_3d) && !(compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA)) |
302 | | new_pixel_format = GF_PIXEL_RGB; |
303 | | #endif |
304 | |
|
305 | 0 | #endif //GPAC_USE_GLES1X |
306 | | |
307 | | /*FIXME - we assume RGB+Depth+bitshape, we should check with the video out module*/ |
308 | | #if defined(GF_SR_USE_DEPTH) && !defined(GPAC_DISABLE_3D) |
309 | | if (st->visual->type_3d && (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_DEPTH) ) new_pixel_format = GF_PIXEL_RGBDS; |
310 | | #endif |
311 | | |
312 | |
|
313 | | #ifndef GPAC_DISABLE_3D |
314 | | if (st->visual->type_3d>1) { |
315 | | w = ((M_CompositeTexture3D*)txh->owner)->pixelWidth; |
316 | | h = ((M_CompositeTexture3D*)txh->owner)->pixelHeight; |
317 | | } else |
318 | | #endif |
319 | 0 | { |
320 | 0 | w = ((M_CompositeTexture2D*)txh->owner)->pixelWidth; |
321 | 0 | h = ((M_CompositeTexture2D*)txh->owner)->pixelHeight; |
322 | 0 | } |
323 | | |
324 | | /*internal GPAC hacks for testing color spaces*/ |
325 | 0 | if (w<-1) { |
326 | 0 | w = -w; |
327 | 0 | if (h<0) { |
328 | 0 | h = -h; |
329 | 0 | if (new_pixel_format==GF_PIXEL_RGBA) { |
330 | 0 | new_pixel_format=GF_PIXEL_ARGB; |
331 | 0 | } else { |
332 | 0 | new_pixel_format=GF_PIXEL_BGR; |
333 | 0 | } |
334 | 0 | } else { |
335 | 0 | if (new_pixel_format==GF_PIXEL_RGB) { |
336 | 0 | new_pixel_format=GF_PIXEL_RGBX; |
337 | 0 | } |
338 | 0 | } |
339 | 0 | } |
340 | 0 | else if (h<-1) { |
341 | 0 | h = -h; |
342 | 0 | if (new_pixel_format==GF_PIXEL_RGB) { |
343 | 0 | new_pixel_format=GF_PIXEL_RGBX; |
344 | 0 | } |
345 | 0 | } |
346 | |
|
347 | 0 | if (w<0) w = 0; |
348 | 0 | if (h<0) h = 0; |
349 | | |
350 | |
|
351 | 0 | if (!w || !h) { |
352 | 0 | if (txh->tx_io) { |
353 | | #ifdef GPAC_USE_TINYGL |
354 | | if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); |
355 | | #endif |
356 | 0 | gf_sc_texture_release(txh); |
357 | 0 | if (txh->data) gf_free(txh->data); |
358 | 0 | txh->data = NULL; |
359 | 0 | txh->width = txh->height = txh->stride = 0; |
360 | 0 | } |
361 | 0 | return; |
362 | 0 | } |
363 | 0 | invalidate_all = compositor->rebuild_offscreen_textures; |
364 | | |
365 | | /*rebuild stencil*/ |
366 | 0 | if (!txh->tx_io |
367 | 0 | || (w != (s32) txh->width) || ( h != (s32) txh->height) |
368 | 0 | || (new_pixel_format != txh->pixelformat) |
369 | 0 | ) { |
370 | |
|
371 | 0 | Bool needs_stencil = 1; |
372 | 0 | if (txh->tx_io) { |
373 | | #ifdef GPAC_USE_TINYGL |
374 | | if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); |
375 | | #endif |
376 | 0 | gf_sc_texture_release(txh); |
377 | 0 | if (txh->data) |
378 | 0 | gf_free(txh->data); |
379 | 0 | txh->data = NULL; |
380 | 0 | } |
381 | | |
382 | | /*we don't use rect ext because of no support for texture transforms*/ |
383 | 0 | if ((1) |
384 | | #ifndef GPAC_DISABLE_3D |
385 | | || compositor->gl_caps.npot_texture |
386 | | #endif |
387 | 0 | ) { |
388 | 0 | st->txh.width = w; |
389 | 0 | st->txh.height = h; |
390 | 0 | st->sx = st->sy = FIX_ONE; |
391 | 0 | } else { |
392 | 0 | st->txh.width = 2; |
393 | 0 | while (st->txh.width<(u32)w) st->txh.width*=2; |
394 | 0 | st->txh.height = 2; |
395 | 0 | while (st->txh.height<(u32)h) st->txh.height*=2; |
396 | |
|
397 | 0 | st->sx = INT2FIX(st->txh.width) / w; |
398 | 0 | st->sy = INT2FIX(st->txh.height) / h; |
399 | 0 | } |
400 | |
|
401 | 0 | gf_sc_texture_allocate(txh); |
402 | 0 | txh->pixelformat = new_pixel_format; |
403 | 0 | switch (new_pixel_format) { |
404 | 0 | case GF_PIXEL_RGBA: |
405 | 0 | case GF_PIXEL_ARGB: |
406 | 0 | txh->stride = txh->width * 4; |
407 | 0 | txh->transparent = 1; |
408 | 0 | break; |
409 | 0 | case GF_PIXEL_RGB_565: |
410 | 0 | txh->stride = txh->width * 2; |
411 | 0 | txh->transparent = 0; |
412 | 0 | break; |
413 | 0 | case GF_PIXEL_RGBDS: |
414 | 0 | txh->stride = txh->width * 4; |
415 | 0 | txh->transparent = 1; |
416 | 0 | break; |
417 | 0 | case GF_PIXEL_RGB: |
418 | 0 | txh->stride = txh->width * 3; |
419 | 0 | txh->transparent = 0; |
420 | 0 | break; |
421 | 0 | } |
422 | | |
423 | 0 | st->visual->width = txh->width; |
424 | 0 | st->visual->height = txh->height; |
425 | 0 | st->use_fbo = GF_FALSE; |
426 | |
|
427 | 0 | stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE); |
428 | |
|
429 | | #ifndef GPAC_DISABLE_3D |
430 | | if (st->visual->type_3d) { |
431 | | /*figure out what to do if main visual (eg video out) is not in OpenGL ...*/ |
432 | | if (!compositor->visual->type_3d && !compositor->hybrid_opengl) { |
433 | | /*create an offscreen window for OpenGL rendering*/ |
434 | | if (!compositor->fbo_id |
435 | | && ((compositor->offscreen_width < st->txh.width) || (compositor->offscreen_height < st->txh.height)) |
436 | | ) { |
437 | | |
438 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Offscreen OpenGL is not possible if no OpenGL context is created - use hybridGL mode for compositor\n")); |
439 | | st->unsupported = GF_TRUE; |
440 | | } |
441 | | } else { |
442 | | needs_stencil = 0; |
443 | | } |
444 | | } |
445 | | #endif |
446 | |
|
447 | 0 | if (needs_stencil) { |
448 | 0 | txh->data = (char*)gf_malloc(sizeof(unsigned char) * txh->stride * txh->height); |
449 | 0 | memset(txh->data, 0, sizeof(unsigned char) * txh->stride * txh->height); |
450 | | |
451 | | /*set stencil texture - we don't check error as an image could not be supported by the rasterizer |
452 | | but still supported by the blitter (case of RGBD/RGBDS)*/ |
453 | 0 | gf_evg_stencil_set_texture(stencil, txh->data, txh->width, txh->height, txh->stride, txh->pixelformat); |
454 | |
|
455 | | #ifdef GPAC_USE_TINYGL |
456 | | if (st->visual->type_3d && !compositor->visual->type_3d) { |
457 | | st->tgl_ctx = ostgl_create_context(txh->width, txh->height, txh->transparent ? 32 : 24, &txh->data, 1); |
458 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Creating TinyGL Offscreen context %p (%d %d - pf %s)\n", st->tgl_ctx, txh->width, txh->width, gf_4cc_to_str(txh->pixelformat))); |
459 | | } |
460 | | #endif |
461 | 0 | } |
462 | | #if !defined(GPAC_USE_TINYGL) && !defined(GPAC_DISABLE_3D) |
463 | | else if (compositor->gl_caps.fbo) { |
464 | | if (gf_sc_texture_setup_fbo(&st->txh)==GF_OK) |
465 | | st->use_fbo = GF_TRUE; |
466 | | } |
467 | | #endif |
468 | |
|
469 | 0 | invalidate_all = 1; |
470 | 0 | gf_sc_texture_set_stencil(txh, stencil); |
471 | 0 | } |
472 | 0 | if (!txh->tx_io) return; |
473 | | |
474 | | #ifndef GPAC_DISABLE_3D |
475 | | if (st->visual->camera.is_3D) { |
476 | | #ifdef GPAC_USE_TINYGL |
477 | | st->visual->type_3d = 2; |
478 | | #else |
479 | | if (compositor->visual->type_3d) { |
480 | | st->visual->type_3d = 2; |
481 | | } else if (compositor->hybrid_opengl || compositor->fbo_id) { |
482 | | st->visual->type_3d = 2; |
483 | | } else if (! (compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN)) { |
484 | | st->visual->type_3d = 0; |
485 | | } else { |
486 | | st->visual->type_3d = 2; |
487 | | } |
488 | | #endif |
489 | | } |
490 | | #endif |
491 | | |
492 | 0 | stencil = gf_sc_texture_get_stencil(txh); |
493 | 0 | if (!stencil) return; |
494 | | |
495 | | #ifdef GPAC_USE_TINYGL |
496 | | if (st->tgl_ctx) |
497 | | ostgl_make_current(st->tgl_ctx, 0); |
498 | | #elif !defined(GPAC_DISABLE_3D) |
499 | | if (st->use_fbo) { |
500 | | gf_sc_texture_enable_fbo(&st->txh, GF_TRUE); |
501 | | } |
502 | | #endif |
503 | | |
504 | 0 | sensor_bck = st->tr_state->vrml_sensors; |
505 | 0 | memset(st->tr_state, 0, sizeof(GF_TraverseState)); |
506 | 0 | st->tr_state->vrml_sensors = sensor_bck; |
507 | 0 | st->tr_state->visual = st->visual; |
508 | | #ifndef GPAC_DISABLE_3D |
509 | | st->tr_state->camera = &st->visual->camera; |
510 | | #endif |
511 | 0 | st->tr_state->invalidate_all = invalidate_all; |
512 | |
|
513 | 0 | st->tr_state->immediate_draw = st->visual->compositor->traverse_state->immediate_draw; |
514 | |
|
515 | 0 | gf_mx2d_init(st->tr_state->transform); |
516 | 0 | gf_cmx_init(&st->tr_state->color_mat); |
517 | |
|
518 | 0 | st->tr_state->backgrounds = st->visual->back_stack; |
519 | 0 | st->tr_state->viewpoints = st->visual->view_stack; |
520 | 0 | st->tr_state->pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(st->txh.owner)); |
521 | 0 | st->tr_state->min_hsize = INT2FIX( MIN(txh->width, txh->height) ) / 2; |
522 | 0 | st->tr_state->vp_size.x = INT2FIX(txh->width); |
523 | 0 | st->tr_state->vp_size.y = INT2FIX(txh->height); |
524 | |
|
525 | 0 | composite_do_bindable(st->txh.owner, st->tr_state, st->first); |
526 | 0 | st->first = 0; |
527 | |
|
528 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Entering draw cycle\n")); |
529 | |
|
530 | 0 | txh->needs_refresh = visual_draw_frame(st->visual, st->txh.owner, st->tr_state, 0); |
531 | 0 | txh->transparent = (st->visual->last_had_back==2) ? 0 : 1; |
532 | |
|
533 | 0 | if (!compositor->edited_text && st->visual->has_text_edit) |
534 | 0 | st->visual->has_text_edit = 0; |
535 | | |
536 | |
|
537 | | #if 0 |
538 | | /*set active viewport in image coordinates top-left=(0, 0), not in BIFS*/ |
539 | | if (gf_list_count(st->visual->view_stack)) { |
540 | | M_Viewport *vp = (M_Viewport *)gf_list_get(st->visual->view_stack, 0); |
541 | | |
542 | | if (vp->isBound) { |
543 | | SFVec2f size = vp->size; |
544 | | if (size.x >=0 && size.y>=0) { |
545 | | /*FIXME - we need tracking of VP changes*/ |
546 | | txh->needs_refresh = 1; |
547 | | } |
548 | | } |
549 | | } |
550 | | #endif |
551 | |
|
552 | 0 | if (txh->needs_refresh) { |
553 | | #ifndef GPAC_DISABLE_3D |
554 | | |
555 | | #ifndef GPAC_USE_TINYGL |
556 | | if (st->use_fbo) { |
557 | | gf_sc_texture_enable_fbo(&st->txh, GF_FALSE); |
558 | | } else |
559 | | #endif |
560 | | //no FBO, for composite 3D, store current buffer to texture |
561 | | if (st->visual->camera.is_3D && (st->visual->compositor->visual->type_3d || st->visual->compositor->hybrid_opengl)) { |
562 | | #ifndef GPAC_USE_TINYGL |
563 | | gf_sc_copy_to_texture(&st->txh); |
564 | | #endif |
565 | | } |
566 | | else if (st->visual->camera.is_3D) { |
567 | | if (st->visual->compositor->visual->type_3d) { |
568 | | #ifndef GPAC_USE_TINYGL |
569 | | gf_sc_copy_to_texture(&st->txh); |
570 | | #else |
571 | | /*in TinyGL we only need to push associated bitmap to the texture*/ |
572 | | gf_sc_texture_push_image(&st->txh, 0, 0); |
573 | | #endif |
574 | | } else { |
575 | | #ifndef GPAC_USE_TINYGL |
576 | | gf_sc_copy_to_stencil(&st->txh); |
577 | | #else |
578 | | if (txh->pixelformat==GF_PIXEL_RGBDS) |
579 | | gf_get_tinygl_depth(&st->txh); |
580 | | #endif |
581 | | } |
582 | | } else |
583 | | #endif |
584 | 0 | { |
585 | 0 | gf_sc_texture_set_stencil(txh, stencil); |
586 | 0 | } |
587 | 0 | gf_sc_invalidate(st->txh.compositor, NULL); |
588 | 0 | } |
589 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Leaving draw cycle\n")); |
590 | 0 | } |
591 | | |
592 | | |
593 | | GF_Err composite_get_video_access(GF_VisualManager *visual) |
594 | 0 | { |
595 | 0 | GF_EVGStencil *stencil; |
596 | 0 | GF_Err e; |
597 | 0 | CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(visual->offscreen); |
598 | |
|
599 | 0 | if (!st->txh.tx_io || !visual->raster_surface) return GF_BAD_PARAM; |
600 | 0 | stencil = gf_sc_texture_get_stencil(&st->txh); |
601 | 0 | if (!stencil) return GF_BAD_PARAM; |
602 | 0 | e = gf_evg_surface_attach_to_texture(visual->raster_surface, stencil); |
603 | 0 | if (!e) visual->is_attached = 1; |
604 | 0 | return e; |
605 | 0 | } |
606 | | |
607 | | void composite_release_video_access(GF_VisualManager *visual) |
608 | 0 | { |
609 | 0 | } |
610 | | |
611 | | Bool composite_check_visual_attached(GF_VisualManager *visual) |
612 | 0 | { |
613 | 0 | return visual->is_attached; |
614 | 0 | } |
615 | | |
616 | | void compositor_init_compositetexture2d(GF_Compositor *compositor, GF_Node *node) |
617 | 0 | { |
618 | 0 | M_CompositeTexture2D *c2d = (M_CompositeTexture2D *)node; |
619 | 0 | CompositeTextureStack *st; |
620 | 0 | GF_SAFEALLOC(st, CompositeTextureStack); |
621 | |
|
622 | 0 | if (!st) { |
623 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate composite texture stack\n")); |
624 | 0 | return; |
625 | 0 | } |
626 | | |
627 | 0 | GF_SAFEALLOC(st->tr_state, GF_TraverseState); |
628 | 0 | if (!st->tr_state) { |
629 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate composite texture state\n")); |
630 | 0 | return; |
631 | 0 | } |
632 | 0 | st->tr_state->vrml_sensors = gf_list_new(); |
633 | |
|
634 | 0 | st->sensors = gf_list_new(); |
635 | 0 | st->previous_sensors = gf_list_new(); |
636 | 0 | gf_sc_texture_setup(&st->txh, compositor, node); |
637 | | /*remove texture from compositor and add it at the end, so that any sub-textures are handled before*/ |
638 | 0 | gf_list_del_item(compositor->textures, &st->txh); |
639 | 0 | gf_list_add(compositor->textures, &st->txh); |
640 | |
|
641 | 0 | st->txh.update_texture_fcnt = composite_update; |
642 | |
|
643 | 0 | if ((c2d->repeatSandT==1) || (c2d->repeatSandT==3) ) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S; |
644 | 0 | if (c2d->repeatSandT>1) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T; |
645 | | |
646 | | /*create composite visual*/ |
647 | 0 | st->visual = visual_new(compositor); |
648 | 0 | st->visual->offscreen = node; |
649 | 0 | st->visual->GetSurfaceAccess = composite_get_video_access; |
650 | 0 | st->visual->ReleaseSurfaceAccess = composite_release_video_access; |
651 | 0 | st->visual->DrawBitmap = composite2d_draw_bitmap; |
652 | 0 | st->visual->CheckAttached = composite_check_visual_attached; |
653 | |
|
654 | 0 | st->visual->raster_surface = gf_evg_surface_new(1); |
655 | | |
656 | |
|
657 | 0 | st->first = 1; |
658 | 0 | st->visual->compositor = compositor; |
659 | 0 | gf_node_set_private(node, st); |
660 | 0 | gf_node_set_callback_function(node, composite_traverse); |
661 | 0 | gf_sc_visual_register(compositor, st->visual); |
662 | 0 | } |
663 | | |
664 | | |
665 | | #ifndef GPAC_DISABLE_3D |
666 | | void compositor_init_compositetexture3d(GF_Compositor *compositor, GF_Node *node) |
667 | | { |
668 | | M_CompositeTexture3D *c3d = (M_CompositeTexture3D *)node; |
669 | | CompositeTextureStack *st; |
670 | | |
671 | | if (!gf_sc_check_gl_support(compositor)) { |
672 | | GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] Driver disabled, cannot render 3D composite textures\n")); |
673 | | return; |
674 | | } |
675 | | |
676 | | GF_SAFEALLOC(st, CompositeTextureStack); |
677 | | if (!st) { |
678 | | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate composite texture stack\n")); |
679 | | return; |
680 | | } |
681 | | GF_SAFEALLOC(st->tr_state, GF_TraverseState); |
682 | | if (!st->tr_state) { |
683 | | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate composite texture state\n")); |
684 | | return; |
685 | | } |
686 | | |
687 | | st->tr_state->vrml_sensors = gf_list_new(); |
688 | | |
689 | | st->sensors = gf_list_new(); |
690 | | st->previous_sensors = gf_list_new(); |
691 | | gf_sc_texture_setup(&st->txh, compositor, node); |
692 | | /*remove texture from compositor and add it at the end, so that any sub-textures are handled before*/ |
693 | | gf_list_del_item(compositor->textures, &st->txh); |
694 | | gf_list_add(compositor->textures, &st->txh); |
695 | | |
696 | | st->txh.update_texture_fcnt = composite_update; |
697 | | |
698 | | if (c3d->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S; |
699 | | if (c3d->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T; |
700 | | |
701 | | /*create composite visual*/ |
702 | | st->visual = visual_new(compositor); |
703 | | st->visual->offscreen = node; |
704 | | st->visual->GetSurfaceAccess = composite_get_video_access; |
705 | | st->visual->ReleaseSurfaceAccess = composite_release_video_access; |
706 | | st->visual->CheckAttached = composite_check_visual_attached; |
707 | | |
708 | | st->visual->camera.is_3D = 1; |
709 | | st->first = 1; |
710 | | st->visual->compositor = compositor; |
711 | | gf_node_set_private(node, st); |
712 | | gf_node_set_callback_function(node, composite_traverse); |
713 | | gf_sc_visual_register(compositor, st->visual); |
714 | | |
715 | | camera_invalidate(&st->visual->camera); |
716 | | } |
717 | | #endif |
718 | | |
719 | | GF_TextureHandler *compositor_get_composite_texture(GF_Node *node) |
720 | 0 | { |
721 | 0 | CompositeTextureStack *st = (CompositeTextureStack*) gf_node_get_private(node); |
722 | 0 | return &st->txh; |
723 | 0 | } |
724 | | |
725 | | Bool compositor_compositetexture_handle_event(GF_Compositor *compositor, GF_Node *composite_appear, GF_Event *ev, Bool is_flush) |
726 | 0 | { |
727 | 0 | GF_Ray ray; |
728 | 0 | Fixed dist; |
729 | 0 | Bool had_text_sel=0; |
730 | 0 | GF_Matrix mx; |
731 | 0 | GF_ChildNodeItem *children, *l; |
732 | 0 | Bool res; |
733 | 0 | SFVec3f txcoord, loc_pt, world_pt; |
734 | 0 | GF_Matrix l2w_mx, w2l_mx; |
735 | 0 | CompositeTextureStack *stack; |
736 | 0 | GF_Node *appear, *prev_appear; |
737 | 0 | GF_List *sensor_bck; |
738 | 0 | M_Appearance *ap = (M_Appearance *)composite_appear; |
739 | 0 | if (!ap || !ap->texture) return 0; |
740 | | |
741 | 0 | if (ev->type > GF_EVENT_MOUSEMOVE) return 0; |
742 | 0 | stack = gf_node_get_private(ap->texture); |
743 | 0 | if (!stack->txh.tx_io) return 0; |
744 | | |
745 | 0 | if (stack->in_handle_event) |
746 | 0 | return 0; |
747 | | |
748 | 0 | children = NULL; |
749 | 0 | stack->in_handle_event = GF_TRUE; |
750 | |
|
751 | 0 | if (!is_flush) { |
752 | 0 | txcoord.x = compositor->hit_texcoords.x; |
753 | 0 | txcoord.y = compositor->hit_texcoords.y; |
754 | 0 | txcoord.z = 0; |
755 | 0 | if (gf_sc_texture_get_transform(&stack->txh, ap->textureTransform, &mx, 1)) { |
756 | | /*tx coords are inverted when mapping, thus applying directly the matrix will give us the |
757 | | untransformed coords*/ |
758 | 0 | gf_mx_apply_vec(&mx, &txcoord); |
759 | 0 | while (txcoord.x<0) txcoord.x += FIX_ONE; |
760 | 0 | while (txcoord.x>FIX_ONE) txcoord.x -= FIX_ONE; |
761 | 0 | while (txcoord.y<0) txcoord.y += FIX_ONE; |
762 | 0 | while (txcoord.y>FIX_ONE) txcoord.y -= FIX_ONE; |
763 | 0 | } |
764 | | |
765 | | /*convert to tx space*/ |
766 | 0 | ev->mouse.x = FIX2INT( (txcoord.x - FIX_ONE/2) * stack->visual->width + FIX_ONE/2); |
767 | 0 | ev->mouse.y = FIX2INT( (txcoord.y - FIX_ONE/2) * stack->visual->height + FIX_ONE/2); |
768 | |
|
769 | 0 | sensor_bck = stack->tr_state->vrml_sensors; |
770 | 0 | memset(stack->tr_state, 0, sizeof(GF_TraverseState)); |
771 | 0 | stack->tr_state->vrml_sensors = sensor_bck; |
772 | |
|
773 | 0 | stack->tr_state->visual = stack->visual; |
774 | 0 | stack->tr_state->traversing_mode = TRAVERSE_PICK; |
775 | 0 | stack->tr_state->pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(ap->texture)); |
776 | 0 | stack->tr_state->vp_size.x = INT2FIX(stack->txh.width); |
777 | 0 | stack->tr_state->vp_size.y = INT2FIX(stack->txh.height); |
778 | 0 | stack->tr_state->color_mat.identity = 1; |
779 | |
|
780 | 0 | gf_mx2d_init(stack->tr_state->transform); |
781 | | #ifndef GPAC_DISABLE_3D |
782 | | gf_mx_init(stack->tr_state->model_matrix); |
783 | | #endif |
784 | | /*collect sensors but not anchors*/ |
785 | 0 | l = children = ((M_CompositeTexture2D*)ap->texture)->children; |
786 | 0 | while (l) { |
787 | 0 | GF_SensorHandler *hsens = compositor_mpeg4_get_sensor_handler_ex(l->node, GF_TRUE); |
788 | 0 | if (hsens) gf_list_add(stack->tr_state->vrml_sensors, hsens); |
789 | 0 | l = l->next; |
790 | 0 | } |
791 | 0 | } |
792 | |
|
793 | 0 | stack->temp_sensors = compositor->sensors; |
794 | 0 | stack->temp_previous_sensors = compositor->previous_sensors; |
795 | 0 | compositor->sensors = stack->sensors; |
796 | 0 | compositor->previous_sensors = stack->previous_sensors; |
797 | |
|
798 | 0 | ray = compositor->hit_world_ray; |
799 | 0 | dist = compositor->hit_square_dist; |
800 | 0 | prev_appear = compositor->prev_hit_appear; |
801 | | |
802 | | /*protect against destrucion in case of self-destroy*/ |
803 | 0 | if (prev_appear) { |
804 | 0 | gf_node_register(prev_appear, NULL); |
805 | 0 | } |
806 | 0 | compositor->prev_hit_appear = stack->prev_hit_appear; |
807 | 0 | appear = compositor->hit_appear; |
808 | 0 | compositor->hit_appear = NULL; |
809 | | |
810 | | /*also backup current hit state in case we hit a node in the texture but don't consume the event*/ |
811 | 0 | loc_pt = compositor->hit_local_point; |
812 | 0 | world_pt = compositor->hit_world_point; |
813 | 0 | gf_mx_copy(l2w_mx, compositor->hit_local_to_world); |
814 | 0 | gf_mx_copy(w2l_mx, compositor->hit_world_to_local); |
815 | |
|
816 | 0 | if (compositor->text_selection) had_text_sel=1; |
817 | |
|
818 | 0 | if (is_flush) { |
819 | 0 | res = 0; |
820 | 0 | gf_list_reset(stack->sensors); |
821 | 0 | gf_sc_exec_event_vrml(compositor, ev); |
822 | 0 | } else { |
823 | 0 | res = visual_execute_event(stack->visual, stack->tr_state, ev, children); |
824 | 0 | } |
825 | |
|
826 | 0 | if (!had_text_sel && compositor->edited_text) { |
827 | 0 | stack->visual->has_text_edit = 1; |
828 | 0 | } else if (!compositor->text_selection) { |
829 | 0 | stack->visual->has_text_edit = 0; |
830 | 0 | } |
831 | |
|
832 | 0 | if (!res) { |
833 | 0 | compositor->hit_local_point = loc_pt; |
834 | 0 | gf_mx_copy(compositor->hit_local_to_world, l2w_mx); |
835 | 0 | gf_mx_copy(compositor->hit_world_to_local, w2l_mx); |
836 | 0 | } |
837 | |
|
838 | 0 | compositor->hit_world_point = world_pt; |
839 | 0 | compositor->hit_world_ray = ray; |
840 | 0 | compositor->hit_square_dist = dist; |
841 | |
|
842 | 0 | stack->sensors = compositor->sensors; |
843 | 0 | stack->previous_sensors = compositor->previous_sensors; |
844 | 0 | compositor->sensors = stack->temp_sensors; |
845 | 0 | stack->temp_sensors = NULL; |
846 | 0 | compositor->previous_sensors = stack->temp_previous_sensors; |
847 | 0 | stack->temp_previous_sensors = NULL; |
848 | | |
849 | |
|
850 | 0 | if (!is_flush) { |
851 | | #ifndef GPAC_DISABLE_3D |
852 | | if (stack->tr_state->layer3d) compositor->traverse_state->layer3d = stack->tr_state->layer3d; |
853 | | #endif |
854 | 0 | } |
855 | |
|
856 | 0 | stack->prev_hit_appear = compositor->prev_hit_appear; |
857 | | |
858 | | //finally unregister the node, this may destroy the stack ! |
859 | 0 | if (prev_appear) { |
860 | 0 | if (prev_appear->sgprivate->num_instances>1) { |
861 | 0 | compositor->prev_hit_appear = prev_appear; |
862 | 0 | compositor->hit_appear = appear; |
863 | 0 | } else { |
864 | 0 | compositor->prev_hit_appear = NULL; |
865 | 0 | compositor->hit_appear = NULL; |
866 | 0 | } |
867 | 0 | gf_node_unregister(prev_appear, NULL); |
868 | 0 | } else { |
869 | 0 | compositor->prev_hit_appear = prev_appear; |
870 | 0 | compositor->hit_appear = appear; |
871 | 0 | } |
872 | 0 | stack->in_handle_event = GF_FALSE; |
873 | |
|
874 | 0 | return res; |
875 | 0 | } |
876 | | |
877 | | void compositor_compositetexture_sensor_delete(GF_Node *composite_appear, GF_SensorHandler *hdl) |
878 | 0 | { |
879 | 0 | CompositeTextureStack *stack = gf_node_get_private(composite_appear); |
880 | 0 | gf_list_del_item(stack->previous_sensors, hdl); |
881 | 0 | gf_list_del_item(stack->sensors, hdl); |
882 | 0 | if (stack->temp_sensors) |
883 | 0 | gf_list_del_item(stack->temp_sensors, hdl); |
884 | 0 | if (stack->temp_previous_sensors) |
885 | 0 | gf_list_del_item(stack->temp_previous_sensors, hdl); |
886 | 0 | } |
887 | | |
888 | | |
889 | | void compositor_adjust_scale(GF_Node *node, Fixed *sx, Fixed *sy) |
890 | 0 | { |
891 | 0 | switch (gf_node_get_tag(node)) { |
892 | 0 | case TAG_MPEG4_CompositeTexture2D: |
893 | 0 | case TAG_MPEG4_CompositeTexture3D: |
894 | 0 | { |
895 | 0 | CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(node); |
896 | 0 | (*sx) = gf_divfix(*sx, st->sx); |
897 | 0 | (*sy) = gf_divfix(*sy, st->sy); |
898 | 0 | break; |
899 | 0 | } |
900 | 0 | default: |
901 | 0 | return; |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | | Bool compositor_is_composite_texture(GF_Node *appear) |
906 | 0 | { |
907 | 0 | M_Appearance *ap = NULL; |
908 | 0 | u32 tag; |
909 | 0 | if (!appear) return 0; |
910 | | |
911 | 0 | tag = gf_node_get_tag(appear); |
912 | 0 | if (tag==TAG_MPEG4_Appearance) ap = (M_Appearance *)appear; |
913 | 0 | #ifndef GPAC_DISABLE_X3D |
914 | 0 | else if (tag==TAG_X3D_Appearance) ap = (M_Appearance *)appear; |
915 | 0 | #endif |
916 | 0 | if (!ap) return 0; |
917 | 0 | if (!ap->texture) return 0; |
918 | 0 | switch (gf_node_get_tag(((M_Appearance *)appear)->texture)) { |
919 | 0 | case TAG_MPEG4_CompositeTexture2D: |
920 | 0 | case TAG_MPEG4_CompositeTexture3D: |
921 | 0 | return 1; |
922 | 0 | } |
923 | 0 | return 0; |
924 | 0 | } |
925 | | |
926 | | #endif //!defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR) |