Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2012
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 : #include "nodes_stacks.h"
27 : #include "visual_manager.h"
28 :
29 : #include "offscreen_cache.h"
30 : #include "mpeg4_grouping.h"
31 :
32 : #include <gpac/modules/hardcoded_proto.h>
33 : #include "texturing.h"
34 :
35 :
36 : #define CHECK_FIELD(__name, __index, __type) \
37 : if (gf_node_get_field(node, __index, &field) != GF_OK) {\
38 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[HardcodedProtos] Cannot get field index %d\n", __index));\
39 : return GF_FALSE; \
40 : }\
41 : if (field.fieldType != __type) {\
42 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[HardcodedProtos] %s field idx %d (%s) is not of type %s\n", __name, field.fieldIndex, field.name, gf_sg_vrml_get_field_type_name(__type) ));\
43 : return GF_FALSE;\
44 : }
45 :
46 :
47 : #ifndef GPAC_DISABLE_VRML
48 :
49 : #ifndef GPAC_DISABLE_3D
50 :
51 :
52 : /*PathExtrusion hardcoded proto*/
53 :
54 : typedef struct
55 : {
56 : GF_Node *geometry;
57 : MFVec3f *spine;
58 : Bool beginCap;
59 : Bool endCap;
60 : Fixed creaseAngle;
61 : MFRotation *orientation;
62 : MFVec2f *scale;
63 : Bool txAlongSpine;
64 : } PathExtrusion;
65 :
66 :
67 12 : static Bool PathExtrusion_GetNode(GF_Node *node, PathExtrusion *path_ext)
68 : {
69 : GF_FieldInfo field;
70 : memset(path_ext, 0, sizeof(PathExtrusion));
71 :
72 12 : CHECK_FIELD("PathExtrusion", 0, GF_SG_VRML_SFNODE);
73 12 : path_ext->geometry = * (GF_Node **) field.far_ptr;
74 :
75 12 : CHECK_FIELD("PathExtrusion", 1, GF_SG_VRML_MFVEC3F);
76 12 : path_ext->spine = (MFVec3f *) field.far_ptr;
77 :
78 12 : CHECK_FIELD("PathExtrusion", 2, GF_SG_VRML_SFBOOL);
79 12 : path_ext->beginCap = *(SFBool *) field.far_ptr;
80 :
81 12 : CHECK_FIELD("PathExtrusion", 3, GF_SG_VRML_SFBOOL);
82 12 : path_ext->endCap = *(SFBool *) field.far_ptr;
83 :
84 12 : CHECK_FIELD("PathExtrusion", 4, GF_SG_VRML_SFFLOAT);
85 12 : path_ext->creaseAngle = *(SFFloat *) field.far_ptr;
86 :
87 12 : CHECK_FIELD("PathExtrusion", 5, GF_SG_VRML_MFROTATION);
88 12 : path_ext->orientation = (MFRotation *) field.far_ptr;
89 :
90 12 : CHECK_FIELD("PathExtrusion", 6, GF_SG_VRML_MFVEC2F);
91 12 : path_ext->scale = (MFVec2f *) field.far_ptr;
92 :
93 12 : CHECK_FIELD("PathExtrusion", 7, GF_SG_VRML_SFBOOL);
94 12 : path_ext->txAlongSpine = *(SFBool *) field.far_ptr;
95 12 : return GF_TRUE;
96 : }
97 :
98 13 : static void TraversePathExtrusion(GF_Node *node, void *rs, Bool is_destroy)
99 : {
100 : PathExtrusion path_ext;
101 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
102 13 : Drawable3D *stack = (Drawable3D *)gf_node_get_private(node);
103 :
104 13 : if (is_destroy) {
105 1 : drawable_3d_del(node);
106 1 : return;
107 : }
108 12 : if (!PathExtrusion_GetNode(node, &path_ext)) return;
109 12 : if (!path_ext.geometry) return;
110 :
111 :
112 12 : if (gf_node_dirty_get(node)) {
113 : Drawable *stack_2d;
114 1 : u32 mode = tr_state->traversing_mode;
115 1 : tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
116 1 : gf_node_traverse(path_ext.geometry, tr_state);
117 1 : tr_state->traversing_mode = mode;
118 :
119 1 : gf_node_dirty_clear(node, 0);
120 :
121 1 : switch (gf_node_get_tag(path_ext.geometry) ) {
122 0 : case TAG_MPEG4_Circle:
123 : case TAG_MPEG4_Ellipse:
124 : case TAG_MPEG4_Rectangle:
125 : case TAG_MPEG4_Curve2D:
126 : case TAG_MPEG4_XCurve2D:
127 : case TAG_MPEG4_IndexedFaceSet2D:
128 : case TAG_MPEG4_IndexedLineSet2D:
129 0 : stack_2d = (Drawable*)gf_node_get_private(path_ext.geometry);
130 0 : if (!stack_2d) return;
131 0 : mesh_extrude_path(stack->mesh, stack_2d->path, path_ext.spine, path_ext.creaseAngle, path_ext.beginCap, path_ext.endCap, path_ext.orientation, path_ext.scale, path_ext.txAlongSpine);
132 0 : break;
133 1 : case TAG_MPEG4_Text:
134 1 : compositor_extrude_text(path_ext.geometry, tr_state, stack->mesh, path_ext.spine, path_ext.creaseAngle, path_ext.beginCap, path_ext.endCap, path_ext.orientation, path_ext.scale, path_ext.txAlongSpine);
135 1 : break;
136 : }
137 : }
138 :
139 12 : if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) {
140 9 : visual_3d_draw(tr_state, stack->mesh);
141 3 : } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
142 3 : tr_state->bbox = stack->mesh->bounds;
143 : }
144 : }
145 :
146 : static void compositor_init_path_extrusion(GF_Compositor *compositor, GF_Node *node)
147 : {
148 1 : drawable_3d_new(node);
149 1 : gf_node_set_callback_function(node, TraversePathExtrusion);
150 : }
151 :
152 :
153 : /*PlanarExtrusion hardcoded proto*/
154 : typedef struct
155 : {
156 : GF_Node *geometry;
157 : GF_Node *spine;
158 : Bool beginCap;
159 : Bool endCap;
160 : Fixed creaseAngle;
161 : MFFloat *orientationKeys;
162 : MFRotation *orientation;
163 : MFFloat *scaleKeys;
164 : MFVec2f *scale;
165 : Bool txAlongSpine;
166 : } PlanarExtrusion;
167 :
168 5 : static Bool PlanarExtrusion_GetNode(GF_Node *node, PlanarExtrusion *path_ext)
169 : {
170 : GF_FieldInfo field;
171 : memset(path_ext, 0, sizeof(PathExtrusion));
172 :
173 5 : CHECK_FIELD("PlanarExtrusion", 0, GF_SG_VRML_SFNODE);
174 5 : path_ext->geometry = * (GF_Node **) field.far_ptr;
175 :
176 5 : CHECK_FIELD("PlanarExtrusion", 1, GF_SG_VRML_SFNODE);
177 5 : path_ext->spine = * (GF_Node **) field.far_ptr;
178 :
179 5 : CHECK_FIELD("PlanarExtrusion", 2, GF_SG_VRML_SFBOOL);
180 5 : path_ext->beginCap = *(SFBool *) field.far_ptr;
181 :
182 5 : CHECK_FIELD("PlanarExtrusion", 3, GF_SG_VRML_SFBOOL);
183 5 : path_ext->endCap = *(SFBool *) field.far_ptr;
184 :
185 5 : CHECK_FIELD("PlanarExtrusion", 4, GF_SG_VRML_SFFLOAT);
186 5 : path_ext->creaseAngle = *(SFFloat *) field.far_ptr;
187 :
188 5 : CHECK_FIELD("PlanarExtrusion", 5, GF_SG_VRML_MFFLOAT);
189 5 : path_ext->orientationKeys = (MFFloat *) field.far_ptr;
190 :
191 5 : CHECK_FIELD("PlanarExtrusion", 6, GF_SG_VRML_MFROTATION);
192 5 : path_ext->orientation = (MFRotation *) field.far_ptr;
193 :
194 5 : CHECK_FIELD("PlanarExtrusion", 7, GF_SG_VRML_MFFLOAT);
195 5 : path_ext->scaleKeys = (MFFloat *) field.far_ptr;
196 :
197 5 : CHECK_FIELD("PlanarExtrusion", 8, GF_SG_VRML_MFVEC2F);
198 5 : path_ext->scale = (MFVec2f *) field.far_ptr;
199 :
200 5 : CHECK_FIELD("PlanarExtrusion", 9, GF_SG_VRML_SFBOOL);
201 5 : path_ext->txAlongSpine = *(SFBool *) field.far_ptr;
202 :
203 5 : return GF_TRUE;
204 : }
205 :
206 6 : static void TraversePlanarExtrusion(GF_Node *node, void *rs, Bool is_destroy)
207 : {
208 : PlanarExtrusion plane_ext;
209 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
210 6 : Drawable3D *stack = (Drawable3D *)gf_node_get_private(node);
211 :
212 6 : if (is_destroy) {
213 1 : drawable_3d_del(node);
214 1 : return;
215 : }
216 :
217 5 : if (!PlanarExtrusion_GetNode(node, &plane_ext)) return;
218 5 : if (!plane_ext.geometry || !plane_ext.spine) return;
219 :
220 :
221 5 : if (gf_node_dirty_get(node)) {
222 : Drawable *stack_2d;
223 : u32 i, j, k;
224 : MFVec3f spine_vec;
225 : SFVec3f d;
226 : Fixed spine_len;
227 : GF_Rect bounds;
228 : u32 cur, nb_pts;
229 1 : u32 mode = tr_state->traversing_mode;
230 : GF_Path *geo, *spine;
231 : geo = spine = NULL;
232 :
233 1 : tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
234 1 : gf_node_traverse(plane_ext.geometry, tr_state);
235 1 : gf_node_traverse(plane_ext.spine, tr_state);
236 1 : tr_state->traversing_mode = mode;
237 1 : gf_node_dirty_clear(node, 0);
238 :
239 1 : switch (gf_node_get_tag(plane_ext.geometry) ) {
240 1 : case TAG_MPEG4_Circle:
241 : case TAG_MPEG4_Ellipse:
242 : case TAG_MPEG4_Rectangle:
243 : case TAG_MPEG4_Curve2D:
244 : case TAG_MPEG4_XCurve2D:
245 : case TAG_MPEG4_IndexedFaceSet2D:
246 : case TAG_MPEG4_IndexedLineSet2D:
247 1 : stack_2d = (Drawable*)gf_node_get_private(plane_ext.geometry);
248 1 : if (stack_2d) geo = stack_2d->path;
249 : break;
250 : default:
251 0 : return;
252 : }
253 1 : switch (gf_node_get_tag(plane_ext.spine) ) {
254 1 : case TAG_MPEG4_Circle:
255 : case TAG_MPEG4_Ellipse:
256 : case TAG_MPEG4_Rectangle:
257 : case TAG_MPEG4_Curve2D:
258 : case TAG_MPEG4_XCurve2D:
259 : case TAG_MPEG4_IndexedFaceSet2D:
260 : case TAG_MPEG4_IndexedLineSet2D:
261 1 : stack_2d = (Drawable*)gf_node_get_private(plane_ext.spine);
262 1 : if (stack_2d) spine = stack_2d->path;
263 : break;
264 : default:
265 : return;
266 : }
267 1 : if (!geo || !spine) return;
268 :
269 1 : mesh_reset(stack->mesh);
270 1 : gf_path_flatten(spine);
271 1 : gf_path_get_bounds(spine, &bounds);
272 1 : gf_path_flatten(geo);
273 1 : gf_path_get_bounds(geo, &bounds);
274 :
275 : cur = 0;
276 2 : for (i=0; i<spine->n_contours; i++) {
277 1 : nb_pts = 1 + spine->contours[i] - cur;
278 1 : spine_vec.vals = NULL;
279 1 : gf_sg_vrml_mf_alloc(&spine_vec, GF_SG_VRML_MFVEC3F, nb_pts);
280 : spine_len = 0;
281 66 : for (j=cur; j<nb_pts; j++) {
282 65 : spine_vec.vals[j].x = spine->points[j].x;
283 65 : spine_vec.vals[j].y = spine->points[j].y;
284 65 : spine_vec.vals[j].z = 0;
285 65 : if (j) {
286 64 : gf_vec_diff(d, spine_vec.vals[j], spine_vec.vals[j-1]);
287 64 : spine_len += gf_vec_len(d);
288 : }
289 : }
290 1 : cur += nb_pts;
291 1 : if (!plane_ext.orientation->count && !plane_ext.scale->count) {
292 2 : mesh_extrude_path_ext(stack->mesh, geo, &spine_vec, plane_ext.creaseAngle,
293 1 : bounds.x, bounds.y-bounds.height, bounds.width, bounds.height,
294 : plane_ext.beginCap, plane_ext.endCap, NULL, NULL, plane_ext.txAlongSpine);
295 : }
296 : /*interpolate orientation and scale along subpath line*/
297 : else {
298 : MFRotation ori;
299 : MFVec2f scale;
300 : Fixed cur_len, frac;
301 :
302 0 : ori.vals = NULL;
303 0 : gf_sg_vrml_mf_alloc(&ori, GF_SG_VRML_MFROTATION, nb_pts);
304 0 : scale.vals = NULL;
305 0 : gf_sg_vrml_mf_alloc(&scale, GF_SG_VRML_MFVEC2F, nb_pts);
306 : cur_len = 0;
307 0 : if (!plane_ext.orientation->count) ori.vals[0].y = FIX_ONE;
308 0 : if (!plane_ext.scale->count) scale.vals[0].x = scale.vals[0].y = FIX_ONE;
309 0 : for (j=0; j<nb_pts; j++) {
310 0 : if (j) {
311 0 : gf_vec_diff(d, spine_vec.vals[j], spine_vec.vals[j-1]);
312 0 : cur_len += gf_vec_len(d);
313 0 : ori.vals[j] = ori.vals[j-1];
314 0 : scale.vals[j] = scale.vals[j-1];
315 : }
316 :
317 0 : if (plane_ext.orientation->count && (plane_ext.orientation->count == plane_ext.orientationKeys->count)) {
318 :
319 0 : frac = gf_divfix(cur_len , spine_len);
320 0 : if (frac < plane_ext.orientationKeys->vals[0]) ori.vals[j] = plane_ext.orientation->vals[0];
321 0 : else if (frac >= plane_ext.orientationKeys->vals[plane_ext.orientationKeys->count-1]) ori.vals[j] = plane_ext.orientation->vals[plane_ext.orientationKeys->count-1];
322 : else {
323 0 : for (k=1; k<plane_ext.orientationKeys->count; k++) {
324 0 : Fixed kDiff = plane_ext.orientationKeys->vals[k] - plane_ext.orientationKeys->vals[k-1];
325 0 : if (!kDiff) continue;
326 0 : if (frac < plane_ext.orientationKeys->vals[k-1]) continue;
327 0 : if (frac > plane_ext.orientationKeys->vals[k]) continue;
328 0 : frac = gf_divfix(frac - plane_ext.orientationKeys->vals[k-1], kDiff);
329 0 : break;
330 : }
331 0 : ori.vals[j] = gf_sg_sfrotation_interpolate(plane_ext.orientation->vals[k-1], plane_ext.orientation->vals[k], frac);
332 : }
333 : }
334 :
335 0 : if (plane_ext.scale->count == plane_ext.scaleKeys->count) {
336 0 : frac = gf_divfix(cur_len , spine_len);
337 0 : if (frac <= plane_ext.scaleKeys->vals[0]) scale.vals[j] = plane_ext.scale->vals[0];
338 0 : else if (frac >= plane_ext.scaleKeys->vals[plane_ext.scaleKeys->count-1]) scale.vals[j] = plane_ext.scale->vals[plane_ext.scale->count-1];
339 : else {
340 0 : for (k=1; k<plane_ext.scaleKeys->count; k++) {
341 0 : Fixed kDiff = plane_ext.scaleKeys->vals[k] - plane_ext.scaleKeys->vals[k-1];
342 0 : if (!kDiff) continue;
343 0 : if (frac < plane_ext.scaleKeys->vals[k-1]) continue;
344 0 : if (frac > plane_ext.scaleKeys->vals[k]) continue;
345 0 : frac = gf_divfix(frac - plane_ext.scaleKeys->vals[k-1], kDiff);
346 0 : break;
347 : }
348 0 : scale.vals[j].x = gf_mulfix(plane_ext.scale->vals[k].x - plane_ext.scale->vals[k-1].x, frac) + plane_ext.scale->vals[k-1].x;
349 0 : scale.vals[j].y = gf_mulfix(plane_ext.scale->vals[k].y - plane_ext.scale->vals[k-1].y, frac) + plane_ext.scale->vals[k-1].y;
350 : }
351 : }
352 : }
353 :
354 0 : mesh_extrude_path_ext(stack->mesh, geo, &spine_vec, plane_ext.creaseAngle,
355 0 : bounds.x, bounds.y-bounds.height, bounds.width, bounds.height,
356 : plane_ext.beginCap, plane_ext.endCap, &ori, &scale, plane_ext.txAlongSpine);
357 :
358 0 : gf_sg_vrml_mf_reset(&ori, GF_SG_VRML_MFROTATION);
359 0 : gf_sg_vrml_mf_reset(&scale, GF_SG_VRML_MFVEC2F);
360 : }
361 :
362 1 : gf_sg_vrml_mf_reset(&spine_vec, GF_SG_VRML_MFVEC3F);
363 : }
364 1 : mesh_update_bounds(stack->mesh);
365 1 : gf_mesh_build_aabbtree(stack->mesh);
366 : }
367 :
368 5 : if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) {
369 3 : visual_3d_draw(tr_state, stack->mesh);
370 2 : } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
371 2 : tr_state->bbox = stack->mesh->bounds;
372 : }
373 : }
374 :
375 0 : void compositor_init_planar_extrusion(GF_Compositor *compositor, GF_Node *node)
376 : {
377 1 : drawable_3d_new(node);
378 1 : gf_node_set_callback_function(node, TraversePlanarExtrusion);
379 0 : }
380 :
381 : /*PlaneClipper hardcoded proto*/
382 : typedef struct
383 : {
384 : BASE_NODE
385 : CHILDREN
386 :
387 : GF_Plane plane;
388 : } PlaneClipper;
389 :
390 : typedef struct
391 : {
392 : GROUPING_NODE_STACK_3D
393 : PlaneClipper pc;
394 : } PlaneClipperStack;
395 :
396 349 : static Bool PlaneClipper_GetNode(GF_Node *node, PlaneClipper *pc)
397 : {
398 : GF_FieldInfo field;
399 : memset(pc, 0, sizeof(PlaneClipper));
400 349 : pc->sgprivate = node->sgprivate;
401 :
402 349 : CHECK_FIELD("PlaneClipper", 0, GF_SG_VRML_SFVEC3F);
403 349 : pc->plane.normal = * (SFVec3f *) field.far_ptr;
404 :
405 349 : CHECK_FIELD("PlaneClipper", 1, GF_SG_VRML_SFFLOAT);
406 349 : pc->plane.d = * (SFFloat *) field.far_ptr;
407 :
408 349 : CHECK_FIELD("PlaneClipper", 2, GF_SG_VRML_MFNODE);
409 349 : pc->children = *(GF_ChildNodeItem **) field.far_ptr;
410 349 : return GF_TRUE;
411 : }
412 :
413 :
414 991 : static void TraversePlaneClipper(GF_Node *node, void *rs, Bool is_destroy)
415 : {
416 991 : PlaneClipperStack *stack = (PlaneClipperStack *)gf_node_get_private(node);
417 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
418 :
419 991 : if (is_destroy) {
420 2 : group_3d_delete(node);
421 2 : return;
422 : }
423 :
424 989 : if (gf_node_dirty_get(node)) {
425 347 : PlaneClipper_GetNode(node, &stack->pc);
426 347 : gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
427 : }
428 :
429 989 : if (tr_state->num_clip_planes==MAX_USER_CLIP_PLANES) {
430 0 : group_3d_traverse((GF_Node*)&stack->pc, (GroupingNode*)stack, tr_state);
431 0 : return;
432 : }
433 :
434 989 : if (tr_state->traversing_mode == TRAVERSE_SORT) {
435 : GF_Matrix mx;
436 305 : gf_mx_copy(mx, tr_state->model_matrix);
437 305 : visual_3d_set_clip_plane(tr_state->visual, stack->pc.plane, &mx, GF_FALSE);
438 305 : tr_state->num_clip_planes++;
439 :
440 305 : group_3d_traverse((GF_Node*)&stack->pc, (GroupingNode*)stack, tr_state);
441 305 : visual_3d_reset_clip_plane(tr_state->visual);
442 305 : tr_state->num_clip_planes--;
443 : } else {
444 684 : tr_state->clip_planes[tr_state->num_clip_planes] = stack->pc.plane;
445 684 : gf_mx_apply_plane(&tr_state->model_matrix, &tr_state->clip_planes[tr_state->num_clip_planes]);
446 684 : tr_state->num_clip_planes++;
447 :
448 684 : group_3d_traverse((GF_Node*)&stack->pc, (GroupingNode*)stack, tr_state);
449 :
450 684 : tr_state->num_clip_planes--;
451 : }
452 :
453 : }
454 :
455 2 : void compositor_init_plane_clipper(GF_Compositor *compositor, GF_Node *node)
456 : {
457 : PlaneClipper pc;
458 2 : if (PlaneClipper_GetNode(node, &pc)) {
459 : PlaneClipperStack *stack;
460 2 : GF_SAFEALLOC(stack, PlaneClipperStack);
461 2 : if (!stack) {
462 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate plane clipper stack\n"));
463 0 : return;
464 : }
465 : //SetupGroupingNode(stack, compositor->compositor, node, & pc.children);
466 2 : gf_node_set_private(node, stack);
467 2 : gf_node_set_callback_function(node, TraversePlaneClipper);
468 : /*we're a grouping node, force bounds rebuild as soon as loaded*/
469 2 : gf_node_dirty_set(node, GF_SG_CHILD_DIRTY, GF_FALSE);
470 :
471 2 : stack->pc = pc;
472 2 : gf_node_proto_set_grouping(node);
473 : }
474 : }
475 :
476 : #endif
477 :
478 :
479 : /*OffscreenGroup hardcoded proto*/
480 : typedef struct
481 : {
482 : BASE_NODE
483 : CHILDREN
484 :
485 : s32 offscreen;
486 : Fixed opacity;
487 : } OffscreenGroup;
488 :
489 : typedef struct
490 : {
491 : GROUPING_MPEG4_STACK_2D
492 :
493 : #ifndef GF_SR_USE_VIDEO_CACHE
494 : struct _group_cache *cache;
495 : #endif
496 :
497 : OffscreenGroup og;
498 : Bool detached;
499 : } OffscreenGroupStack;
500 :
501 7 : static Bool OffscreenGroup_GetNode(GF_Node *node, OffscreenGroup *og)
502 : {
503 : GF_FieldInfo field;
504 : memset(og, 0, sizeof(OffscreenGroup));
505 7 : og->sgprivate = node->sgprivate;
506 :
507 7 : CHECK_FIELD("OffscreenGroup", 0, GF_SG_VRML_MFNODE);
508 7 : og->children = *(GF_ChildNodeItem **) field.far_ptr;
509 :
510 7 : CHECK_FIELD("OffscreenGroup", 1, GF_SG_VRML_SFINT32);
511 7 : og->offscreen = * (SFInt32 *) field.far_ptr;
512 :
513 7 : CHECK_FIELD("OffscreenGroup", 2, GF_SG_VRML_SFFLOAT);
514 7 : og->opacity = * (SFFloat *) field.far_ptr;
515 :
516 7 : return GF_TRUE;
517 : }
518 :
519 :
520 606 : static void TraverseOffscreenGroup(GF_Node *node, void *rs, Bool is_destroy)
521 : {
522 606 : OffscreenGroupStack *stack = (OffscreenGroupStack *)gf_node_get_private(node);
523 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
524 :
525 606 : if (is_destroy) {
526 3 : if (stack->cache) group_cache_del(stack->cache);
527 3 : gf_free(stack);
528 3 : return;
529 : }
530 :
531 603 : if (tr_state->traversing_mode==TRAVERSE_SORT) {
532 363 : if (!stack->detached && (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY)) {
533 4 : OffscreenGroup_GetNode(node, &stack->og);
534 :
535 4 : if (stack->og.offscreen) {
536 4 : stack->flags |= GROUP_IS_CACHED | GROUP_PERMANENT_CACHE;
537 4 : if (!stack->cache) {
538 3 : stack->cache = group_cache_new(tr_state->visual->compositor, (GF_Node*)&stack->og);
539 : }
540 4 : stack->cache->opacity = stack->og.opacity;
541 4 : stack->cache->drawable->flags |= DRAWABLE_HAS_CHANGED;
542 : } else {
543 0 : if (stack->cache) group_cache_del(stack->cache);
544 0 : stack->cache = NULL;
545 0 : stack->flags &= ~(GROUP_IS_CACHED|GROUP_PERMANENT_CACHE);
546 : }
547 4 : gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
548 : /*flag is not set for PROTO*/
549 4 : gf_node_dirty_set(node, GF_SG_CHILD_DIRTY, GF_FALSE);
550 : }
551 363 : if (stack->cache) {
552 363 : if (stack->detached)
553 0 : gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY);
554 :
555 363 : tr_state->subscene_not_over = 0;
556 363 : group_cache_traverse((GF_Node *)&stack->og, stack->cache, tr_state, stack->cache->force_recompute, GF_TRUE, stack->detached ? GF_TRUE : GF_FALSE);
557 :
558 363 : if (gf_node_dirty_get(node)) {
559 2 : gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY);
560 361 : } else if ((stack->og.offscreen==2) && !stack->detached && !tr_state->subscene_not_over && stack->cache->txh.width && stack->cache->txh.height) {
561 : GF_FieldInfo field;
562 0 : if (gf_node_get_field(node, 0, &field) == GF_OK) {
563 0 : gf_node_unregister_children(node, *(GF_ChildNodeItem **) field.far_ptr);
564 0 : *(GF_ChildNodeItem **) field.far_ptr = NULL;
565 0 : stack->detached = GF_TRUE;
566 : }
567 0 : if (gf_node_get_field(node, 3, &field) == GF_OK) {
568 0 : *(SFBool *) field.far_ptr = 1;
569 : //gf_node_event_out(node, 3);
570 : }
571 : }
572 : } else {
573 0 : group_2d_traverse((GF_Node *)&stack->og, (GroupingNode2D*)stack, tr_state);
574 : }
575 : }
576 : /*draw mode*/
577 240 : else if (stack->cache && (tr_state->traversing_mode == TRAVERSE_DRAW_2D)) {
578 : /*draw it*/
579 240 : group_cache_draw(stack->cache, tr_state);
580 240 : gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY);
581 0 : } else if (!stack->detached) {
582 0 : group_2d_traverse((GF_Node *)&stack->og, (GroupingNode2D*)stack, tr_state);
583 : } else {
584 0 : if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
585 0 : tr_state->bounds = stack->bounds;
586 : }
587 0 : else if (stack->cache && (tr_state->traversing_mode == TRAVERSE_PICK)) {
588 0 : vrml_drawable_pick(stack->cache->drawable, tr_state);
589 : }
590 : }
591 : }
592 :
593 3 : void compositor_init_offscreen_group(GF_Compositor *compositor, GF_Node *node)
594 : {
595 : OffscreenGroup og;
596 3 : if (OffscreenGroup_GetNode(node, &og)) {
597 : OffscreenGroupStack *stack;
598 3 : GF_SAFEALLOC(stack, OffscreenGroupStack);
599 3 : if (!stack) {
600 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate offscreen group stack\n"));
601 0 : return;
602 : }
603 3 : gf_node_set_private(node, stack);
604 3 : gf_node_set_callback_function(node, TraverseOffscreenGroup);
605 3 : stack->og = og;
606 3 : if (og.offscreen) stack->flags |= GROUP_IS_CACHED;
607 3 : gf_node_proto_set_grouping(node);
608 : }
609 : }
610 :
611 :
612 : /*DepthGroup hardcoded proto*/
613 : typedef struct
614 : {
615 : BASE_NODE
616 : CHILDREN
617 :
618 : Fixed depth_gain, depth_offset;
619 :
620 : } DepthGroup;
621 :
622 : typedef struct
623 : {
624 : GROUPING_MPEG4_STACK_2D
625 : DepthGroup dg;
626 : } DepthGroupStack;
627 :
628 3 : static Bool DepthGroup_GetNode(GF_Node *node, DepthGroup *dg)
629 : {
630 : GF_FieldInfo field;
631 : memset(dg, 0, sizeof(DepthGroup));
632 3 : dg->sgprivate = node->sgprivate;
633 :
634 3 : CHECK_FIELD("DepthGroup", 0, GF_SG_VRML_MFNODE);
635 3 : dg->children = *(GF_ChildNodeItem **) field.far_ptr;
636 :
637 3 : CHECK_FIELD("DepthGroup", 1, GF_SG_VRML_SFFLOAT);
638 3 : dg->depth_gain = * (SFFloat *) field.far_ptr;
639 :
640 3 : CHECK_FIELD("DepthGroup", 2, GF_SG_VRML_SFFLOAT);
641 3 : dg->depth_offset = * (SFFloat *) field.far_ptr;
642 :
643 3 : return GF_TRUE;
644 : }
645 :
646 :
647 3 : static void TraverseDepthGroup(GF_Node *node, void *rs, Bool is_destroy)
648 : {
649 : #ifdef GF_SR_USE_DEPTH
650 : Fixed depth_gain, depth_offset;
651 : #endif
652 :
653 3 : DepthGroupStack *stack = (DepthGroupStack *)gf_node_get_private(node);
654 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
655 :
656 3 : if (is_destroy) {
657 1 : gf_free(stack);
658 1 : return;
659 : }
660 :
661 2 : if (tr_state->traversing_mode==TRAVERSE_SORT) {
662 2 : if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) {
663 :
664 1 : gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
665 : /*flag is not set for PROTO*/
666 1 : gf_node_dirty_set(node, GF_SG_CHILD_DIRTY, GF_FALSE);
667 : }
668 : }
669 2 : DepthGroup_GetNode(node, &stack->dg);
670 :
671 :
672 : #ifdef GF_SR_USE_DEPTH
673 2 : depth_gain = tr_state->depth_gain;
674 2 : depth_offset = tr_state->depth_offset;
675 :
676 : // new offset is multiplied by parent gain and added to parent offset
677 2 : tr_state->depth_offset = gf_mulfix(stack->dg.depth_offset, tr_state->depth_gain) + tr_state->depth_offset;
678 :
679 : // gain is multiplied by parent gain
680 2 : tr_state->depth_gain = gf_mulfix(tr_state->depth_gain, stack->dg.depth_gain);
681 : #endif
682 :
683 : #ifndef GPAC_DISABLE_3D
684 2 : if (tr_state->visual->type_3d) {
685 : GF_Matrix mx_bckup, mx;
686 :
687 0 : gf_mx_copy(mx_bckup, tr_state->model_matrix);
688 0 : gf_mx_init(mx);
689 0 : mx.m[14] = gf_mulfix(stack->dg.depth_offset, tr_state->visual->compositor->depth_gl_scale);
690 0 : gf_mx_add_matrix(&tr_state->model_matrix, &mx);
691 0 : group_2d_traverse((GF_Node *)&stack->dg, (GroupingNode2D*)stack, tr_state);
692 : gf_mx_copy(tr_state->model_matrix, mx_bckup);
693 :
694 : } else
695 : #endif
696 : {
697 :
698 2 : group_2d_traverse((GF_Node *)&stack->dg, (GroupingNode2D*)stack, tr_state);
699 : }
700 :
701 : #ifdef GF_SR_USE_DEPTH
702 2 : tr_state->depth_gain = depth_gain;
703 2 : tr_state->depth_offset = depth_offset;
704 : #endif
705 : }
706 :
707 1 : void compositor_init_depth_group(GF_Compositor *compositor, GF_Node *node)
708 : {
709 : DepthGroup dg;
710 1 : if (DepthGroup_GetNode(node, &dg)) {
711 : DepthGroupStack *stack;
712 1 : GF_SAFEALLOC(stack, DepthGroupStack);
713 1 : if (!stack) {
714 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate depth group stack\n"));
715 0 : return;
716 : }
717 1 : gf_node_set_private(node, stack);
718 1 : gf_node_set_callback_function(node, TraverseDepthGroup);
719 1 : stack->dg = dg;
720 1 : gf_node_proto_set_grouping(node);
721 0 : } else GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Unable to initialize depth group \n"));
722 :
723 : }
724 :
725 : #ifdef GF_SR_USE_DEPTH
726 3 : static void TraverseDepthViewPoint(GF_Node *node, void *rs, Bool is_destroy)
727 : {
728 3 : if (!is_destroy && gf_node_dirty_get(node)) {
729 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
730 : GF_FieldInfo field;
731 1 : gf_node_dirty_clear(node, 0);
732 :
733 1 : tr_state->visual->depth_vp_position = 0;
734 1 : tr_state->visual->depth_vp_range = 0;
735 : #ifndef GPAC_DISABLE_3D
736 2 : if (!tr_state->camera) return;
737 0 : tr_state->camera->flags |= CAM_IS_DIRTY;
738 : #endif
739 :
740 0 : if (gf_node_get_field(node, 0, &field) != GF_OK) return;
741 0 : if (field.fieldType != GF_SG_VRML_SFBOOL) return;
742 :
743 0 : if ( *(SFBool *) field.far_ptr) {
744 0 : if (gf_node_get_field(node, 1, &field) != GF_OK) return;
745 0 : if (field.fieldType != GF_SG_VRML_SFFLOAT) return;
746 0 : tr_state->visual->depth_vp_position = *(SFFloat *) field.far_ptr;
747 0 : if (gf_node_get_field(node, 2, &field) != GF_OK) return;
748 0 : if (field.fieldType != GF_SG_VRML_SFFLOAT) return;
749 0 : tr_state->visual->depth_vp_range = *(SFFloat *) field.far_ptr;
750 : }
751 : #ifndef GPAC_DISABLE_3D
752 0 : if (tr_state->layer3d) gf_node_dirty_set(tr_state->layer3d, GF_SG_NODE_DIRTY, GF_FALSE);
753 : #endif
754 0 : gf_sc_invalidate(tr_state->visual->compositor, NULL);
755 : }
756 : }
757 : #endif
758 :
759 : static void compositor_init_depth_viewpoint(GF_Compositor *compositor, GF_Node *node)
760 : {
761 : #ifdef GF_SR_USE_DEPTH
762 1 : gf_node_set_callback_function(node, TraverseDepthViewPoint);
763 : #endif
764 : }
765 :
766 : /*IndexedCurve2D hardcoded proto*/
767 :
768 : typedef struct
769 : {
770 : BASE_NODE
771 :
772 : GF_Node *point;
773 : Fixed fineness;
774 : MFInt32 type;
775 : MFInt32 index;
776 : } IndexedCurve2D;
777 :
778 263 : static Bool IndexedCurve2D_GetNode(GF_Node *node, IndexedCurve2D *ic2d)
779 : {
780 : GF_FieldInfo field;
781 : memset(ic2d, 0, sizeof(IndexedCurve2D));
782 :
783 263 : ic2d->sgprivate = node->sgprivate;
784 :
785 263 : CHECK_FIELD("IndexedCurve2D", 0, GF_SG_VRML_SFNODE);
786 263 : ic2d->point = * (GF_Node **) field.far_ptr;
787 :
788 263 : CHECK_FIELD("IndexedCurve2D", 1, GF_SG_VRML_SFFLOAT);
789 263 : ic2d->fineness = *(SFFloat *) field.far_ptr;
790 :
791 263 : CHECK_FIELD("IndexedCurve2D", 2, GF_SG_VRML_MFINT32);
792 263 : ic2d->type = *(MFInt32 *) field.far_ptr;
793 :
794 263 : CHECK_FIELD("IndexedCurve2D", 3, GF_SG_VRML_MFINT32);
795 263 : ic2d->index = *(MFInt32 *) field.far_ptr;
796 :
797 263 : return GF_TRUE;
798 : }
799 :
800 : void curve2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state, MFInt32 *idx);
801 :
802 9621 : static void TraverseIndexedCurve2D(GF_Node *node, void *rs, Bool is_destroy)
803 : {
804 : DrawableContext *ctx;
805 : IndexedCurve2D ic2d;
806 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
807 9621 : Drawable *stack = (Drawable *)gf_node_get_private(node);
808 :
809 9621 : if (is_destroy) {
810 263 : drawable_node_del(node);
811 263 : return;
812 : }
813 :
814 9358 : if (gf_node_dirty_get(node)) {
815 263 : if (!IndexedCurve2D_GetNode(node, &ic2d)) return;
816 : //clears dirty flag
817 263 : curve2d_check_changes((GF_Node*) &ic2d, stack, tr_state, &ic2d.index);
818 : }
819 :
820 9358 : switch (tr_state->traversing_mode) {
821 : #ifndef GPAC_DISABLE_3D
822 0 : case TRAVERSE_DRAW_3D:
823 0 : if (!stack->mesh) {
824 0 : stack->mesh = new_mesh();
825 0 : mesh_from_path(stack->mesh, stack->path);
826 : }
827 0 : visual_3d_draw_2d(stack, tr_state);
828 0 : return;
829 : #endif
830 0 : case TRAVERSE_PICK:
831 0 : vrml_drawable_pick(stack, tr_state);
832 0 : return;
833 0 : case TRAVERSE_GET_BOUNDS:
834 0 : gf_path_get_bounds(stack->path, &tr_state->bounds);
835 0 : return;
836 9358 : case TRAVERSE_SORT:
837 : #ifndef GPAC_DISABLE_3D
838 9358 : if (tr_state->visual->type_3d) return;
839 : #endif
840 9358 : ctx = drawable_init_context_mpeg4(stack, tr_state);
841 9358 : if (!ctx) return;
842 9358 : drawable_finalize_sort(ctx, tr_state, NULL);
843 9358 : return;
844 : }
845 : }
846 :
847 : static void compositor_init_idx_curve2d(GF_Compositor *compositor, GF_Node *node)
848 : {
849 263 : drawable_stack_new(compositor, node);
850 263 : gf_node_set_callback_function(node, TraverseIndexedCurve2D);
851 : }
852 :
853 :
854 :
855 :
856 :
857 : /*TransformRef hardcoded proto*/
858 : typedef struct
859 : {
860 : BASE_NODE
861 : CHILDREN
862 : } Untransform;
863 :
864 : typedef struct
865 : {
866 : GROUPING_MPEG4_STACK_2D
867 : Untransform untr;
868 : } UntransformStack;
869 :
870 60 : static Bool Untransform_GetNode(GF_Node *node, Untransform *tr)
871 : {
872 : GF_FieldInfo field;
873 : memset(tr, 0, sizeof(Untransform));
874 60 : tr->sgprivate = node->sgprivate;
875 :
876 60 : CHECK_FIELD("Untransform", 0, GF_SG_VRML_MFNODE);
877 60 : tr->children = *(GF_ChildNodeItem **) field.far_ptr;
878 :
879 60 : return GF_TRUE;
880 : }
881 :
882 :
883 184 : static void TraverseUntransform(GF_Node *node, void *rs, Bool is_destroy)
884 : {
885 184 : UntransformStack *stack = (UntransformStack *)gf_node_get_private(node);
886 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
887 :
888 184 : if (is_destroy) {
889 3 : gf_free(stack);
890 3 : return;
891 : }
892 :
893 181 : if (tr_state->traversing_mode==TRAVERSE_SORT) {
894 181 : if (gf_node_dirty_get(node)) {
895 57 : Untransform_GetNode(node, &stack->untr); /*lets place it below*/
896 57 : gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
897 : }
898 : }
899 :
900 : #ifndef GPAC_DISABLE_3D
901 181 : if (tr_state->visual->type_3d) {
902 : GF_Matrix mx_model;
903 : GF_Camera backup_cam;
904 :
905 0 : if (!tr_state->camera) return;
906 :
907 0 : gf_mx_copy(mx_model, tr_state->model_matrix);
908 0 : gf_mx_init(tr_state->model_matrix);
909 :
910 : memcpy(&backup_cam, tr_state->camera, sizeof(GF_Camera));
911 :
912 0 : camera_invalidate(tr_state->camera);
913 0 : tr_state->camera->is_3D = GF_FALSE;
914 0 : tr_state->camera->flags |= CAM_NO_LOOKAT;
915 0 : tr_state->camera->end_zoom = FIX_ONE;
916 0 : camera_update(tr_state->camera, NULL, GF_TRUE);
917 :
918 :
919 0 : if (tr_state->traversing_mode == TRAVERSE_SORT) {
920 0 : visual_3d_set_viewport(tr_state->visual, tr_state->camera->proj_vp);
921 0 : visual_3d_projection_matrix_modified(tr_state->visual);
922 :
923 0 : gf_node_traverse_children((GF_Node *)&stack->untr, tr_state);
924 :
925 : gf_mx_copy(tr_state->model_matrix, mx_model);
926 0 : memcpy(tr_state->camera, &backup_cam, sizeof(GF_Camera));
927 :
928 0 : visual_3d_projection_matrix_modified(tr_state->visual);
929 :
930 0 : visual_3d_set_viewport(tr_state->visual, tr_state->camera->proj_vp);
931 0 : } else if (tr_state->traversing_mode == TRAVERSE_PICK) {
932 0 : Fixed prev_dist = tr_state->visual->compositor->hit_square_dist;
933 0 : GF_Ray r = tr_state->ray;
934 0 : tr_state->ray.orig.x = INT2FIX(tr_state->pick_x);
935 0 : tr_state->ray.orig.y = INT2FIX(tr_state->pick_y);
936 0 : tr_state->ray.orig.z = 0;
937 0 : tr_state->ray.dir.x = 0;
938 0 : tr_state->ray.dir.y = 0;
939 0 : tr_state->ray.dir.z = -FIX_ONE;
940 0 : tr_state->visual->compositor->hit_square_dist=0;
941 :
942 0 : gf_node_traverse_children((GF_Node *)&stack->untr, tr_state);
943 :
944 : gf_mx_copy(tr_state->model_matrix, mx_model);
945 0 : memcpy(tr_state->camera, &backup_cam, sizeof(GF_Camera));
946 0 : tr_state->ray = r;
947 :
948 : /*nothing picked, restore previous pick*/
949 0 : if (!tr_state->visual->compositor->hit_square_dist)
950 0 : tr_state->visual->compositor->hit_square_dist = prev_dist;
951 :
952 : } else {
953 0 : gf_node_traverse_children((GF_Node *)&stack->untr, tr_state);
954 :
955 : gf_mx_copy(tr_state->model_matrix, mx_model);
956 0 : memcpy(tr_state->camera, &backup_cam, sizeof(GF_Camera));
957 : }
958 :
959 : } else
960 : #endif
961 : {
962 : GF_Matrix2D mx2d_backup;
963 181 : gf_mx2d_copy(mx2d_backup, tr_state->transform);
964 181 : gf_mx2d_init(tr_state->transform);
965 :
966 181 : group_2d_traverse((GF_Node *)&stack->untr, (GroupingNode2D *)stack, tr_state);
967 :
968 : gf_mx2d_copy(tr_state->transform, mx2d_backup);
969 :
970 :
971 : }
972 : }
973 :
974 3 : void compositor_init_untransform(GF_Compositor *compositor, GF_Node *node)
975 : {
976 : Untransform tr;
977 3 : if (Untransform_GetNode(node, &tr)) {
978 : UntransformStack *stack;
979 3 : GF_SAFEALLOC(stack, UntransformStack);
980 3 : if (!stack) {
981 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate untransform stack\n"));
982 0 : return;
983 : }
984 3 : gf_node_set_private(node, stack);
985 3 : gf_node_set_callback_function(node, TraverseUntransform);
986 3 : stack->untr = tr;
987 3 : gf_node_proto_set_grouping(node);
988 : }
989 : }
990 :
991 :
992 :
993 : /*StyleGroup: overrides appearance of all children*/
994 : typedef struct
995 : {
996 : BASE_NODE
997 : CHILDREN
998 :
999 : GF_Node *appearance;
1000 : } StyleGroup;
1001 :
1002 : typedef struct
1003 : {
1004 : GROUPING_MPEG4_STACK_2D
1005 : StyleGroup sg;
1006 : } StyleGroupStack;
1007 :
1008 710 : static Bool StyleGroup_GetNode(GF_Node *node, StyleGroup *sg)
1009 : {
1010 : GF_FieldInfo field;
1011 : memset(sg, 0, sizeof(StyleGroup));
1012 710 : sg->sgprivate = node->sgprivate;
1013 :
1014 710 : CHECK_FIELD("StyleGroup", 0, GF_SG_VRML_MFNODE);
1015 710 : sg->children = *(GF_ChildNodeItem **) field.far_ptr;
1016 :
1017 710 : CHECK_FIELD("StyleGroup", 1, GF_SG_VRML_SFNODE);
1018 710 : sg->appearance = *(GF_Node **)field.far_ptr;
1019 :
1020 710 : return GF_TRUE;
1021 : }
1022 :
1023 :
1024 710 : static void TraverseStyleGroup(GF_Node *node, void *rs, Bool is_destroy)
1025 : {
1026 : Bool set = GF_FALSE;
1027 710 : StyleGroupStack *stack = (StyleGroupStack *)gf_node_get_private(node);
1028 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
1029 :
1030 710 : if (is_destroy) {
1031 73 : gf_free(stack);
1032 73 : return;
1033 : }
1034 :
1035 637 : if (tr_state->traversing_mode==TRAVERSE_SORT) {
1036 637 : if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) {
1037 :
1038 26 : gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
1039 : /*flag is not set for PROTO*/
1040 26 : gf_node_dirty_set(node, GF_SG_CHILD_DIRTY, GF_FALSE);
1041 : }
1042 : }
1043 637 : StyleGroup_GetNode(node, &stack->sg);
1044 :
1045 637 : if (!tr_state->override_appearance) {
1046 : set = GF_TRUE;
1047 637 : tr_state->override_appearance = stack->sg.appearance;
1048 : }
1049 637 : group_2d_traverse((GF_Node *)&stack->sg, (GroupingNode2D*)stack, tr_state);
1050 :
1051 637 : if (set) {
1052 637 : tr_state->override_appearance = NULL;
1053 : }
1054 : }
1055 :
1056 73 : void compositor_init_style_group(GF_Compositor *compositor, GF_Node *node)
1057 : {
1058 : StyleGroup sg;
1059 73 : if (StyleGroup_GetNode(node, &sg)) {
1060 : StyleGroupStack *stack;
1061 73 : GF_SAFEALLOC(stack, StyleGroupStack);
1062 73 : if (!stack) {
1063 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate style group stack\n"));
1064 0 : return;
1065 : }
1066 73 : gf_node_set_private(node, stack);
1067 73 : gf_node_set_callback_function(node, TraverseStyleGroup);
1068 73 : stack->sg = sg;
1069 73 : gf_node_proto_set_grouping(node);
1070 : } else {
1071 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Unable to initialize style group\n"));
1072 : }
1073 : }
1074 :
1075 :
1076 :
1077 : /*TestSensor: tests eventIn/eventOuts for hardcoded proto*/
1078 : typedef struct
1079 : {
1080 : BASE_NODE
1081 :
1082 : Bool onTrigger;
1083 : Fixed value;
1084 : } TestSensor;
1085 :
1086 : typedef struct
1087 : {
1088 : TestSensor ts;
1089 : } TestSensorStack;
1090 :
1091 6 : static Bool TestSensor_GetNode(GF_Node *node, TestSensor *ts)
1092 : {
1093 : GF_FieldInfo field;
1094 : memset(ts, 0, sizeof(TestSensor));
1095 6 : ts->sgprivate = node->sgprivate;
1096 :
1097 6 : CHECK_FIELD("TestSensor", 0, GF_SG_VRML_SFBOOL);
1098 6 : if (field.eventType != GF_SG_EVENT_IN) return GF_FALSE;
1099 6 : ts->onTrigger = *(SFBool *)field.far_ptr;
1100 :
1101 6 : CHECK_FIELD("TestSensor", 1, GF_SG_VRML_SFFLOAT);
1102 6 : if (field.eventType != GF_SG_EVENT_EXPOSED_FIELD) return GF_FALSE;
1103 6 : ts->value = *(SFFloat *)field.far_ptr;
1104 :
1105 6 : CHECK_FIELD("TestSensor", 2, GF_SG_VRML_SFFLOAT);
1106 6 : if (field.eventType != GF_SG_EVENT_OUT) return GF_FALSE;
1107 :
1108 6 : return GF_TRUE;
1109 : }
1110 :
1111 :
1112 50 : static void TraverseTestSensor(GF_Node *node, void *rs, Bool is_destroy)
1113 : {
1114 50 : TestSensorStack *stack = (TestSensorStack *)gf_node_get_private(node);
1115 :
1116 50 : if (is_destroy) {
1117 1 : gf_free(stack);
1118 1 : return;
1119 : }
1120 : }
1121 :
1122 5 : void TestSensor_OnTrigger(GF_Node *node, struct _route *route)
1123 : {
1124 : GF_FieldInfo field;
1125 : Fixed value;
1126 5 : TestSensorStack *stack = (TestSensorStack *)gf_node_get_private(node);
1127 5 : TestSensor_GetNode(node, &stack->ts);
1128 :
1129 5 : if (stack->ts.onTrigger) {
1130 3 : value = stack->ts.value;
1131 : } else {
1132 2 : value = 1-stack->ts.value;
1133 : }
1134 :
1135 5 : gf_node_get_field(node, 2, &field);
1136 5 : *(SFFloat*)field.far_ptr = value;
1137 5 : gf_node_event_out(node, 2);
1138 5 : }
1139 :
1140 1 : void compositor_init_test_sensor(GF_Compositor *compositor, GF_Node *node)
1141 : {
1142 : TestSensor ts;
1143 1 : if (TestSensor_GetNode(node, &ts)) {
1144 : GF_Err e;
1145 : TestSensorStack *stack;
1146 1 : GF_SAFEALLOC(stack, TestSensorStack);
1147 1 : if (!stack) {
1148 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate test sensor stack\n"));
1149 0 : return;
1150 : }
1151 1 : gf_node_set_private(node, stack);
1152 1 : gf_node_set_callback_function(node, TraverseTestSensor);
1153 1 : stack->ts = ts;
1154 :
1155 1 : e = gf_node_set_proto_eventin_handler(node, 0, TestSensor_OnTrigger);
1156 1 : if (e) {
1157 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to initialize Proto TestSensor callback: %s\n", gf_error_to_string(e) ));
1158 : }
1159 : } else {
1160 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Unable to initialize test sensor\n"));
1161 : }
1162 : }
1163 :
1164 :
1165 : /*CustomTexture: tests defining new (openGL) textures*/
1166 : typedef struct
1167 : {
1168 : BASE_NODE
1169 :
1170 : Fixed intensity;
1171 : } CustomTexture;
1172 :
1173 : typedef struct
1174 : {
1175 : CustomTexture tx;
1176 : GF_TextureHandler txh;
1177 : u32 gl_id;
1178 : Bool disabled;
1179 : } CustomTextureStack;
1180 :
1181 152 : static Bool CustomTexture_GetNode(GF_Node *node, CustomTexture *tx)
1182 : {
1183 : GF_FieldInfo field;
1184 : memset(tx, 0, sizeof(CustomTexture));
1185 152 : tx->sgprivate = node->sgprivate;
1186 :
1187 152 : CHECK_FIELD("CustomTexture", 0, GF_SG_VRML_SFFLOAT);
1188 152 : if (field.eventType != GF_SG_EVENT_EXPOSED_FIELD) return GF_FALSE;
1189 152 : tx->intensity = *(SFFloat *)field.far_ptr;
1190 :
1191 152 : return GF_TRUE;
1192 : }
1193 :
1194 1 : static void TraverseCustomTexture(GF_Node *node, void *rs, Bool is_destroy)
1195 : {
1196 1 : CustomTextureStack *stack = (CustomTextureStack *)gf_node_get_private(node);
1197 :
1198 1 : if (is_destroy) {
1199 : //release texture object
1200 1 : gf_sc_texture_destroy(&stack->txh);
1201 1 : gf_free(stack);
1202 1 : return;
1203 : }
1204 : }
1205 :
1206 : #ifndef GPAC_DISABLE_3D
1207 : #include "gl_inc.h"
1208 : #endif
1209 :
1210 151 : static void CustomTexture_update(GF_TextureHandler *txh)
1211 : {
1212 : #ifndef GPAC_DISABLE_3D
1213 : u8 data[16];
1214 : #endif
1215 151 : CustomTextureStack *stack = gf_node_get_private(txh->owner);
1216 : //alloc texture
1217 151 : if (!txh->tx_io) {
1218 : //allocate texture
1219 1 : gf_sc_texture_allocate(txh);
1220 1 : if (!txh->tx_io) return;
1221 : }
1222 151 : if (stack->disabled) return;
1223 :
1224 : #ifndef GPAC_DISABLE_3D
1225 : //texture not setup, do it
1226 151 : if (! gf_sc_texture_get_gl_id(txh)) {
1227 :
1228 : //setup some defaults (these two vars are used to setup internal texture format)
1229 : //in our case we only want to test openGL so no need to fill in the texture width/height stride
1230 : //since we will upload ourselves the texture
1231 1 : txh->transparent = 0;
1232 1 : txh->pixelformat = GF_PIXEL_RGB;
1233 :
1234 : //signaling we modified associated data (even if no data in our case) to mark texture as dirty
1235 1 : gf_sc_texture_set_data(txh);
1236 :
1237 : //trigger HW setup of the texture
1238 1 : gf_sc_texture_push_image(txh, GF_FALSE, GF_FALSE);
1239 :
1240 : //OK we have a valid textureID
1241 1 : stack->gl_id = gf_sc_texture_get_gl_id(txh);
1242 : }
1243 : #endif
1244 :
1245 :
1246 : //get current value of node->value
1247 151 : CustomTexture_GetNode(txh->owner, &stack->tx);
1248 :
1249 : #ifndef GPAC_DISABLE_3D
1250 : //setup our texture data
1251 : memset(data, 0, sizeof(char)*12);
1252 151 : data[0] = (u8) (0xFF * FIX2FLT(stack->tx.intensity)); //first pixel red modulated by intensity
1253 151 : data[4] = (u8) (0xFF * FIX2FLT(stack->tx.intensity)); //second pixel green
1254 151 : data[8] = (u8) (0xFF * FIX2FLT(stack->tx.intensity)); //third pixel blue
1255 : //last pixel black
1256 :
1257 151 : glBindTexture( GL_TEXTURE_2D, stack->gl_id);
1258 151 : glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
1259 :
1260 : #endif
1261 :
1262 : }
1263 :
1264 1 : void compositor_init_custom_texture(GF_Compositor *compositor, GF_Node *node)
1265 : {
1266 : CustomTexture tx;
1267 1 : if (CustomTexture_GetNode(node, &tx)) {
1268 : CustomTextureStack *stack;
1269 1 : GF_SAFEALLOC(stack, CustomTextureStack);
1270 1 : if (!stack) {
1271 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate custom texture group stack\n"));
1272 0 : return;
1273 : }
1274 1 : gf_node_set_private(node, stack);
1275 1 : gf_node_set_callback_function(node, TraverseCustomTexture);
1276 1 : stack->tx = tx;
1277 :
1278 1 : if (!gf_sc_check_gl_support(compositor)) {
1279 0 : stack->disabled = GF_TRUE;
1280 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] Driver disabled, cannot render custom texture test\n"));
1281 : return;
1282 : }
1283 : //register texture object
1284 1 : gf_sc_texture_setup(&stack->txh, compositor, node);
1285 1 : stack->txh.update_texture_fcnt = CustomTexture_update;
1286 :
1287 : } else {
1288 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Unable to initialize custom texture\n"));
1289 : }
1290 : }
1291 :
1292 : #ifndef GPAC_DISABLE_3D
1293 :
1294 0 : static void TraverseVRGeometry(GF_Node *node, void *rs, Bool is_destroy)
1295 : {
1296 : GF_TextureHandler *txh;
1297 : GF_MediaObjectVRInfo vrinfo;
1298 : GF_MeshSphereAngles sphere_angles;
1299 : Bool mesh_was_reset = GF_FALSE;
1300 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
1301 0 : Drawable3D *stack = (Drawable3D *)gf_node_get_private(node);
1302 :
1303 0 : if (is_destroy) {
1304 0 : drawable_3d_del(node);
1305 0 : return;
1306 : }
1307 :
1308 0 : if (!tr_state->appear || ! ((M_Appearance *)tr_state->appear)->texture)
1309 : return;
1310 :
1311 0 : txh = gf_sc_texture_get_handler( ((M_Appearance *) tr_state->appear)->texture );
1312 0 : if (!txh->stream) return;
1313 :
1314 0 : if (gf_node_dirty_get(node) || (tr_state->traversing_mode==TRAVERSE_DRAW_3D)) {
1315 0 : if (! gf_mo_get_srd_info(txh->stream, &vrinfo))
1316 : return;
1317 :
1318 0 : if (vrinfo.has_full_coverage && tr_state->disable_partial_sphere) {
1319 0 : if ((vrinfo.srd_w!=vrinfo.srd_max_x) || (vrinfo.srd_h!=vrinfo.srd_max_y))
1320 : return;
1321 : }
1322 :
1323 0 : sphere_angles.min_phi = -GF_PI2 + GF_PI * vrinfo.srd_y / vrinfo.srd_max_y;
1324 0 : sphere_angles.max_phi = -GF_PI2 + GF_PI * (vrinfo.srd_h + vrinfo.srd_y) / vrinfo.srd_max_y;
1325 :
1326 0 : sphere_angles.min_theta = GF_2PI * vrinfo.srd_x / vrinfo.srd_max_x;
1327 0 : sphere_angles.max_theta = GF_2PI * ( vrinfo.srd_w + vrinfo.srd_x ) / vrinfo.srd_max_x;
1328 :
1329 0 : if (gf_node_dirty_get(node)) {
1330 : u32 radius;
1331 0 : mesh_reset(stack->mesh);
1332 :
1333 0 : radius = MAX(vrinfo.scene_width, vrinfo.scene_height) / 4;
1334 : //may happen that we don't have a scene width/height, use hardcoded 100 units radius (size actually doesn't matter
1335 : //since our VP/camera is at the center of the sphere
1336 0 : if (!radius) {
1337 : radius = 100;
1338 : }
1339 :
1340 : if (radius) {
1341 0 : mesh_new_sphere(stack->mesh, -1 * INT2FIX(radius), GF_FALSE, &sphere_angles);
1342 :
1343 0 : txh->flags &= ~GF_SR_TEXTURE_REPEAT_S;
1344 0 : txh->flags &= ~GF_SR_TEXTURE_REPEAT_T;
1345 : }
1346 : mesh_was_reset = GF_TRUE;
1347 0 : gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
1348 : }
1349 :
1350 :
1351 0 : if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) {
1352 : Bool visible = GF_FALSE;
1353 : #ifndef GPAC_DISABLE_LOG
1354 0 : const char *pid_name = gf_filter_pid_get_name(txh->stream->odm->pid);
1355 : #endif
1356 0 : if (! tr_state->camera_was_dirty && !mesh_was_reset) {
1357 0 : visible = (stack->mesh->flags & MESH_WAS_VISIBLE) ? GF_TRUE : GF_FALSE;
1358 0 : } else if ((vrinfo.srd_w==vrinfo.srd_max_x) && (vrinfo.srd_h==vrinfo.srd_max_y)) {
1359 : visible = GF_TRUE;
1360 : }
1361 0 : else if (txh->compositor->tvtf) {
1362 : visible = GF_TRUE;
1363 : //estimate visibility asap, even if texture not yet ready (we have SRD info):
1364 : //this allows sending stop commands which will free inactive decoder HW context
1365 : } else {
1366 : u32 i, j;
1367 : u32 nb_visible=0;
1368 0 : u32 nb_tests = tr_state->visual->compositor->tvtn;
1369 0 : u32 min_visible_threshold = tr_state->visual->compositor->tvtt;
1370 : u32 stride;
1371 :
1372 : //pick nb_tests vertices spaced every stride in the mesh
1373 0 : stride = stack->mesh->v_count;
1374 0 : stride /= nb_tests;
1375 0 : for (i=0; i<nb_tests; i++) {
1376 : Bool vis = GF_TRUE;
1377 0 : GF_Vec pt = stack->mesh->vertices[i*stride].pos;
1378 : //check the point is in our frustum - don't test far plane
1379 0 : for (j=1; j<6; j++) {
1380 0 : Fixed d = gf_plane_get_distance(&tr_state->camera->planes[j], &pt);
1381 0 : if (d<0) {
1382 : vis = GF_FALSE;
1383 : break;
1384 : }
1385 : }
1386 0 : if (vis) {
1387 0 : nb_visible++;
1388 : //abort test if more visible points than our threshold
1389 0 : if (nb_visible > min_visible_threshold)
1390 : break;
1391 : }
1392 : }
1393 0 : if (nb_visible > min_visible_threshold)
1394 : visible = GF_TRUE;
1395 0 : GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texture %s Partial sphere is %s - %d sample points visible out of %d\n", pid_name, visible ? "visible" : "hidden", nb_visible, i));
1396 : }
1397 :
1398 0 : if (visible) {
1399 0 : stack->mesh->flags |= MESH_WAS_VISIBLE;
1400 : } else {
1401 0 : stack->mesh->flags &= ~MESH_WAS_VISIBLE;
1402 : }
1403 :
1404 0 : if (visible && (vrinfo.srd_w != vrinfo.srd_max_x) && tr_state->visual->compositor->gazer_enabled) {
1405 : s32 gx, gy;
1406 0 : tr_state->visual->compositor->hit_node = NULL;
1407 0 : tr_state->visual->compositor->hit_square_dist = 0;
1408 :
1409 : //gaze coords are 0,0 in top-left
1410 0 : gx = (s32)( tr_state->visual->compositor->gaze_x - tr_state->camera->width/2 );
1411 0 : gy = (s32)( tr_state->camera->height/2 - tr_state->visual->compositor->gaze_y );
1412 :
1413 0 : visual_3d_setup_ray(tr_state->visual, tr_state, gx, gy);
1414 0 : visual_3d_vrml_drawable_pick(node, tr_state, stack->mesh, NULL);
1415 0 : if (tr_state->visual->compositor->hit_node) {
1416 0 : GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texture %s Partial sphere is under gaze coord %d %d\n", pid_name, tr_state->visual->compositor->gaze_x, tr_state->visual->compositor->gaze_y));
1417 :
1418 0 : tr_state->visual->compositor->hit_node = NULL;
1419 : } else {
1420 : visible = GF_FALSE;
1421 : }
1422 :
1423 : }
1424 :
1425 0 : if (vrinfo.has_full_coverage) {
1426 0 : if (visible) {
1427 0 : if (!txh->is_open) {
1428 0 : GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texture %s stoped on visible partial sphere - starting it\n", pid_name));
1429 : assert(txh->stream && txh->stream->odm);
1430 0 : txh->stream->odm->disable_buffer_at_next_play = GF_TRUE;
1431 0 : txh->stream->odm->flags |= GF_ODM_TILED_SHARED_CLOCK;
1432 0 : gf_sc_texture_play_from_to(txh, NULL, -1, -1, 1, 0);
1433 : }
1434 :
1435 0 : if (txh->data) {
1436 : Bool do_show = GF_TRUE;
1437 0 : Bool is_full_cover = (vrinfo.srd_w == vrinfo.srd_max_x) ? GF_TRUE : GF_FALSE;
1438 0 : visual_3d_enable_depth_buffer(tr_state->visual, GF_FALSE);
1439 0 : visual_3d_enable_antialias(tr_state->visual, GF_FALSE);
1440 0 : if ((tr_state->visual->compositor->tvtd == TILE_DEBUG_FULL) && !is_full_cover) do_show = GF_FALSE;
1441 0 : else if ((tr_state->visual->compositor->tvtd == TILE_DEBUG_PARTIAL) && is_full_cover) do_show = GF_FALSE;
1442 :
1443 : if (do_show) {
1444 0 : visual_3d_draw(tr_state, stack->mesh);
1445 : }
1446 0 : visual_3d_enable_depth_buffer(tr_state->visual, GF_TRUE);
1447 : }
1448 : } else {
1449 0 : if (txh->is_open) {
1450 0 : GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texture %s playing on hidden partial sphere - stopping it\n", pid_name));
1451 0 : gf_sc_texture_stop_no_unregister(txh);
1452 : }
1453 : }
1454 : } else {
1455 0 : if (txh->data) {
1456 0 : visual_3d_enable_depth_buffer(tr_state->visual, GF_FALSE);
1457 0 : visual_3d_enable_antialias(tr_state->visual, GF_FALSE);
1458 0 : visual_3d_draw(tr_state, stack->mesh);
1459 0 : visual_3d_enable_depth_buffer(tr_state->visual, GF_TRUE);
1460 : }
1461 0 : if (!tr_state->disable_partial_sphere) {
1462 0 : if (visible) {
1463 0 : gf_mo_hint_quality_degradation(txh->stream, 0);
1464 : } else {
1465 0 : gf_mo_hint_quality_degradation(txh->stream, 100);
1466 : }
1467 : }
1468 : }
1469 : }
1470 : }
1471 0 : if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
1472 0 : tr_state->bbox = stack->mesh->bounds;
1473 : }
1474 : }
1475 :
1476 :
1477 : static void compositor_init_vr_geometry(GF_Compositor *compositor, GF_Node *node)
1478 : {
1479 0 : drawable_3d_new(node);
1480 0 : gf_node_set_callback_function(node, TraverseVRGeometry);
1481 : }
1482 :
1483 : #define VRHUD_SCALE 6
1484 154 : static void TraverseVRHUD(GF_Node *node, void *rs, Bool is_destroy)
1485 : {
1486 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
1487 : GF_Matrix mv_bck, proj_bck, cam_bck;
1488 : /*SFVec3f target;*/
1489 : GF_Rect vp, orig_vp;
1490 : u32 mode, i, cull_bck;
1491 : Fixed angle_yaw/*, angle_pitch*/;
1492 : SFVec3f axis;
1493 154 : GF_Node *subtree = gf_node_get_private(node);
1494 308 : if (is_destroy) return;
1495 :
1496 152 : if (!tr_state->camera) return;
1497 150 : mode = tr_state->visual->compositor->vrhud_mode;
1498 150 : if (!mode) return;
1499 :
1500 0 : gf_mx_copy(mv_bck, tr_state->model_matrix);
1501 0 : gf_mx_copy(cam_bck, tr_state->camera->modelview);
1502 :
1503 0 : tr_state->disable_partial_sphere = GF_TRUE;
1504 : /*target = tr_state->camera->target;*/
1505 0 : orig_vp = tr_state->camera->proj_vp;
1506 :
1507 : /*
1508 : //compute pitch (elevation)
1509 : dlen = tr_state->camera->target.x*tr_state->camera->target.x + tr_state->camera->target.z*tr_state->camera->target.z;
1510 : dlen = gf_sqrt(dlen);
1511 : */
1512 :
1513 : /*angle_pitch = gf_atan2(tr_state->camera->target.y, dlen);*/
1514 :
1515 : //compute yaw (rotation Y)
1516 0 : angle_yaw = gf_atan2(tr_state->camera->target.z, tr_state->camera->target.x);
1517 :
1518 : //compute axis for the pitch
1519 0 : axis = tr_state->camera->target;
1520 0 : axis.y=0;
1521 0 : gf_vec_norm(&axis);
1522 :
1523 0 : visual_3d_enable_depth_buffer(tr_state->visual, GF_FALSE);
1524 :
1525 0 : if (mode==2) {
1526 : //rear mirror, reverse x-axis on projection
1527 0 : tr_state->camera->projection.m[0] *= -1;
1528 0 : visual_3d_projection_matrix_modified(tr_state->visual);
1529 : //inverse backface culling
1530 0 : tr_state->reverse_backface = GF_TRUE;
1531 : vp = orig_vp;
1532 0 : vp.width/=VRHUD_SCALE;
1533 0 : vp.height/=VRHUD_SCALE;
1534 0 : vp.x = orig_vp.x + (orig_vp.width-vp.width)/2;
1535 0 : vp.y = orig_vp.y + orig_vp.height-vp.height;
1536 :
1537 0 : visual_3d_set_viewport(tr_state->visual, vp);
1538 :
1539 0 : gf_mx_add_rotation(&tr_state->model_matrix, GF_PI, tr_state->camera->up.x, tr_state->camera->up.y, tr_state->camera->up.z);
1540 0 : gf_node_traverse(subtree, rs);
1541 0 : tr_state->camera->projection.m[0] *= -1;
1542 0 : visual_3d_projection_matrix_modified(tr_state->visual);
1543 0 : } else if (mode==1) {
1544 0 : gf_mx_copy(proj_bck, tr_state->camera->projection);
1545 0 : gf_mx_init(tr_state->camera->modelview);
1546 :
1547 : //force projection with PI/2 fov and AR 1:1
1548 0 : tr_state->camera->projection.m[0] = -1;
1549 0 : tr_state->camera->projection.m[5] = 1;
1550 0 : visual_3d_projection_matrix_modified(tr_state->visual);
1551 : //force cull inside
1552 0 : cull_bck = tr_state->cull_flag;
1553 0 : tr_state->cull_flag = CULL_INSIDE;
1554 : //inverse backface culling
1555 0 : tr_state->reverse_backface = GF_TRUE;
1556 :
1557 : //draw 3 viewports, each separated by PI/2 rotation
1558 0 : for (i=0; i<3; i++) {
1559 : vp = orig_vp;
1560 0 : vp.height/=VRHUD_SCALE;
1561 : vp.width=vp.height;
1562 : //we reverse X in the projection, so reverse the viewports
1563 0 : vp.x = orig_vp.x + orig_vp.width/2 - 3*vp.width/2 + (3-i-1)*vp.width;
1564 0 : vp.y = orig_vp.y + orig_vp.height - vp.height;
1565 0 : visual_3d_set_viewport(tr_state->visual, vp);
1566 0 : tr_state->disable_cull = GF_TRUE;
1567 :
1568 0 : gf_mx_init(tr_state->model_matrix);
1569 0 : gf_mx_add_rotation(&tr_state->model_matrix, angle_yaw-GF_PI, 0, 1, 0);
1570 0 : gf_mx_add_rotation(&tr_state->model_matrix, i*GF_PI2, 0, 1, 0);
1571 :
1572 0 : gf_node_traverse(subtree, rs);
1573 : }
1574 0 : gf_mx_copy(tr_state->camera->projection, proj_bck);
1575 0 : visual_3d_projection_matrix_modified(tr_state->visual);
1576 0 : tr_state->cull_flag = cull_bck;
1577 0 : gf_mx_copy(tr_state->camera->modelview, cam_bck);
1578 : }
1579 0 : else if ((mode==4) || (mode==3)) {
1580 : // mirror, reverse x-axis on projection
1581 0 : tr_state->camera->projection.m[0] *= -1;
1582 0 : visual_3d_projection_matrix_modified(tr_state->visual);
1583 : //inverse backface culling
1584 0 : tr_state->reverse_backface = GF_TRUE;
1585 :
1586 : //side left view
1587 0 : vp = orig_vp;
1588 0 : vp.width/=VRHUD_SCALE;
1589 0 : vp.height/=VRHUD_SCALE;
1590 0 : if (mode==3) {
1591 : vp.x = orig_vp.x;
1592 : } else {
1593 0 : vp.x = orig_vp.x + orig_vp.width/2 - 2*vp.width;
1594 : }
1595 0 : vp.y = orig_vp.y + orig_vp.height - vp.height;
1596 0 : visual_3d_set_viewport(tr_state->visual, vp);
1597 :
1598 0 : gf_mx_add_rotation(&tr_state->model_matrix, -2*GF_PI/3, 0, 1, 0);
1599 :
1600 0 : gf_node_traverse(subtree, rs);
1601 :
1602 : //side right view
1603 0 : if (mode==3) {
1604 0 : vp.x = orig_vp.x + orig_vp.width - vp.width;
1605 : } else {
1606 0 : vp.x = orig_vp.x + orig_vp.width/2+vp.width;
1607 : }
1608 0 : visual_3d_set_viewport(tr_state->visual, vp);
1609 :
1610 : gf_mx_copy(tr_state->model_matrix, mv_bck);
1611 0 : gf_mx_add_rotation(&tr_state->model_matrix, 2*GF_PI/3, 0, 1, 0);
1612 :
1613 0 : gf_node_traverse(subtree, rs);
1614 :
1615 0 : if (mode==4) {
1616 : //upper view
1617 0 : vp.x = orig_vp.x + orig_vp.width/2 - vp.width;
1618 0 : visual_3d_set_viewport(tr_state->visual, vp);
1619 :
1620 : gf_mx_copy(tr_state->model_matrix, mv_bck);
1621 0 : gf_mx_add_rotation(&tr_state->model_matrix, - GF_PI2, -axis.z, 0, axis.x);
1622 0 : gf_node_traverse(subtree, rs);
1623 :
1624 : //down view
1625 0 : vp.x = orig_vp.x + orig_vp.width/2;
1626 0 : visual_3d_set_viewport(tr_state->visual, vp);
1627 :
1628 : gf_mx_copy(tr_state->model_matrix, mv_bck);
1629 0 : gf_mx_add_rotation(&tr_state->model_matrix, GF_PI2, -axis.z, 0, axis.x);
1630 :
1631 0 : gf_node_traverse(subtree, rs);
1632 : }
1633 :
1634 0 : tr_state->camera->projection.m[0] *= -1;
1635 0 : visual_3d_projection_matrix_modified(tr_state->visual);
1636 : }
1637 :
1638 : //restore camera and VP
1639 : gf_mx_copy(tr_state->model_matrix, mv_bck);
1640 0 : visual_3d_set_viewport(tr_state->visual, orig_vp);
1641 0 : visual_3d_enable_depth_buffer(tr_state->visual, GF_TRUE);
1642 0 : tr_state->disable_partial_sphere = GF_FALSE;
1643 0 : tr_state->reverse_backface = GF_FALSE;
1644 : }
1645 :
1646 2 : void compositor_init_vrhud(GF_Compositor *compositor, GF_Node *node)
1647 : {
1648 : GF_Node *n;
1649 2 : GF_SceneGraph *sg = gf_node_get_graph(node);
1650 2 : sg = gf_sg_get_parent(sg);
1651 :
1652 2 : n = gf_sg_find_node_by_name(sg, "DYN_TRANS");
1653 2 : if (!n) {
1654 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Unable to initialize VRHUD group, no main scene\n"));
1655 : } else {
1656 2 : gf_node_set_callback_function(node, TraverseVRHUD);
1657 2 : gf_node_proto_set_grouping(node);
1658 2 : gf_node_set_private(node, n);
1659 : }
1660 2 : }
1661 :
1662 :
1663 : #endif //GPAC_DISABLE_3D
1664 :
1665 : /*hardcoded proto loading - this is mainly used for module development and testing...*/
1666 359 : void gf_sc_init_hardcoded_proto(GF_Compositor *compositor, GF_Node *node)
1667 : {
1668 : MFURL *proto_url;
1669 : GF_Proto *proto;
1670 : u32 i, j;
1671 : GF_HardcodedProto *ifce;
1672 :
1673 359 : proto = gf_node_get_proto(node);
1674 711 : if (!proto) return;
1675 359 : proto_url = gf_sg_proto_get_extern_url(proto);
1676 :
1677 363 : for (i=0; i<proto_url->count; i++) {
1678 356 : const char *url = proto_url->vals[0].url;
1679 356 : if (!url) continue;
1680 :
1681 : #ifndef GPAC_DISABLE_3D
1682 356 : if (!strcmp(url, "urn:inet:gpac:builtin:PathExtrusion")) {
1683 : compositor_init_path_extrusion(compositor, node);
1684 : return;
1685 : }
1686 355 : if (!strcmp(url, "urn:inet:gpac:builtin:PlanarExtrusion")) {
1687 : compositor_init_planar_extrusion(compositor, node);
1688 : return;
1689 : }
1690 354 : if (!strcmp(url, "urn:inet:gpac:builtin:PlaneClipper")) {
1691 2 : compositor_init_plane_clipper(compositor, node);
1692 2 : return;
1693 : }
1694 352 : if (!strcmp(url, "urn:inet:gpac:builtin:VRGeometry")) {
1695 : compositor_init_vr_geometry(compositor, node);
1696 : return;
1697 : }
1698 352 : if (!strcmp(url, "urn:inet:gpac:builtin:VRHUD")) {
1699 2 : compositor_init_vrhud(compositor, node);
1700 2 : return;
1701 : }
1702 : #endif
1703 350 : if (!strcmp(url, "urn:inet:gpac:builtin:OffscreenGroup")) {
1704 3 : compositor_init_offscreen_group(compositor, node);
1705 3 : return;
1706 : }
1707 347 : if (!strcmp(url, "urn:inet:gpac:builtin:DepthGroup")) {
1708 1 : compositor_init_depth_group(compositor, node);
1709 1 : return;
1710 : }
1711 346 : if (!strcmp(url, "urn:inet:gpac:builtin:DepthViewPoint")) {
1712 : compositor_init_depth_viewpoint(compositor, node);
1713 : return;
1714 : }
1715 345 : if (!strcmp(url, "urn:inet:gpac:builtin:IndexedCurve2D")) {
1716 : compositor_init_idx_curve2d(compositor, node);
1717 : return;
1718 : }
1719 82 : if (!strcmp(url, "urn:inet:gpac:builtin:Untransform")) {
1720 3 : compositor_init_untransform(compositor, node);
1721 3 : return;
1722 : }
1723 79 : if (!strcmp(url, "urn:inet:gpac:builtin:FlashShape")) {
1724 : #ifdef GPAC_ENABLE_FLASHSHAPE
1725 : compositor_init_hc_flashshape(compositor, node);
1726 : #endif
1727 : return;
1728 : }
1729 79 : if (!strcmp(url, "urn:inet:gpac:builtin:StyleGroup")) {
1730 73 : compositor_init_style_group(compositor, node);
1731 73 : return;
1732 : }
1733 6 : if (!strcmp(url, "urn:inet:gpac:builtin:TestSensor")) {
1734 1 : compositor_init_test_sensor(compositor, node);
1735 1 : return;
1736 : }
1737 5 : if (!strcmp(url, "urn:inet:gpac:builtin:CustomTexture")) {
1738 1 : compositor_init_custom_texture(compositor, node);
1739 1 : return;
1740 : }
1741 :
1742 :
1743 : /*check proto modules*/
1744 4 : if (compositor->proto_modules) {
1745 4 : j = 0;
1746 8 : while ( (ifce = (GF_HardcodedProto *)gf_list_enum(compositor->proto_modules, &j) )) {
1747 0 : if ( ifce->can_load_proto(url) && ifce->init(ifce, compositor, node, url) ) {
1748 : return;
1749 : }
1750 : }
1751 : }
1752 : }
1753 :
1754 : }
1755 :
1756 306 : Bool gf_sc_uri_is_hardcoded_proto(GF_Compositor *compositor, const char *uri)
1757 : {
1758 : /*check proto modules*/
1759 306 : if (compositor && compositor->proto_modules ) {
1760 306 : u32 j = 0;
1761 : GF_HardcodedProto *ifce;
1762 612 : while ( (ifce = (GF_HardcodedProto *)gf_list_enum(compositor->proto_modules, &j) )) {
1763 0 : if ( ifce->can_load_proto(uri)) {
1764 0 : return GF_TRUE;
1765 : }
1766 : }
1767 : }
1768 : return GF_FALSE;
1769 : }
1770 :
1771 302 : GF_TextureHandler *gf_sc_hardcoded_proto_get_texture_handler(GF_Node *n)
1772 : {
1773 :
1774 : MFURL *proto_url;
1775 : GF_Proto *proto;
1776 : u32 i;
1777 :
1778 302 : proto = gf_node_get_proto(n);
1779 302 : if (!proto) return NULL;
1780 302 : proto_url = gf_sg_proto_get_extern_url(proto);
1781 :
1782 302 : for (i=0; i<proto_url->count; i++) {
1783 302 : const char *url = proto_url->vals[0].url;
1784 302 : if (!strcmp(url, "urn:inet:gpac:builtin:CustomTexture")) {
1785 302 : CustomTextureStack *stack = gf_node_get_private(n);
1786 302 : if (stack) return &stack->txh;
1787 : }
1788 : }
1789 : return NULL;
1790 : }
1791 :
1792 : #endif /*GPAC_DISABLE_VRML*/
|