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 :
27 :
28 : #include "nodes_stacks.h"
29 :
30 : #include "mpeg4_grouping.h"
31 : #include "visual_manager.h"
32 :
33 : #ifndef GPAC_DISABLE_VRML
34 :
35 :
36 : #ifdef GPAC_DISABLE_3D
37 :
38 : static void TraverseGroup(GF_Node *node, void *rs, Bool is_destroy)
39 : {
40 : GroupingNode2D *group = (GroupingNode2D *) gf_node_get_private(node);
41 : if (is_destroy) {
42 : gf_sc_check_focus_upon_destroy(node);
43 : gf_free(group);
44 : } else {
45 : group_2d_traverse(node, group, (GF_TraverseState*)rs);
46 : }
47 : }
48 :
49 : void compositor_init_group(GF_Compositor *compositor, GF_Node *node)
50 : {
51 : GroupingNode2D *ptr;
52 : GF_SAFEALLOC(ptr, GroupingNode2D);
53 : if (!ptr) return;
54 : gf_node_set_private(node, ptr);
55 : gf_node_set_callback_function(node, TraverseGroup);
56 : }
57 :
58 : void compositor_init_static_group(GF_Compositor *compositor, GF_Node *node)
59 : {
60 : compositor_init_group(compositor, node);
61 : }
62 :
63 : #else
64 :
65 21409 : static void TraverseGroup(GF_Node *node, void *rs, Bool is_destroy)
66 : {
67 21409 : GroupingNode *group = (GroupingNode *) gf_node_get_private(node);
68 21409 : if (is_destroy) {
69 136 : gf_sc_check_focus_upon_destroy(node);
70 136 : group_3d_delete(node);
71 : } else {
72 21273 : group_3d_traverse(node, group, (GF_TraverseState*)rs);
73 : }
74 21409 : }
75 :
76 135 : void compositor_init_group(GF_Compositor *compositor, GF_Node *node)
77 : {
78 135 : group_3d_new(node);
79 135 : gf_node_set_callback_function(node, TraverseGroup);
80 135 : }
81 :
82 :
83 : /*currently static group use the same drawing as all grouping nodes, without any opts.
84 : since we already do a lot of caching (lights, sensors) at the grouping node level, I'm not sure
85 : we could get anything better trying to optimize the rest, since what is not cached in the base grouping
86 : node always involves frustum culling, and caching that would require caching partial model matrix (from
87 : the static group to each leaf)*/
88 1 : void compositor_init_static_group(GF_Compositor *compositor, GF_Node *node)
89 : {
90 1 : group_3d_new(node);
91 1 : gf_node_set_callback_function(node, TraverseGroup);
92 1 : }
93 :
94 :
95 296 : void TraverseCollision(GF_Node *node, void *rs, Bool is_destroy)
96 : {
97 : u32 collide_flags;
98 : SFVec3f last_point;
99 : Fixed last_dist;
100 : M_Collision *col = (M_Collision *)node;
101 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
102 296 : GroupingNode *group = (GroupingNode *) gf_node_get_private(node);
103 :
104 296 : if (is_destroy) {
105 8 : gf_sc_check_focus_upon_destroy(node);
106 8 : group_3d_delete(node);
107 : return;
108 : }
109 :
110 288 : if (tr_state->traversing_mode != TRAVERSE_COLLIDE) {
111 154 : group_3d_traverse(node, group, tr_state);
112 134 : } else if (col->collide) {
113 :
114 134 : collide_flags = tr_state->camera->collide_flags;
115 134 : last_dist = tr_state->camera->collide_dist;
116 134 : tr_state->camera->collide_flags &= 0;
117 134 : tr_state->camera->collide_dist = FIX_MAX;
118 134 : last_point = tr_state->camera->collide_point;
119 :
120 134 : if (col->proxy) {
121 : /*always check bounds to update any dirty node*/
122 68 : tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
123 68 : gf_node_traverse(col->proxy, rs);
124 :
125 68 : tr_state->traversing_mode = TRAVERSE_COLLIDE;
126 68 : gf_node_traverse(col->proxy, rs);
127 : } else {
128 66 : group_3d_traverse(node, group, (GF_TraverseState*)rs);
129 : }
130 134 : if (tr_state->camera->collide_flags & CF_COLLISION) {
131 0 : col->collideTime = gf_node_get_scene_time(node);
132 0 : gf_node_event_out(node, 5/*"collideTime"*/);
133 : /*if not closer restore*/
134 0 : if (collide_flags && (last_dist<tr_state->camera->collide_dist)) {
135 0 : tr_state->camera->collide_flags = collide_flags;
136 0 : tr_state->camera->collide_dist = last_dist;
137 0 : tr_state->camera->collide_point = last_point;
138 : }
139 : } else {
140 134 : tr_state->camera->collide_flags = collide_flags;
141 134 : tr_state->camera->collide_dist = last_dist;
142 : }
143 : }
144 : }
145 :
146 8 : void compositor_init_collision(GF_Compositor *compositor, GF_Node *node)
147 : {
148 8 : group_3d_new(node);
149 8 : gf_node_set_callback_function(node, TraverseCollision);
150 8 : }
151 :
152 : /*for transform, transform2D & transformMatrix2D*/
153 : typedef struct
154 : {
155 : GROUPING_NODE_STACK_3D
156 :
157 : GF_Matrix mx;
158 : Bool has_scale;
159 : } TransformStack;
160 :
161 187 : static void DestroyTransform(GF_Node *n)
162 : {
163 187 : TransformStack *ptr = (TransformStack *)gf_node_get_private(n);
164 187 : gf_sc_check_focus_upon_destroy(n);
165 187 : gf_free(ptr);
166 187 : }
167 :
168 187 : static void NewTransformStack(GF_Compositor *compositor, GF_Node *node, GF_ChildNodeItem **children)
169 : {
170 : TransformStack *st;
171 187 : GF_SAFEALLOC(st, TransformStack);
172 187 : if (!st) return;
173 :
174 374 : gf_mx_init(st->mx);
175 187 : gf_node_set_private(node, st);
176 : }
177 :
178 58213 : static void TraverseTransform(GF_Node *n, void *rs, Bool is_destroy)
179 : {
180 : GF_Matrix gf_mx_bckup;
181 58213 : TransformStack *st = (TransformStack *)gf_node_get_private(n);
182 : M_Transform *tr = (M_Transform *)n;
183 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
184 :
185 58213 : if (is_destroy) {
186 184 : DestroyTransform(n);
187 184 : return;
188 : }
189 :
190 : /*note we don't clear dirty flag, this is done in traversing*/
191 58029 : if (gf_node_dirty_get(n) & GF_SG_NODE_DIRTY) {
192 : Bool scale_rot, recenter;
193 6010 : gf_mx_init(st->mx);
194 3005 : if (tr->translation.x || tr->translation.y || tr->translation.z)
195 1584 : gf_mx_add_translation(&st->mx, tr->translation.x, tr->translation.y, tr->translation.z);
196 3005 : recenter = (tr->center.x || tr->center.y || tr->center.z) ? 1 : 0;
197 3005 : if (recenter)
198 0 : gf_mx_add_translation(&st->mx, tr->center.x, tr->center.y, tr->center.z);
199 :
200 3005 : if (tr->rotation.q) gf_mx_add_rotation(&st->mx, tr->rotation.q, tr->rotation.x, tr->rotation.y, tr->rotation.z);
201 3005 : scale_rot = (tr->scaleOrientation.q) ? 1 : 0;
202 3005 : if (scale_rot)
203 7 : gf_mx_add_rotation(&st->mx, tr->scaleOrientation.q, tr->scaleOrientation.x, tr->scaleOrientation.y, tr->scaleOrientation.z);
204 3005 : if ((tr->scale.x != FIX_ONE) || (tr->scale.y != FIX_ONE) || (tr->scale.z != FIX_ONE))
205 743 : gf_mx_add_scale(&st->mx, tr->scale.x, tr->scale.y, tr->scale.z);
206 3005 : if (scale_rot)
207 7 : gf_mx_add_rotation(&st->mx, -tr->scaleOrientation.q, tr->scaleOrientation.x, tr->scaleOrientation.y, tr->scaleOrientation.z);
208 3005 : if (recenter)
209 0 : gf_mx_add_translation(&st->mx, -tr->center.x, -tr->center.y, -tr->center.z);
210 :
211 3005 : st->has_scale = ((tr->scale.x != FIX_ONE) || (tr->scale.y != FIX_ONE) || (tr->scale.z != FIX_ONE)) ? 1 : 0;
212 : }
213 :
214 58029 : gf_mx_copy(gf_mx_bckup, tr_state->model_matrix);
215 58029 : gf_mx_add_matrix(&tr_state->model_matrix, &st->mx);
216 :
217 : /*note we don't clear dirty flag, this is done in traversing*/
218 58029 : group_3d_traverse(n, (GroupingNode *) st, tr_state);
219 :
220 : gf_mx_copy(tr_state->model_matrix, gf_mx_bckup);
221 58029 : if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS)
222 12865 : gf_mx_apply_bbox(&st->mx, &tr_state->bbox);
223 : }
224 :
225 184 : void compositor_init_transform(GF_Compositor *compositor, GF_Node *node)
226 : {
227 184 : NewTransformStack(compositor, node, & ((M_Transform *)node)->children);
228 184 : gf_node_set_callback_function(node, TraverseTransform);
229 :
230 184 : }
231 :
232 :
233 564 : static void TraverseBillboard(GF_Node *n, void *rs, Bool is_destroy)
234 : {
235 : GF_Matrix gf_mx_bckup;
236 564 : TransformStack *st = (TransformStack *)gf_node_get_private(n);
237 : M_Billboard *bb = (M_Billboard *)n;
238 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
239 :
240 564 : if (is_destroy) {
241 3 : DestroyTransform(n);
242 3 : return;
243 : }
244 561 : if (! tr_state->camera) return;
245 :
246 : /*can't cache the matrix here*/
247 1116 : gf_mx_init(st->mx);
248 558 : if (tr_state->camera->is_3D) {
249 : SFVec3f z, axis;
250 : Fixed axis_len;
251 558 : SFVec3f user_pos = tr_state->camera->position;
252 :
253 558 : gf_mx_apply_vec(&tr_state->model_matrix, &user_pos);
254 558 : gf_vec_norm(&user_pos);
255 558 : axis = bb->axisOfRotation;
256 558 : axis_len = gf_vec_len(axis);
257 558 : if (axis_len<FIX_EPSILON) {
258 : SFVec3f x, y, t;
259 : /*get user's right in local coord*/
260 515 : gf_vec_diff(t, tr_state->camera->position, tr_state->camera->target);
261 515 : gf_vec_norm(&t);
262 515 : x = gf_vec_cross(tr_state->camera->up, t);
263 515 : gf_vec_norm(&x);
264 515 : gf_mx_rotate_vector(&tr_state->model_matrix, &x);
265 515 : gf_vec_norm(&x);
266 : /*get user's up in local coord*/
267 515 : y = tr_state->camera->up;
268 515 : gf_mx_rotate_vector(&tr_state->model_matrix, &y);
269 515 : gf_vec_norm(&y);
270 515 : z = gf_vec_cross(x, y);
271 515 : gf_vec_norm(&z);
272 :
273 515 : gf_mx_rotation_matrix_from_vectors(&st->mx, x, y, z);
274 515 : gf_mx_inverse(&st->mx);
275 : } else {
276 : SFVec3f tmp;
277 : Fixed d, cosw, sinw, angle;
278 43 : gf_vec_norm(&axis);
279 : /*map eye & z into plane with normal axis through 0.0*/
280 43 : d = -gf_vec_dot(axis, user_pos);
281 43 : tmp = gf_vec_scale(axis, d);
282 43 : gf_vec_add(user_pos, user_pos, tmp);
283 43 : gf_vec_norm(&user_pos);
284 :
285 43 : z.x = z.y = 0;
286 43 : z.z = FIX_ONE;
287 43 : d = -gf_vec_dot(axis, z);
288 43 : tmp = gf_vec_scale(axis, d);
289 43 : gf_vec_add(z, z, tmp);
290 43 : gf_vec_norm(&z);
291 :
292 43 : cosw = gf_vec_dot(user_pos, z);
293 43 : tmp = gf_vec_cross(user_pos, z);
294 43 : sinw = gf_vec_len(tmp);
295 43 : angle = gf_acos(cosw);
296 43 : gf_vec_norm(&tmp);
297 43 : if ((sinw>0) && (gf_vec_dot(axis, tmp) > 0)) gf_vec_rev(axis);
298 43 : gf_mx_add_rotation(&st->mx, angle, axis.x, axis.y, axis.z);
299 : }
300 : }
301 :
302 558 : gf_mx_copy(gf_mx_bckup, tr_state->model_matrix);
303 558 : gf_mx_add_matrix(&tr_state->model_matrix, &st->mx);
304 :
305 : /*note we don't clear dirty flag, this is done in traversing*/
306 558 : group_3d_traverse(n, (GroupingNode *) st, tr_state);
307 :
308 : gf_mx_copy(tr_state->model_matrix, gf_mx_bckup);
309 :
310 558 : if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) gf_mx_apply_bbox(&st->mx, &tr_state->bbox);
311 : }
312 :
313 3 : void compositor_init_billboard(GF_Compositor *compositor, GF_Node *node)
314 : {
315 3 : NewTransformStack(compositor, node, & ((M_Billboard *)node)->children);
316 3 : gf_node_set_callback_function(node, TraverseBillboard);
317 :
318 3 : }
319 :
320 28 : static void TraverseLOD(GF_Node *node, void *rs, Bool is_destroy)
321 : {
322 : GF_ChildNodeItem *children;
323 : MFFloat *ranges;
324 : SFVec3f pos, usr;
325 : u32 which_child, nb_children;
326 : Fixed dist;
327 : Bool do_all;
328 : GF_Matrix mx;
329 : SFVec3f center;
330 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
331 28 : s32 *prev_child = (s32 *)gf_node_get_private(node);
332 :
333 28 : if (is_destroy) {
334 1 : gf_free(prev_child);
335 1 : gf_sc_check_focus_upon_destroy(node);
336 1 : return;
337 : }
338 :
339 : /*WARNING: X3D/MPEG4 NOT COMPATIBLE*/
340 27 : if (gf_node_get_tag(node) == TAG_MPEG4_LOD) {
341 27 : children = ((M_LOD *) node)->level;
342 27 : ranges = &((M_LOD *) node)->range;
343 27 : center = ((M_LOD *) node)->center;
344 : #ifndef GPAC_DISABLE_X3D
345 : } else {
346 0 : children = ((X_LOD *) node)->children;
347 0 : ranges = &((X_LOD *) node)->range;
348 0 : center = ((X_LOD *) node)->center;
349 : #endif
350 : }
351 :
352 27 : if (!children) return;
353 27 : nb_children = gf_node_list_get_count(children);
354 :
355 27 : if (!tr_state->camera) {
356 : do_all = 1;
357 : which_child = 0;
358 : } else {
359 : /*can't cache the matrix here*/
360 26 : usr = tr_state->camera->position;
361 : pos = center;
362 26 : gf_mx_copy(mx, tr_state->model_matrix);
363 26 : gf_mx_inverse(&mx);
364 26 : gf_mx_apply_vec(&mx, &usr);
365 26 : gf_vec_diff(pos, pos, usr);
366 26 : dist = gf_vec_len(pos);
367 52 : for (which_child=0; which_child<ranges->count; which_child++) {
368 26 : if (dist<ranges->vals[which_child]) break;
369 : }
370 26 : if (which_child>=nb_children) which_child = nb_children-1;
371 :
372 : /*check if we're traversing the same child or not for audio rendering*/
373 : do_all = 0;
374 26 : if (gf_node_dirty_get(node)) {
375 1 : gf_node_dirty_clear(node, 0);
376 : do_all = 1;
377 25 : } else if ((s32) which_child != *prev_child) {
378 1 : *prev_child = which_child;
379 : do_all = 1;
380 : }
381 : }
382 :
383 : if (do_all) {
384 : u32 i;
385 3 : Bool prev_switch = tr_state->switched_off;
386 : GF_ChildNodeItem *l = children;
387 3 : tr_state->switched_off = 1;
388 : i=0;
389 12 : while (l) {
390 6 : if (i!=which_child) gf_node_traverse(l->node, rs);
391 6 : l = l->next;
392 : }
393 3 : tr_state->switched_off = prev_switch;
394 : }
395 27 : gf_node_traverse(gf_node_list_get_child(children, which_child), rs);
396 : }
397 :
398 1 : void compositor_init_lod(GF_Compositor *compositor, GF_Node *node)
399 : {
400 1 : s32 *stack = (s32*)gf_malloc(sizeof(s32));
401 1 : *stack = -1;
402 1 : gf_node_set_callback_function(node, TraverseLOD);
403 1 : gf_node_set_private(node, stack);
404 1 : }
405 :
406 : #endif /*GPAC_DISABLE_3D*/
407 :
408 :
409 : #endif /*GPAC_DISABLE_VRML*/
|