2
* GPAC - Multimedia Framework C SDK
4
* Copyright (c) Jean Le Feuvre 2000-2005
7
* This file is part of GPAC / Scene Compositor sub-project
9
* GPAC is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU Lesser General Public License as published by
11
* the Free Software Foundation; either version 2, or (at your option)
14
* GPAC is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU Lesser General Public License for more details.
19
* You should have received a copy of the GNU Lesser General Public
20
* License along with this library; see the file COPYING. If not, write to
21
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25
#include "nodes_stacks.h"
26
#include "visual_manager.h"
27
/*for default scene view*/
28
#include <gpac/internal/terminal_dev.h>
29
#include <gpac/options.h>
32
GF_Err gf_sc_get_viewpoint(GF_Compositor *compositor, u32 viewpoint_idx, const char **outName, Bool *is_bound)
34
#ifndef GPAC_DISABLE_VRML
37
if (!compositor->visual) return GF_BAD_PARAM;
38
count = gf_list_count(compositor->visual->view_stack);
39
if (!viewpoint_idx) return GF_BAD_PARAM;
40
if (viewpoint_idx>count) return GF_EOS;
42
n = (GF_Node*)gf_list_get(compositor->visual->view_stack, viewpoint_idx-1);
43
switch (gf_node_get_tag(n)) {
44
case TAG_MPEG4_Viewport: *outName = ((M_Viewport*)n)->description.buffer; *is_bound = ((M_Viewport*)n)->isBound;return GF_OK;
45
case TAG_MPEG4_Viewpoint:
46
#ifndef GPAC_DISABLE_X3D
47
case TAG_X3D_Viewpoint:
49
*outName = ((M_Viewpoint*)n)->description.buffer; *is_bound = ((M_Viewpoint*)n)->isBound;
51
default: *outName = NULL; return GF_OK;
54
return GF_NOT_SUPPORTED;
58
GF_Err gf_sc_set_viewpoint(GF_Compositor *compositor, u32 viewpoint_idx, const char *viewpoint_name)
60
#ifndef GPAC_DISABLE_VRML
63
if (!compositor->visual) return GF_BAD_PARAM;
64
count = gf_list_count(compositor->visual->view_stack);
65
if (viewpoint_idx>count) return GF_BAD_PARAM;
66
if (!viewpoint_idx && !viewpoint_name) return GF_BAD_PARAM;
70
n = (GF_Node*)gf_list_get(compositor->visual->view_stack, viewpoint_idx-1);
71
bind = Bindable_GetIsBound(n);
72
Bindable_SetSetBind(n, !bind);
75
for (i=0; i<count;i++) {
77
n = (GF_Node*)gf_list_get(compositor->visual->view_stack, viewpoint_idx-1);
78
switch (gf_node_get_tag(n)) {
79
case TAG_MPEG4_Viewport:
80
name = ((M_Viewport*)n)->description.buffer;
82
case TAG_MPEG4_Viewpoint:
83
name = ((M_Viewpoint*)n)->description.buffer;
85
#ifndef GPAC_DISABLE_X3D
86
case TAG_X3D_Viewpoint:
87
name = ((M_Viewpoint*)n)->description.buffer;
92
if (name && !stricmp(name, viewpoint_name)) {
93
Bool bind = Bindable_GetIsBound(n);
94
Bindable_SetSetBind(n, !bind);
100
return GF_NOT_SUPPORTED;
104
#ifndef GPAC_DISABLE_VRML
107
#define VPCHANGED(__rend) { GF_Event evt; evt.type = GF_EVENT_VIEWPOINTS; gf_term_send_event(__rend->term, &evt); }
110
static void DestroyViewStack(GF_Node *node)
112
ViewStack *st = (ViewStack *) gf_node_get_private(node);
113
PreDestroyBindable(node, st->reg_stacks);
114
gf_list_del(st->reg_stacks);
115
VPCHANGED(gf_sc_get_compositor(node));
119
static void viewport_set_bind(GF_Node *node, GF_Route *route)
121
GF_Compositor *rend = gf_sc_get_compositor(node);
122
ViewStack *st = (ViewStack *) gf_node_get_private(node);
123
Bindable_OnSetBind(node, st->reg_stacks, NULL);
125
gf_sc_invalidate(rend, NULL);
126
/*notify change of vp stack*/
128
/*and dirty ourselves to force frustrum update*/
129
gf_node_dirty_set(node, 0, 0);
133
static void TraverseViewport(GF_Node *node, void *rs, Bool is_destroy)
135
Fixed ar, sx, sy, w, h, tx, ty;
136
#ifndef GPAC_DISABLE_3D
140
GF_Rect rc, rc_bckup;
141
ViewStack *st = (ViewStack *) gf_node_get_private(node);
142
M_Viewport *vp = (M_Viewport *) node;
143
GF_TraverseState *tr_state = (GF_TraverseState *)rs;
146
DestroyViewStack(node);
150
#ifndef GPAC_DISABLE_3D
151
if (tr_state->visual->type_3d>1) return;
154
/*first traverse, bound if needed*/
155
if (gf_list_find(tr_state->viewpoints, node) < 0) {
156
gf_list_add(tr_state->viewpoints, node);
157
assert(gf_list_find(st->reg_stacks, tr_state->viewpoints)==-1);
158
gf_list_add(st->reg_stacks, tr_state->viewpoints);
160
if (gf_list_get(tr_state->viewpoints, 0) == vp) {
161
if (!vp->isBound) Bindable_SetIsBound(node, 1);
163
if (gf_inline_is_default_viewpoint(node)) Bindable_SetSetBindEx(node, 1, tr_state->viewpoints);
165
VPCHANGED(tr_state->visual->compositor);
166
/*in any case don't draw the first time (since the viewport could have been declared last)*/
167
gf_sc_invalidate(tr_state->visual->compositor, NULL);
171
if (tr_state->traversing_mode != TRAVERSE_BINDABLE) return;
172
if (!vp->isBound) return;
174
if (gf_list_get(tr_state->viewpoints, 0) != vp)
177
#ifndef GPAC_DISABLE_3D
178
if (tr_state->visual->type_3d) {
179
w = tr_state->bbox.max_edge.x - tr_state->bbox.min_edge.x;
180
h = tr_state->bbox.max_edge.y - tr_state->bbox.min_edge.y;
184
w = tr_state->bounds.width;
185
h = tr_state->bounds.height;
187
if (!w || !h) return;
190
/*if no parent this is the main viewport, don't update if not changed*/
191
// if (!tr_state->is_layer && !gf_node_dirty_get(node)) return;
193
gf_node_dirty_clear(node, 0);
196
gf_mx2d_add_translation(&mat, vp->position.x, vp->position.y);
197
gf_mx2d_add_rotation(&mat, 0, 0, vp->orientation);
199
//compute scaling ratio
200
sx = (vp->size.x>=0) ? vp->size.x : w;
201
sy = (vp->size.y>=0) ? vp->size.y : h;
202
rc = gf_rect_center(sx, sy);
203
ar = gf_divfix(h, w);
207
/*covers all area and respect aspect ratio*/
209
if (gf_divfix(rc.width, w) > gf_divfix(rc.height, h)) {
210
rc.width = gf_muldiv(rc.width, h, rc.height);
213
rc.height = gf_muldiv(rc.height , w, rc.width);
217
/*fits inside the area and respect AR*/
219
if (gf_divfix(rc.width, w)> gf_divfix(rc.height, h)) {
220
rc.height = gf_muldiv(rc.height, w, rc.width);
223
rc.width = gf_muldiv(rc.width, h, rc.height);
227
/*fit entirely: nothing to change*/
235
sx = gf_divfix(rc.width, rc_bckup.width);
236
sy = gf_divfix(rc.height, rc_bckup.height);
238
/*viewport on root visual, remove compositor scale*/
239
if (!tr_state->is_layer && (tr_state->visual->compositor->visual==tr_state->visual) ) {
240
sx = gf_divfix(sx, tr_state->visual->compositor->scale_x);
241
sy = gf_divfix(sy, tr_state->visual->compositor->scale_y);
248
if (vp->fit && vp->alignment.count) {
250
if (vp->alignment.vals[0] == -1) tx = rc.width/2 - w/2;
251
else if (vp->alignment.vals[0] == 1) tx = w/2 - rc.width/2;
253
if (vp->alignment.count>1) {
255
if (vp->alignment.vals[1]==-1) ty = rc.height/2 - h/2;
256
else if (vp->alignment.vals[1]==1) ty = h/2 - rc.height/2;
261
if (tr_state->pixel_metrics) {
262
gf_mx2d_add_scale(&mat, sx, sy);
264
/*if we are not in pixelMetrics, undo the meterMetrics->pixelMetrics transformation*/
265
gf_mx2d_add_scale(&mat, gf_divfix(sx, tr_state->min_hsize), gf_divfix(sy, tr_state->min_hsize) );
267
gf_mx2d_add_translation(&mat, tx, ty);
269
gf_mx2d_add_translation(&mat, -gf_mulfix(vp->position.x,sx), -gf_mulfix(vp->position.y,sy) );
270
gf_mx2d_add_rotation(&mat, 0, 0, vp->orientation);
272
tr_state->bounds = rc;
273
tr_state->bounds.x += tx;
274
tr_state->bounds.y += ty;
276
#ifndef GPAC_DISABLE_3D
277
if (tr_state->visual->type_3d) {
278
/*in layers directly modify the model matrix*/
279
if (tr_state->is_layer) {
280
gf_mx_from_mx2d(&mx, &mat);
281
gf_mx_add_matrix(&tr_state->model_matrix, &mx);
283
/*otherwise add to camera viewport matrix*/
285
gf_mx_from_mx2d(&tr_state->camera->viewport, &mat);
286
tr_state->camera->flags = (CAM_HAS_VIEWPORT | CAM_IS_DIRTY);
290
gf_mx2d_pre_multiply(&tr_state->transform, &mat);
293
void compositor_init_viewport(GF_Compositor *compositor, GF_Node *node)
296
GF_SAFEALLOC(ptr, ViewStack);
298
ptr->reg_stacks = gf_list_new();
300
gf_node_set_private(node, ptr);
301
gf_node_set_callback_function(node, TraverseViewport);
302
((M_Viewport*)node)->on_set_bind = viewport_set_bind;
306
#ifndef GPAC_DISABLE_3D
308
static void viewpoint_set_bind(GF_Node *node, GF_Route *route)
310
GF_Compositor *rend = gf_sc_get_compositor(node);
311
ViewStack *st = (ViewStack *) gf_node_get_private(node);
313
if (!((M_Viewpoint*)node)->isBound )
314
st->prev_was_bound = 0;
315
Bindable_OnSetBind(node, st->reg_stacks, NULL);
316
gf_sc_invalidate(rend, NULL);
317
/*notify change of vp stack*/
319
/*and dirty ourselves to force frustrum update*/
320
gf_node_dirty_set(node, 0, 0);
323
static void TraverseViewpoint(GF_Node *node, void *rs, Bool is_destroy)
328
GF_TraverseState *tr_state = (GF_TraverseState *)rs;
329
M_Viewpoint *vp = (M_Viewpoint*) node;
330
ViewStack *st = (ViewStack *) gf_node_get_private(node);
333
DestroyViewStack(node);
336
/*may happen in get_bounds*/
337
if (!tr_state->viewpoints) return;
338
// if (!tr_state->camera->is_3D) return;
340
/*first traverse, bound if needed*/
341
if (gf_list_find(tr_state->viewpoints, node) < 0) {
342
gf_list_add(tr_state->viewpoints, node);
343
assert(gf_list_find(st->reg_stacks, tr_state->viewpoints)==-1);
344
gf_list_add(st->reg_stacks, tr_state->viewpoints);
346
if (gf_list_get(tr_state->viewpoints, 0) == vp) {
347
if (!vp->isBound) Bindable_SetIsBound(node, 1);
349
if (gf_inline_is_default_viewpoint(node)) Bindable_SetSetBind(node, 1);
351
VPCHANGED(tr_state->visual->compositor);
352
/*in any case don't draw the first time (since the viewport could have been declared last)*/
353
if (tr_state->layer3d) gf_node_dirty_set(tr_state->layer3d, GF_SG_VRML_BINDABLE_DIRTY, 0);
354
gf_sc_invalidate(tr_state->visual->compositor, NULL);
356
/*not evaluating vp, return*/
357
if (tr_state->traversing_mode != TRAVERSE_BINDABLE) {
358
/*store model matrix if changed - NOTE: we always have a 1-frame delay between VP used and real world...
359
we could remove this by pre-traversing the scene before applying vp, but that would mean 2 scene
361
if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) {
362
if (!gf_mx_equal(&st->world_view_mx, &tr_state->model_matrix)) {
363
gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
364
gf_node_dirty_set(node, 0, 0);
370
/*not bound or in 2D visual*/
371
if (!vp->isBound || !tr_state->navigations) return;
373
if (!gf_node_dirty_get(node)) return;
374
gf_node_dirty_clear(node, 0);
376
/*move to local system*/
377
gf_mx_copy(mx, st->world_view_mx);
378
gf_mx_add_translation(&mx, vp->position.x, vp->position.y, vp->position.z);
379
gf_mx_add_rotation(&mx, vp->orientation.q, vp->orientation.x, vp->orientation.y, vp->orientation.z);
380
gf_mx_decompose(&mx, &pos, &v1, &ori, &v2);
382
v1.x = v1.y = v1.z = 0;
383
#ifndef GPAC_DISABLE_X3D
384
/*X3D specifies examine center*/
385
if (gf_node_get_tag(node)==TAG_X3D_Viewpoint) v1 = ((X_Viewpoint *)node)->centerOfRotation;
387
gf_mx_apply_vec(&st->world_view_mx, &v1);
388
/*set frustrum param - animate only if not bound last frame and jump false*/
389
visual_3d_viewpoint_change(tr_state, node, (!st->prev_was_bound && !vp->jump) ? 1 : 0, vp->fieldOfView, pos, ori, v1);
390
st->prev_was_bound = 1;
393
void compositor_init_viewpoint(GF_Compositor *compositor, GF_Node *node)
396
GF_SAFEALLOC(st, ViewStack);
398
st->reg_stacks = gf_list_new();
399
gf_mx_init(st->world_view_mx);
400
gf_node_set_private(node, st);
401
gf_node_set_callback_function(node, TraverseViewpoint);
402
((M_Viewpoint*)node)->on_set_bind = viewpoint_set_bind;
407
static void navinfo_set_bind(GF_Node *node, GF_Route *route)
409
ViewStack *st = (ViewStack *) gf_node_get_private(node);
410
Bindable_OnSetBind(node, st->reg_stacks, NULL);
411
gf_sc_invalidate( gf_sc_get_compositor(node), NULL);
414
static void TraverseNavigationInfo(GF_Node *node, void *rs, Bool is_destroy)
417
#ifndef GPAC_DISABLE_3D
421
GF_TraverseState *tr_state = (GF_TraverseState *)rs;
422
M_NavigationInfo *ni = (M_NavigationInfo *) node;
423
ViewStack *st = (ViewStack *) gf_node_get_private(node);
426
DestroyViewStack(node);
429
#ifdef GPAC_DISABLE_3D
431
/*FIXME, we only deal with one node, no bind stack for the current time*/
432
for (i=0; i<ni->type.count; i++) {
433
if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "NONE")) {
434
tr_state->visual->compositor->navigation_disabled = 1;
439
if (!tr_state->navigations) return;
441
/*first traverse, bound if needed*/
442
if (gf_list_find(tr_state->navigations, node) < 0) {
443
gf_list_add(tr_state->navigations, node);
444
if (gf_list_get(tr_state->navigations, 0) == ni) {
445
if (!ni->isBound) Bindable_SetIsBound(node, 1);
447
assert(gf_list_find(st->reg_stacks, tr_state->navigations)==-1);
448
gf_list_add(st->reg_stacks, tr_state->navigations);
449
gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
450
/*in any case don't draw the first time*/
451
gf_sc_invalidate(tr_state->visual->compositor, NULL);
455
if (!ni->isBound) return;
456
/*not evaluating, return*/
457
if (tr_state->traversing_mode != TRAVERSE_BINDABLE) {
458
if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) {
459
if (!gf_mx_equal(&st->world_view_mx, &tr_state->model_matrix)) {
460
gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
461
gf_node_dirty_set(node, 0, 0);
467
if (!gf_node_dirty_get(node)) return;
468
gf_node_dirty_clear(node, 0);
470
tr_state->camera->navigation_flags = 0;
471
tr_state->camera->navigate_mode = 0;
472
for (i=0; i<ni->type.count; i++) {
473
if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "ANY")) tr_state->camera->navigation_flags |= NAV_ANY;
474
if (!tr_state->camera->navigate_mode) {
475
if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "NONE")) tr_state->camera->navigate_mode = GF_NAVIGATE_NONE;
476
else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "WALK")) tr_state->camera->navigate_mode = GF_NAVIGATE_WALK;
477
else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "EXAMINE")) tr_state->camera->navigate_mode = GF_NAVIGATE_EXAMINE;
478
else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "FLY")) tr_state->camera->navigate_mode = GF_NAVIGATE_FLY;
479
else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "QTVR")) tr_state->camera->navigate_mode = GF_NAVIGATE_VR;
482
if (ni->headlight) tr_state->camera->navigation_flags |= NAV_HEADLIGHT;
484
start.x = start.y = start.z = 0;
487
gf_mx_apply_vec(&st->world_view_mx, &start);
488
gf_mx_apply_vec(&st->world_view_mx, &end);
489
gf_vec_diff(end, end, start);
490
scale = gf_vec_len(end);
492
tr_state->camera->speed = gf_mulfix(scale, ni->speed);
493
tr_state->camera->visibility = gf_mulfix(scale, ni->visibilityLimit);
494
if (ni->avatarSize.count) tr_state->camera->avatar_size.x = gf_mulfix(scale, ni->avatarSize.vals[0]);
495
if (ni->avatarSize.count>1) tr_state->camera->avatar_size.y = gf_mulfix(scale, ni->avatarSize.vals[1]);
496
if (ni->avatarSize.count>2) tr_state->camera->avatar_size.z = gf_mulfix(scale, ni->avatarSize.vals[2]);
498
if (0 && tr_state->pixel_metrics) {
499
u32 s = MAX(tr_state->visual->width, tr_state->visual->height);
501
// tr_state->camera->speed = ni->speed;
502
tr_state->camera->visibility *= s;
503
tr_state->camera->avatar_size.x *= s;
504
tr_state->camera->avatar_size.y *= s;
505
tr_state->camera->avatar_size.z *= s;
511
void compositor_init_navigation_info(GF_Compositor *compositor, GF_Node *node)
514
GF_SAFEALLOC(st, ViewStack);
516
st->reg_stacks = gf_list_new();
517
gf_node_set_private(node, st);
518
gf_node_set_callback_function(node, TraverseNavigationInfo);
519
((M_NavigationInfo*)node)->on_set_bind = navinfo_set_bind;
523
#ifndef GPAC_DISABLE_3D
525
static void fog_set_bind(GF_Node *node, GF_Route *route)
527
ViewStack *st = (ViewStack *) gf_node_get_private(node);
528
Bindable_OnSetBind(node, st->reg_stacks, NULL);
529
gf_sc_invalidate(gf_sc_get_compositor(node), NULL);
532
static void TraverseFog(GF_Node *node, void *rs, Bool is_destroy)
534
Fixed density, vrange;
538
GF_TraverseState *tr_state = (GF_TraverseState *)rs;
539
M_Fog *fog = (M_Fog *) node;
540
ViewStack *st = (ViewStack *) gf_node_get_private(node);
543
DestroyViewStack(node);
547
if (!tr_state->fogs) return;
549
/*first traverse, bound if needed*/
550
if (gf_list_find(tr_state->fogs, node) < 0) {
551
gf_list_add(tr_state->fogs, node);
552
if (gf_list_get(tr_state->fogs, 0) == fog) {
553
if (!fog->isBound) Bindable_SetIsBound(node, 1);
555
assert(gf_list_find(st->reg_stacks, tr_state->fogs)==-1);
556
gf_list_add(st->reg_stacks, tr_state->fogs);
558
gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
559
/*in any case don't draw the first time*/
560
gf_sc_invalidate(tr_state->visual->compositor, NULL);
563
/*not evaluating, return*/
564
if (tr_state->traversing_mode != TRAVERSE_BINDABLE) {
565
if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) )
566
gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
570
if (!fog->isBound || !fog->visibilityRange) return;
572
/*fog visibility is expressed in current bound VP so get its matrix*/
573
vp = (M_Viewpoint*)gf_list_get(tr_state->viewpoints, 0);
575
if (vp && vp->isBound) vp_st = (ViewStack *) gf_node_get_private((GF_Node *)vp);
577
start.x = start.y = start.z = 0;
578
end.x = end.y = 0; end.z = fog->visibilityRange;
580
gf_mx_apply_vec(&vp_st->world_view_mx, &start);
581
gf_mx_apply_vec(&vp_st->world_view_mx, &end);
583
gf_mx_apply_vec(&st->world_view_mx, &start);
584
gf_mx_apply_vec(&st->world_view_mx, &end);
585
gf_vec_diff(end, end, start);
586
vrange = gf_vec_len(end);
588
density = gf_invfix(vrange);
589
visual_3d_set_fog(tr_state->visual, fog->fogType.buffer, fog->color, density, vrange);
592
void compositor_init_fog(GF_Compositor *compositor, GF_Node *node)
595
GF_SAFEALLOC(st, ViewStack);
597
st->reg_stacks = gf_list_new();
598
gf_node_set_private(node, st);
599
gf_node_set_callback_function(node, TraverseFog);
600
((M_Fog*)node)->on_set_bind = fog_set_bind;
603
#endif /*GPAC_DISABLE_3D*/
605
#endif /*GPAC_DISABLE_VRML*/