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 "visual_manager.h"
29 : #include "texturing.h"
30 : #include "nodes_stacks.h"
31 : #include <gpac/options.h>
32 :
33 : #ifndef GPAC_DISABLE_3D
34 :
35 :
36 :
37 : /*generic drawable 3D constructor/destructor*/
38 186 : Drawable3D *drawable_3d_new(GF_Node *node)
39 : {
40 : Drawable3D *tmp;
41 186 : GF_SAFEALLOC(tmp, Drawable3D);
42 186 : if (!tmp) {
43 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate drawable 3D stack\n"));
44 : return NULL;
45 : }
46 186 : tmp->mesh = new_mesh();
47 186 : gf_node_set_private(node, tmp);
48 186 : return tmp;
49 : }
50 186 : void drawable_3d_del(GF_Node *n)
51 : {
52 186 : Drawable3D *d = (Drawable3D *)gf_node_get_private(n);
53 186 : if (d) {
54 186 : if (d->mesh) mesh_free(d->mesh);
55 186 : gf_free(d);
56 : }
57 186 : gf_sc_check_focus_upon_destroy(n);
58 186 : }
59 :
60 :
61 45667 : void drawable3d_check_focus_highlight(GF_Node *node, GF_TraverseState *tr_state, GF_BBox *orig_bounds)
62 : {
63 : Drawable *hlight;
64 : GF_Node *prev_node;
65 : u32 prev_mode;
66 : GF_BBox *bounds;
67 : GF_Matrix cur;
68 45667 : GF_Compositor *compositor = tr_state->visual->compositor;
69 :
70 91334 : if (compositor->disable_focus_highlight) return;
71 :
72 45667 : if (compositor->focus_node!=node) return;
73 :
74 0 : hlight = compositor->focus_highlight;
75 0 : if (!hlight) return;
76 :
77 : /*check if focus node has changed*/
78 0 : prev_node = gf_node_get_private(hlight->node);
79 0 : if (prev_node != node) {
80 0 : gf_node_set_private(hlight->node, node);
81 :
82 0 : drawable_reset_path(hlight);
83 0 : gf_path_reset(hlight->path);
84 : }
85 : /*this is a grouping node, get its bounds*/
86 0 : if (!orig_bounds) {
87 0 : gf_mx_copy(cur, tr_state->model_matrix);
88 0 : gf_mx_init(tr_state->model_matrix);
89 0 : prev_mode = tr_state->traversing_mode;
90 0 : tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
91 0 : tr_state->bbox.is_set = 0;
92 :
93 : // gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, tr_state, 1);
94 0 : gf_node_traverse_children(node, tr_state);
95 :
96 0 : tr_state->traversing_mode = prev_mode;
97 : gf_mx_copy(tr_state->model_matrix, cur);
98 0 : bounds = &tr_state->bbox;
99 : } else {
100 : bounds = orig_bounds;
101 : }
102 0 : visual_3d_draw_bbox(tr_state, bounds, GF_FALSE);
103 : }
104 :
105 :
106 7473 : static void visual_3d_setup_traversing_state(GF_VisualManager *visual, GF_TraverseState *tr_state)
107 : {
108 7473 : tr_state->visual = visual;
109 7473 : tr_state->camera = &visual->camera;
110 : #ifndef GPAC_DISABLE_VRML
111 7473 : tr_state->backgrounds = visual->back_stack;
112 7473 : tr_state->viewpoints = visual->view_stack;
113 7473 : tr_state->fogs = visual->fog_stack;
114 7473 : tr_state->navigations = visual->navigation_stack;
115 : #endif
116 7473 : tr_state->color_mat.identity = 1;
117 7473 : tr_state->camera->vp.x = tr_state->camera->vp.y = 0;
118 7473 : tr_state->min_hsize = INT2FIX(MIN(visual->width, visual->height) / 2);
119 7473 : if (!tr_state->min_hsize) tr_state->min_hsize = FIX_ONE;
120 :
121 :
122 : /*main visual, set AR*/
123 7473 : if (visual->compositor->visual==visual) {
124 7151 : if (tr_state->visual->compositor->has_size_info) {
125 6900 : tr_state->camera->vp.x = INT2FIX(tr_state->visual->compositor->vp_x);
126 6900 : tr_state->camera->vp.y = INT2FIX(tr_state->visual->compositor->vp_y);
127 6900 : tr_state->camera->vp.width = INT2FIX(tr_state->visual->compositor->vp_width);
128 6900 : tr_state->camera->vp.height = INT2FIX(tr_state->visual->compositor->vp_height);
129 :
130 : /*2D ortho, scale is already present in the root user transform*/
131 6900 : if (visual->type_3d==0) {
132 0 : tr_state->camera->width = INT2FIX(tr_state->visual->width);
133 0 : tr_state->camera->height = INT2FIX(tr_state->visual->height);
134 : } else {
135 6900 : tr_state->camera->width = INT2FIX(tr_state->visual->compositor->vp_width);
136 6900 : tr_state->camera->height = INT2FIX(tr_state->visual->compositor->vp_height);
137 : }
138 : } else {
139 : Fixed sw, sh;
140 251 : sw = INT2FIX(tr_state->visual->compositor->vp_width);
141 251 : sh = INT2FIX(tr_state->visual->compositor->vp_height);
142 : /*AR changed, rebuild camera*/
143 :
144 251 : if (tr_state->visual->compositor->recompute_ar
145 228 : || (sw!=tr_state->camera->vp.width)
146 228 : || (sh!=tr_state->camera->vp.height)) {
147 23 : tr_state->camera->width = tr_state->camera->vp.width = INT2FIX(tr_state->visual->compositor->vp_width);
148 23 : tr_state->camera->height = tr_state->camera->vp.height = INT2FIX(tr_state->visual->compositor->vp_height);
149 23 : tr_state->camera->flags |= CAM_IS_DIRTY;
150 : }
151 : }
152 : }
153 : /*composite visual, no AR*/
154 : else {
155 322 : tr_state->camera->vp.width = tr_state->camera->width = INT2FIX(visual->width);
156 322 : tr_state->camera->vp.height = tr_state->camera->height = INT2FIX(visual->height);
157 : }
158 :
159 7473 : if (!tr_state->pixel_metrics) {
160 2930 : if (tr_state->camera->height > tr_state->camera->width) {
161 0 : tr_state->camera->height = 2*gf_divfix(tr_state->camera->height , tr_state->camera->width);
162 0 : tr_state->camera->width = 2*FIX_ONE;
163 : } else {
164 2930 : tr_state->camera->width = 2 * gf_divfix(tr_state->camera->width, tr_state->camera->height);
165 2930 : tr_state->camera->height = 2 * FIX_ONE;
166 : }
167 : }
168 : /*setup bounds*/
169 7473 : tr_state->bbox.max_edge.x = tr_state->camera->width / 2;
170 7473 : tr_state->bbox.min_edge.x = -tr_state->bbox.max_edge.x;
171 7473 : tr_state->bbox.max_edge.y = tr_state->camera->height / 2;
172 7473 : tr_state->bbox.min_edge.y = -tr_state->bbox.max_edge.y;
173 7473 : tr_state->bbox.max_edge.z = tr_state->bbox.min_edge.z = 0;
174 7473 : tr_state->bbox.is_set = 1;
175 7473 : }
176 :
177 :
178 414 : void visual_3d_viewpoint_change(GF_TraverseState *tr_state, GF_Node *vp, Bool animate_change, Fixed fieldOfView, SFVec3f position, SFRotation orientation, SFVec3f local_center)
179 : {
180 : Fixed dist;
181 : SFVec3f d;
182 :
183 :
184 : /*update znear&zfar*/
185 414 : tr_state->camera->z_near = tr_state->camera->avatar_size.x ;
186 :
187 414 : if (tr_state->camera->z_near<=0) tr_state->camera->z_near = FIX_ONE/2;
188 : /*if pixel metrics, the default znear may be way too far and lead to weird navigation*/
189 414 : else if (tr_state->camera->z_near>=FIX_ONE) tr_state->camera->z_near = FIX_ONE/2;
190 414 : tr_state->camera->z_near /= 2;
191 414 : tr_state->camera->z_far = tr_state->camera->visibility;
192 :
193 : /*z_far is selected so that an object the size of the viewport measures
194 : one pixel when located at the far plane. It can be found through the projection
195 : transformation (projection matrix) of x and y
196 : x transformation is: x'= (1/(ar*tg(fov/2)) )*x/z
197 : y transformation is: y'=(1/(tg(fov/2)))*x/z
198 :
199 : therefore when z=z_far and x=max(width/2, height/2), then
200 : x' = 1/max(vp_size.x, vp_size.y) (transformed OpenGL viewport measures one)
201 :
202 : this yields z_far = max(vp_size.x, vp_size.y) * max(width/2, height/2) * max(1/(ar*tg(fov/2)), 1/tg(fov/2))
203 : z_far = max(vp_size.x, vp_size.y) * max(width, height) / (2*min(1, ar)*tg(fov/2)) )
204 :
205 : to choose a z_far so that the size is more than one pixel, then z_far' = z_far/n_pixels*/
206 414 : if (tr_state->camera->z_far<=0) {
207 414 : Fixed ar = gf_divfix(tr_state->vp_size.x, tr_state->vp_size.y);
208 414 : if (ar>FIX_ONE) ar = FIX_ONE;
209 414 : tr_state->camera->z_far = gf_muldiv(
210 : MAX(tr_state->vp_size.x,tr_state->vp_size.y),
211 : MAX(tr_state->camera->width, tr_state->camera->height),
212 : gf_mulfix(ar*2, gf_tan(fieldOfView/2))
213 : );
214 :
215 : /*fixed-point overflow*/
216 414 : if (tr_state->camera->z_far <= tr_state->camera->z_near) {
217 0 : tr_state->camera->z_far = FIX_MAX/4;
218 : }
219 : }
220 414 : if (vp) {
221 : #if 0
222 : /*now check if vp is in pixel metrics. If not then:
223 : - either it's in the main scene, there's nothing to do
224 : - or it's in an inline, and the inline has been scaled if main scene is in pm: nothing to do*/
225 : if ( gf_sg_use_pixel_metrics(gf_node_get_graph(vp))) {
226 : GF_Matrix mx;
227 : gf_mx_init(mx);
228 : gf_mx_add_scale(&mx, tr_state->min_hsize, tr_state->min_hsize, tr_state->min_hsize);
229 : gf_mx_apply_vec(&mx, &position);
230 : gf_mx_apply_vec(&mx, &local_center);
231 : }
232 : #endif
233 : }
234 : /*default VP setup - this is undocumented in the spec. Default VP pos is (0, 0, 10) but not really nice
235 : in pixel metrics. We set z so that we see just the whole visual*/
236 212 : else if (tr_state->pixel_metrics) {
237 39 : if (tr_state->visual != tr_state->visual->compositor->visual) {
238 2 : position.z = gf_divfix(tr_state->camera->width, 2*gf_tan(fieldOfView/2) );
239 : } else {
240 37 : position.z = gf_mulfix(position.z, tr_state->min_hsize);
241 : }
242 : }
243 : #ifdef GF_SR_USE_DEPTH
244 : /* 3D world calibration for stereoscopic screen */
245 414 : if (tr_state->visual->compositor->autocal && tr_state->visual->compositor->video_out->dispdist) {
246 : Fixed dispdist, disparity;
247 :
248 : /*get view distance in pixels*/
249 0 : dispdist = tr_state->visual->compositor->video_out->dispdist * tr_state->visual->compositor->video_out->dpi_x;
250 0 : dispdist = gf_divfix(dispdist , FLT2FIX(2.54f) );
251 0 : disparity = INT2FIX(tr_state->visual->compositor->video_out->disparity);
252 :
253 0 : if (tr_state->visual->depth_vp_range) {
254 : position.z = dispdist;
255 0 : tr_state->camera->z_near = dispdist - tr_state->visual->depth_vp_position + tr_state->visual->depth_vp_range/2;
256 0 : tr_state->camera->z_far = dispdist - tr_state->visual->depth_vp_position - tr_state->visual->depth_vp_range/2;
257 : }
258 0 : else if (disparity) {
259 : /*3,4 cm = 1,3386 inch -> pixels*/
260 0 : Fixed half_interocular_dist_pixel = FLT2FIX(1.3386) * tr_state->visual->compositor->video_out->dpi_x;
261 :
262 : //frustum placed to match user's real viewpoint
263 : position.z = dispdist;
264 :
265 : //near plane will match front side of the display's stereoscopic box
266 : //-> n=D- (dD)/(e+d)
267 0 : tr_state->camera->z_near = dispdist -
268 0 : gf_divfix( gf_mulfix(disparity,dispdist), (half_interocular_dist_pixel + disparity));
269 : }
270 0 : else if (tr_state->visual->compositor->dispdepth) {
271 0 : dist = INT2FIX(tr_state->visual->compositor->dispdepth);
272 0 : if (dist<0) dist = INT2FIX(tr_state->visual->height);
273 :
274 : #if 1
275 0 : dispdist = gf_divfix(tr_state->visual->height, 2*gf_tan(fieldOfView/2) );
276 : #else
277 : dispdist = gf_muldiv(dispdist, tr_state->visual->height, tr_state->visual->compositor->video_out->max_screen_height);
278 : fieldOfView = 2*gf_atan2(tr_state->visual->height/2, dispdist);
279 : #endif
280 :
281 : //frustum placed to match user's real viewpoint
282 : position.z = dispdist;
283 0 : tr_state->camera->z_near = dispdist - 2*dist/3;
284 0 : tr_state->camera->z_far = dispdist + dist/2;
285 : }
286 : }
287 : #endif
288 :
289 414 : gf_vec_diff(d, position, local_center);
290 414 : dist = gf_vec_len(d);
291 :
292 414 : if (!dist || (dist<tr_state->camera->z_near) || (dist > tr_state->camera->z_far)) {
293 2 : if (dist > tr_state->camera->z_far)
294 0 : tr_state->camera->z_far = 2*dist;
295 :
296 2 : dist = 10 * tr_state->camera->avatar_size.x;
297 2 : if ((dist<tr_state->camera->z_near) || (dist > tr_state->camera->z_far))
298 0 : dist = (tr_state->camera->avatar_size.x + tr_state->camera->z_far) / 5;
299 : }
300 414 : tr_state->camera->vp_dist = dist;
301 414 : tr_state->camera->vp_position = position;
302 414 : tr_state->camera->vp_orientation = orientation;
303 414 : tr_state->camera->vp_fov = fieldOfView;
304 414 : tr_state->camera->examine_center = local_center;
305 :
306 414 : camera_reset_viewpoint(tr_state->camera, animate_change);
307 414 : if (tr_state->layer3d) gf_node_dirty_set(tr_state->layer3d, GF_SG_VRML_BINDABLE_DIRTY, 0);
308 414 : gf_sc_invalidate(tr_state->visual->compositor, NULL);
309 414 : }
310 :
311 9305 : void visual_3d_setup_projection(GF_TraverseState *tr_state, Bool is_layer)
312 : {
313 : #ifndef GPAC_DISABLE_VRML
314 : GF_Node *bindable;
315 : #endif
316 9305 : u32 mode = tr_state->traversing_mode;
317 9305 : tr_state->traversing_mode = TRAVERSE_BINDABLE;
318 :
319 : /*setup viewpoint (this directly modifies the frustum)*/
320 : #ifndef GPAC_DISABLE_VRML
321 9305 : bindable = (GF_Node*)gf_list_get(tr_state->viewpoints, 0);
322 9305 : if (Bindable_GetIsBound(bindable)) {
323 4917 : gf_node_traverse(bindable, tr_state);
324 4917 : tr_state->camera->had_viewpoint = 1;
325 : } else
326 : #endif
327 4388 : if (tr_state->camera->had_viewpoint) {
328 : u32 had_vp = tr_state->camera->had_viewpoint;
329 247 : tr_state->camera->had_viewpoint = 0;
330 247 : if (tr_state->camera->is_3D) {
331 : SFVec3f pos, center;
332 : SFRotation r;
333 : Fixed fov = GF_PI/4;
334 : #ifdef GF_SR_USE_DEPTH
335 : /* 3D world calibration for stereoscopic screen */
336 212 : if (tr_state->visual->compositor->autocal && tr_state->visual->compositor->video_out->dispdist) {
337 : /*get view distance in pixels*/
338 0 : Fixed dispdist = tr_state->visual->compositor->video_out->dispdist * tr_state->visual->compositor->video_out->dpi_x;
339 0 : dispdist = gf_divfix(dispdist , FLT2FIX(2.54f) );
340 :
341 0 : fov = 2*gf_atan2( INT2FIX(tr_state->visual->compositor->video_out->max_screen_width)/2, dispdist);
342 : }
343 : #endif
344 :
345 : /*default viewpoint*/
346 212 : pos.x = pos.y = 0;
347 212 : pos.z = INT2FIX(10);
348 212 : center.x = center.y = center.z = 0;
349 212 : r.q = r.x = r.z = 0;
350 212 : r.y = FIX_ONE;
351 : /*this takes care of pixelMetrics*/
352 212 : visual_3d_viewpoint_change(tr_state, NULL, 0, fov, pos, r, center);
353 : /*initial vp compute, don't animate*/
354 212 : if (had_vp == 2) {
355 212 : camera_stop_anim(tr_state->camera);
356 212 : camera_reset_viewpoint(tr_state->camera, 0);
357 : /*scene not yet ready, force a recompute of world bounds at next frame*/
358 212 : if (!is_layer && gf_sc_fit_world_to_screen(tr_state->visual->compositor) == 0) {
359 125 : tr_state->camera->had_viewpoint = 2;
360 125 : gf_sc_invalidate(tr_state->visual->compositor, NULL);
361 : }
362 : }
363 : } else {
364 35 : tr_state->camera->flags &= ~CAM_HAS_VIEWPORT;
365 35 : tr_state->camera->flags |= CAM_IS_DIRTY;
366 : }
367 : }
368 :
369 :
370 9305 : tr_state->camera_was_dirty = GF_FALSE;
371 :
372 9305 : if (tr_state->visual->nb_views>1) {
373 : s32 view_idx;
374 : Fixed interocular_dist_pixel;
375 : Fixed delta = 0;
376 :
377 830 : interocular_dist_pixel = tr_state->visual->compositor->iod + tr_state->visual->compositor->interoccular_offset;
378 :
379 830 : view_idx = tr_state->visual->current_view;
380 830 : view_idx -= tr_state->visual->nb_views/2;
381 830 : delta = interocular_dist_pixel * view_idx;
382 830 : if (! (tr_state->visual->nb_views % 2)) {
383 0 : delta += interocular_dist_pixel/2;
384 : }
385 830 : if (tr_state->visual->compositor->rview) delta = - delta;
386 :
387 830 : tr_state->camera->flags |= CAM_IS_DIRTY;
388 830 : tr_state->camera_was_dirty = GF_TRUE;
389 830 : camera_update_stereo(tr_state->camera, &tr_state->transform, tr_state->visual->center_coords, delta, tr_state->visual->compositor->video_out->dispdist, tr_state->visual->compositor->focdist, tr_state->visual->camlay);
390 : } else {
391 8475 : if (tr_state->camera->flags & CAM_IS_DIRTY) tr_state->camera_was_dirty = GF_TRUE;
392 8475 : camera_update(tr_state->camera, &tr_state->transform, tr_state->visual->center_coords);
393 : }
394 :
395 : /*setup projection (we do this to avoidloading the projection matrix for each draw operation in non-GL3/GLES2 modes
396 : modelview will be loaded at each object
397 : */
398 9305 : visual_3d_projection_matrix_modified(tr_state->visual);
399 18610 : gf_mx_init(tr_state->model_matrix);
400 :
401 9305 : tr_state->traversing_mode = mode;
402 : #ifdef GF_SR_USE_DEPTH
403 9305 : tr_state->depth_offset = 0;
404 9305 : tr_state->depth_gain = FIX_ONE;
405 : #endif
406 9305 : }
407 :
408 8742 : static void visual_3d_draw_background(GF_TraverseState *tr_state, u32 layer_type)
409 : {
410 : u32 mode;
411 : #ifndef GPAC_DISABLE_VRML
412 : GF_Node *bindable;
413 : #endif
414 :
415 : /*setup background*/
416 8742 : mode = tr_state->traversing_mode;
417 8742 : tr_state->traversing_mode = TRAVERSE_BINDABLE;
418 :
419 : /*if in layer clear z buffer (even if background)*/
420 8742 : if (layer_type) visual_3d_clear_depth(tr_state->visual);
421 :
422 : /*clear requested - do it before background drawing for layer3D (transparent background)*/
423 8742 : if (layer_type==2) {
424 : SFColor col;
425 0 : col.red = INT2FIX((tr_state->visual->compositor->back_color>>16)&0xFF) / 255;
426 0 : col.green = INT2FIX((tr_state->visual->compositor->back_color>>8)&0xFF) / 255;
427 0 : col.blue = INT2FIX((tr_state->visual->compositor->back_color)&0xFF) / 255;
428 0 : visual_3d_clear(tr_state->visual, col, 0);
429 : }
430 :
431 : #ifndef GPAC_DISABLE_VRML
432 8742 : bindable = (GF_Node*) gf_list_get(tr_state->backgrounds, 0);
433 8742 : if (Bindable_GetIsBound(bindable)) {
434 4282 : gf_node_traverse(bindable, tr_state);
435 : }
436 : /*clear if not in layer*/
437 : else
438 : #endif
439 4460 : if (!layer_type) {
440 : SFColor col;
441 : Fixed alpha = 0;
442 4042 : col.red = INT2FIX((tr_state->visual->compositor->back_color>>16)&0xFF) / 255;
443 4042 : col.green = INT2FIX((tr_state->visual->compositor->back_color>>8)&0xFF) / 255;
444 4042 : col.blue = INT2FIX((tr_state->visual->compositor->back_color)&0xFF) / 255;
445 : /*if composite visual, clear with alpha = 0*/
446 4042 : if (tr_state->visual==tr_state->visual->compositor->visual) {
447 : alpha = FIX_ONE;
448 3978 : if (tr_state->visual->compositor->init_flags & GF_TERM_WINDOW_TRANSPARENT) {
449 : alpha = 0;
450 3978 : } else if (tr_state->visual->compositor->dyn_filter_mode) {
451 : alpha = 0;
452 : }
453 : }
454 4042 : visual_3d_clear(tr_state->visual, col, alpha);
455 : }
456 8742 : tr_state->traversing_mode = mode;
457 :
458 8742 : }
459 :
460 : /*in off-axis, projection matrix is not aligned with view axis, however we need to draw the background
461 : centered !!
462 : In this case we draw the background before setting up the projection but after setting up scissor and Viewport*/
463 6797 : static void visual_3d_draw_background_on_axis(GF_TraverseState *tr_state, u32 layer_type)
464 : {
465 : GF_Matrix proj, model;
466 6797 : GF_Camera *cam = &tr_state->visual->camera;
467 6797 : Fixed ar = gf_divfix(cam->width, cam->height);
468 :
469 6797 : tr_state->visual->camlay = 0;
470 6797 : gf_mx_copy(proj, cam->projection);
471 6797 : gf_mx_copy(model, cam->modelview);
472 :
473 6797 : gf_mx_perspective(&cam->projection, cam->fieldOfView, ar, cam->z_near, cam->z_far);
474 6797 : visual_3d_projection_matrix_modified(tr_state->visual);
475 :
476 : /*setup modelview*/
477 6797 : gf_mx_lookat(&cam->modelview, cam->position, cam->target, cam->up);
478 :
479 6797 : visual_3d_draw_background(tr_state, layer_type);
480 :
481 6797 : tr_state->visual->camlay = GF_3D_CAMERA_OFFAXIS;
482 : gf_mx_copy(cam->projection, proj);
483 : gf_mx_copy(cam->modelview, model);
484 :
485 :
486 6797 : }
487 :
488 8742 : void visual_3d_init_draw(GF_TraverseState *tr_state, u32 layer_type)
489 : {
490 : #ifndef GPAC_DISABLE_VRML
491 : GF_Node *bindable;
492 : #endif
493 8742 : Bool off_axis_background = (tr_state->camera->is_3D && (tr_state->visual->camlay==GF_3D_CAMERA_OFFAXIS)) ? 1 : 0;
494 :
495 : /*if not in layer, traverse navigation node
496 : FIXME: we should update the nav info according to the world transform at the current viewpoint (vrml)*/
497 8742 : tr_state->traversing_mode = TRAVERSE_BINDABLE;
498 : #ifndef GPAC_DISABLE_VRML
499 8742 : bindable = tr_state->navigations ? (GF_Node*) gf_list_get(tr_state->navigations, 0) : NULL;
500 8742 : if (Bindable_GetIsBound(bindable)) {
501 2058 : gf_node_traverse(bindable, tr_state);
502 2058 : tr_state->camera->had_nav_info = 1;
503 : } else
504 : #endif
505 6684 : if (tr_state->camera->had_nav_info) {
506 : /*if no navigation specified, use default VRML one*/
507 123 : tr_state->camera->avatar_size.x = FLT2FIX(0.25f);
508 123 : tr_state->camera->avatar_size.y = FLT2FIX(1.6f);
509 123 : tr_state->camera->avatar_size.z = FLT2FIX(0.75f);
510 123 : tr_state->camera->visibility = 0;
511 123 : tr_state->camera->speed = FIX_ONE;
512 : /*not specified in the spec, but by default we forbid navigation in layer*/
513 123 : if (layer_type) {
514 6 : tr_state->camera->navigation_flags = NAV_HEADLIGHT;
515 6 : tr_state->camera->navigate_mode = GF_NAVIGATE_NONE;
516 : } else {
517 117 : tr_state->camera->navigation_flags = NAV_ANY | NAV_HEADLIGHT;
518 117 : if (tr_state->camera->is_3D) {
519 82 : if (tr_state->visual->compositor->nav != GF_NAVIGATE_NONE) {
520 0 : tr_state->camera->navigate_mode = tr_state->visual->compositor->nav;
521 : } else {
522 : /*X3D is by default examine, VRML/MPEG4 is WALK*/
523 82 : tr_state->camera->navigate_mode = (tr_state->visual->type_3d==3) ? GF_NAVIGATE_EXAMINE : GF_NAVIGATE_WALK;
524 : }
525 :
526 : #ifdef GF_SR_USE_DEPTH
527 : /* if (tr_state->visual->compositor->dispdepth)
528 : tr_state->camera->navigate_mode = GF_NAVIGATE_NONE;
529 : */
530 : #endif
531 : } else {
532 35 : tr_state->camera->navigate_mode = GF_NAVIGATE_NONE;
533 : }
534 : }
535 123 : tr_state->camera->had_nav_info = 0;
536 :
537 123 : if (tr_state->pixel_metrics) {
538 69 : tr_state->camera->visibility = gf_mulfix(tr_state->camera->visibility, tr_state->min_hsize);
539 69 : tr_state->camera->avatar_size.x = gf_mulfix(tr_state->camera->avatar_size.x, tr_state->min_hsize);
540 69 : tr_state->camera->avatar_size.y = gf_mulfix(tr_state->camera->avatar_size.y, tr_state->min_hsize);
541 69 : tr_state->camera->avatar_size.z = gf_mulfix(tr_state->camera->avatar_size.z, tr_state->min_hsize);
542 : }
543 : }
544 :
545 : /*animate current camera - if returns TRUE draw next frame*/
546 8742 : if (camera_animate(tr_state->camera, tr_state->visual->compositor)) {
547 106 : if (tr_state->visual->compositor->active_layer) gf_node_dirty_set(tr_state->visual->compositor->active_layer, 0, 1);
548 :
549 106 : tr_state->visual->compositor->force_next_frame_redraw = GF_TRUE;
550 : }
551 :
552 :
553 : /*turn off depth buffer in 2D*/
554 8742 : visual_3d_enable_depth_buffer(tr_state->visual, tr_state->camera->is_3D);
555 :
556 8742 : tr_state->camera->proj_vp = tr_state->camera->vp;
557 :
558 8742 : if ((tr_state->visual->autostereo_type==GF_3D_STEREO_SIDE) || (tr_state->visual->autostereo_type==GF_3D_STEREO_HEADSET)) {
559 0 : GF_Rect orig_vp = tr_state->camera->vp;
560 : Fixed vp_width = orig_vp.width;
561 : // Fixed vp_height = orig_vp.height;
562 0 : Fixed old_w = tr_state->camera->width;
563 0 : Fixed old_h = tr_state->camera->height;
564 :
565 : //fill up the entire screen matchin AR
566 0 : if (tr_state->visual->autostereo_type==GF_3D_STEREO_HEADSET) {
567 0 : Fixed max_width = INT2FIX(tr_state->visual->compositor->display_width) / tr_state->visual->nb_views;
568 0 : Fixed max_height = INT2FIX(tr_state->visual->compositor->display_height);
569 :
570 : #if 0
571 : Fixed ratio = gf_divfix(vp_width, vp_height);
572 :
573 : if (max_width < gf_mulfix(ratio, max_height) ) {
574 : tr_state->camera->vp.width = max_width;
575 : } else {
576 : tr_state->camera->vp.width = gf_mulfix(ratio, max_height);
577 : }
578 : tr_state->camera->vp.height = gf_divfix(tr_state->camera->vp.width, ratio);
579 : #else
580 : //fill max of screen
581 0 : tr_state->camera->vp.width = max_width;
582 0 : tr_state->camera->vp.height = max_height;
583 : #endif
584 0 : tr_state->camera->width = max_width;
585 0 : tr_state->camera->height = max_height;
586 :
587 0 : tr_state->camera->vp.x = (INT2FIX(tr_state->visual->compositor->display_width) - tr_state->visual->nb_views*tr_state->camera->vp.width)/2 + tr_state->visual->current_view * tr_state->camera->vp.width;
588 0 : tr_state->camera->vp.y = (INT2FIX(tr_state->visual->compositor->display_height) - tr_state->camera->vp.height)/2;
589 :
590 : } else {
591 0 : tr_state->camera->vp.width = vp_width / tr_state->visual->nb_views;
592 0 : tr_state->camera->vp.x += tr_state->visual->current_view * tr_state->camera->vp.width;
593 : }
594 : //if first view clear up the original vp
595 0 : if (!tr_state->visual->current_view) {
596 : SFColor col;
597 0 : col.red = INT2FIX((tr_state->visual->compositor->back_color>>16)&0xFF) / 255;
598 0 : col.green = INT2FIX((tr_state->visual->compositor->back_color>>8)&0xFF) / 255;
599 0 : col.blue = INT2FIX((tr_state->visual->compositor->back_color)&0xFF) / 255;
600 0 : visual_3d_set_viewport(tr_state->visual, orig_vp);
601 0 : visual_3d_clear(tr_state->visual, col, 0);
602 : }
603 :
604 0 : visual_3d_set_viewport(tr_state->visual, tr_state->camera->vp);
605 : //we must set scissor in side-by-side to avoid drawing the background outside the viewport!
606 0 : visual_3d_set_scissor(tr_state->visual, &tr_state->camera->vp);
607 :
608 0 : if (off_axis_background) {
609 0 : visual_3d_draw_background_on_axis(tr_state, layer_type);
610 : }
611 :
612 : /*setup projection*/
613 0 : visual_3d_setup_projection(tr_state, layer_type);
614 :
615 0 : tr_state->camera->proj_vp = tr_state->camera->vp;
616 0 : tr_state->camera->vp = orig_vp;
617 0 : tr_state->camera->width = old_w;
618 0 : tr_state->camera->height = old_h;
619 :
620 8742 : } else if (tr_state->visual->autostereo_type==GF_3D_STEREO_TOP) {
621 : GF_Rect orig_vp;
622 0 : orig_vp = tr_state->camera->vp;
623 :
624 0 : tr_state->camera->vp.height = tr_state->camera->vp.height / tr_state->visual->nb_views;
625 :
626 0 : if (tr_state->visual == tr_state->visual->compositor->visual) {
627 0 : Fixed remain = INT2FIX(tr_state->visual->compositor->output_height - orig_vp.height) / tr_state->visual->nb_views;
628 :
629 0 : tr_state->camera->vp.y = remain/2 + tr_state->visual->current_view*remain + tr_state->visual->current_view *tr_state->camera->vp.height;
630 : } else {
631 0 : tr_state->camera->vp.y = tr_state->visual->height - tr_state->camera->vp.y - tr_state->camera->vp.height;
632 : }
633 0 : visual_3d_set_viewport(tr_state->visual, tr_state->camera->vp);
634 : //we must set scissor in top-to-bottom to avoid drawing the background outside the viewport!
635 0 : visual_3d_set_scissor(tr_state->visual, &tr_state->camera->vp);
636 :
637 0 : if (off_axis_background) {
638 0 : visual_3d_draw_background_on_axis(tr_state, layer_type);
639 : }
640 :
641 : /*setup projection*/
642 0 : visual_3d_setup_projection(tr_state, layer_type);
643 :
644 0 : tr_state->camera->proj_vp = tr_state->camera->vp;
645 0 : tr_state->camera->vp = orig_vp;
646 : } else {
647 8742 : visual_3d_set_viewport(tr_state->visual, tr_state->camera->vp);
648 :
649 8742 : if (off_axis_background) {
650 6797 : visual_3d_draw_background_on_axis(tr_state, layer_type);
651 : }
652 :
653 : /*setup projection*/
654 8742 : visual_3d_setup_projection(tr_state, layer_type);
655 :
656 8742 : if (tr_state->visual->autostereo_type) {
657 830 : visual_3d_clear_depth(tr_state->visual);
658 : }
659 : }
660 :
661 : /*regular background draw*/
662 8742 : if (!tr_state->camera->is_3D || tr_state->visual->camlay != GF_3D_CAMERA_OFFAXIS) {
663 1945 : visual_3d_draw_background(tr_state, layer_type);
664 : }
665 :
666 : /*set headlight if any*/
667 8742 : if (tr_state->visual->type_3d>1) {
668 7101 : visual_3d_enable_headlight(tr_state->visual, (tr_state->camera->navigation_flags & NAV_HEADLIGHT) ? 1 : 0, tr_state->camera);
669 : }
670 :
671 : //reset scissor
672 8742 : visual_3d_set_scissor(tr_state->visual, NULL);
673 8742 : }
674 :
675 :
676 15846 : static GFINLINE Bool visual_3d_has_alpha(GF_TraverseState *tr_state, GF_Node *geom)
677 : {
678 : Drawable3D *stack;
679 :
680 : #ifndef GPAC_DISABLE_VRML
681 : Bool is_mat3D = GF_FALSE;
682 15846 : if (tr_state->appear) {
683 15834 : GF_Node *mat = ((M_Appearance *)tr_state->appear)->material;
684 15834 : if (mat) {
685 15024 : u32 tag = gf_node_get_tag(mat);
686 15024 : switch (tag) {
687 : /*for M2D: if filled & transparent we're transparent - otherwise we must check texture*/
688 1261 : case TAG_MPEG4_Material2D:
689 1261 : if (((M_Material2D *)mat)->filled && ((M_Material2D *)mat)->transparency) return 1;
690 : break;
691 13763 : case TAG_MPEG4_Material:
692 : #ifndef GPAC_DISABLE_X3D
693 : case TAG_X3D_Material:
694 : #endif
695 : is_mat3D = GF_TRUE;
696 13763 : if ( ((M_Material *)mat)->transparency) return 1;
697 : break;
698 : case TAG_MPEG4_MaterialKey:
699 : return 1;
700 : break;
701 : }
702 810 : } else if (tr_state->camera->is_3D) {
703 810 : GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture);
704 810 : if (txh && txh->transparent) return 1;
705 : }
706 : }
707 :
708 : /*check alpha texture in3D or with bitmap*/
709 14375 : if (tr_state->appear && ( is_mat3D || (gf_node_get_tag(geom)==TAG_MPEG4_Bitmap))) {
710 12575 : GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture);
711 12575 : if (txh && txh->transparent) return 1;
712 : }
713 :
714 : #endif /*GPAC_DISABLE_VRML*/
715 :
716 : /*TODO - FIXME check alpha only...*/
717 14117 : if (!tr_state->color_mat.identity) return 1;
718 :
719 14117 : stack = (Drawable3D *)gf_node_get_private(geom);
720 14117 : if (stack && stack->mesh && (stack->mesh->flags & MESH_HAS_ALPHA)) return 1;
721 14113 : return 0;
722 : }
723 :
724 23665 : void visual_3d_register_context(GF_TraverseState *tr_state, GF_Node *geometry)
725 : {
726 : u32 i, count;
727 : DirectionalLightContext *ol;
728 : Drawable3DContext *ctx;
729 : Drawable3D *drawable;
730 :
731 : assert(tr_state->traversing_mode == TRAVERSE_SORT);
732 :
733 23665 : drawable = (Drawable3D*)gf_node_get_private(geometry);
734 :
735 : /*if 2D draw in declared order. Otherwise, if no alpha or node is a layer, draw directly
736 : if mesh is not setup yet, consider it as opaque*/
737 23665 : if (!tr_state->camera->is_3D || !visual_3d_has_alpha(tr_state, geometry) || !drawable->mesh) {
738 21932 : tr_state->traversing_mode = TRAVERSE_DRAW_3D;
739 : /*layout/form clipper, set it in world coords only*/
740 21932 : if (tr_state->has_clip) {
741 84 : visual_3d_set_clipper_2d(tr_state->visual, tr_state->clipper, NULL);
742 : }
743 :
744 21932 : gf_node_traverse(geometry, tr_state);
745 :
746 : /*clear appearance flag once drawn in 3D - not doing so would prevent
747 : dirty flag propagation in the tree, whcih would typically prevent redrawing
748 : layers/offscreen groups when texture changes*/
749 21932 : if (tr_state->appear)
750 20479 : gf_node_dirty_clear(tr_state->appear, 0);
751 :
752 : /*back to SORT*/
753 21932 : tr_state->traversing_mode = TRAVERSE_SORT;
754 :
755 21932 : if (tr_state->has_clip) visual_3d_reset_clipper_2d(tr_state->visual);
756 22230 : return;
757 : }
758 :
759 1733 : GF_SAFEALLOC(ctx, Drawable3DContext);
760 1733 : if (!ctx) {
761 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate drawable 3D context\n"));
762 : return;
763 : }
764 :
765 1733 : ctx->directional_lights = gf_list_new();
766 1733 : ctx->geometry = geometry;
767 1733 : ctx->appearance = tr_state->appear;
768 :
769 1733 : memcpy(&ctx->model_matrix, &tr_state->model_matrix, sizeof(GF_Matrix));
770 1733 : ctx->color_mat.identity = tr_state->color_mat.identity;
771 1733 : if (!tr_state->color_mat.identity) memcpy(&ctx->color_mat, &tr_state->color_mat, sizeof(GF_ColorMatrix));
772 :
773 1733 : ctx->pixel_metrics = tr_state->pixel_metrics;
774 1733 : ctx->text_split_idx = tr_state->text_split_idx;
775 :
776 1733 : i=0;
777 3466 : while ((ol = (DirectionalLightContext*)gf_list_enum(tr_state->local_lights, &i))) {
778 0 : DirectionalLightContext *nl = (DirectionalLightContext*)gf_malloc(sizeof(DirectionalLightContext));
779 : memcpy(nl, ol, sizeof(DirectionalLightContext));
780 0 : gf_list_add(ctx->directional_lights, nl);
781 : }
782 1733 : ctx->clipper = tr_state->clipper;
783 1733 : ctx->has_clipper = tr_state->has_clip;
784 1733 : ctx->cull_flag = tr_state->cull_flag;
785 :
786 1733 : if ((ctx->num_clip_planes = tr_state->num_clip_planes))
787 0 : memcpy(ctx->clip_planes, tr_state->clip_planes, sizeof(GF_Plane)*MAX_USER_CLIP_PLANES);
788 :
789 : /*get bbox and and insert from further to closest*/
790 1733 : tr_state->bbox = drawable->mesh->bounds;
791 :
792 1733 : gf_mx_apply_bbox(&ctx->model_matrix, &tr_state->bbox);
793 1733 : gf_mx_apply_bbox(&tr_state->camera->modelview, &tr_state->bbox);
794 1733 : ctx->zmax = tr_state->bbox.max_edge.z;
795 : #ifdef GF_SR_USE_DEPTH
796 1733 : ctx->depth_offset=tr_state->depth_offset;
797 : #endif
798 :
799 : /*we don't need an exact sorting, as long as we keep transparent nodes above -note that for
800 : speed purposes we store in reverse-z transparent nodes*/
801 1733 : count = gf_list_count(tr_state->visual->alpha_nodes_to_draw);
802 2629 : for (i=0; i<count; i++) {
803 1194 : Drawable3DContext *next = (Drawable3DContext *)gf_list_get(tr_state->visual->alpha_nodes_to_draw, i);
804 1194 : if (next->zmax>ctx->zmax) {
805 298 : gf_list_insert(tr_state->visual->alpha_nodes_to_draw, ctx, i);
806 298 : return;
807 : }
808 : }
809 1435 : gf_list_add(tr_state->visual->alpha_nodes_to_draw, ctx);
810 : }
811 :
812 9547 : void visual_3d_flush_contexts(GF_VisualManager *visual, GF_TraverseState *tr_state)
813 : {
814 : u32 i, idx, count;
815 9547 : Bool pixel_metrics = tr_state->pixel_metrics;
816 :
817 9547 : tr_state->traversing_mode = TRAVERSE_DRAW_3D;
818 :
819 9547 : count = gf_list_count(visual->alpha_nodes_to_draw);
820 11280 : for (idx=0; idx<count; idx++) {
821 : DirectionalLightContext *dl;
822 1733 : Drawable3DContext *ctx = (Drawable3DContext *)gf_list_get(visual->alpha_nodes_to_draw, idx);
823 :
824 : /*apply directional lights*/
825 1733 : tr_state->local_light_on = 1;
826 1733 : i=0;
827 3466 : while ((dl = (DirectionalLightContext*)gf_list_enum(ctx->directional_lights, &i))) {
828 : GF_Matrix mx;
829 0 : gf_mx_copy(mx, tr_state->model_matrix);
830 :
831 0 : gf_mx_add_matrix(&tr_state->model_matrix, &dl->light_matrix);
832 0 : gf_node_traverse(dl->dlight, tr_state);
833 :
834 : gf_mx_copy(tr_state->model_matrix, mx);
835 : }
836 :
837 : /*clipper, set it in world coords only*/
838 1733 : if (ctx->has_clipper) {
839 : visual_3d_set_clipper_2d(visual, ctx->clipper, NULL);
840 : }
841 :
842 : /*clip planes, set it in world coords only*/
843 1733 : for (i=0; i<ctx->num_clip_planes; i++)
844 : visual_3d_set_clip_plane(visual, ctx->clip_planes[i], NULL, 0);
845 :
846 : /*restore traversing state*/
847 1733 : memcpy(&tr_state->model_matrix, &ctx->model_matrix, sizeof(GF_Matrix));
848 1733 : tr_state->color_mat.identity = ctx->color_mat.identity;
849 1733 : if (!tr_state->color_mat.identity) memcpy(&tr_state->color_mat, &ctx->color_mat, sizeof(GF_ColorMatrix));
850 1733 : tr_state->text_split_idx = ctx->text_split_idx;
851 1733 : tr_state->pixel_metrics = ctx->pixel_metrics;
852 : /*restore cull flag in case we're completely inside (avoids final frustum/AABB tree culling)*/
853 1733 : tr_state->cull_flag = ctx->cull_flag;
854 :
855 1733 : tr_state->appear = ctx->appearance;
856 : #ifdef GF_SR_USE_DEPTH
857 1733 : tr_state->depth_offset = ctx->depth_offset;
858 : #endif
859 1733 : gf_node_traverse(ctx->geometry, tr_state);
860 :
861 : /*clear appearance flag once drawn in 3D - not doing so would prevent
862 : dirty flag propagation in the tree, whcih would typically prevent redrawing
863 : layers/offscreen groups when texture changes*/
864 1733 : if (tr_state->appear) {
865 1729 : gf_node_dirty_clear(tr_state->appear, 0);
866 1729 : tr_state->appear = NULL;
867 : }
868 :
869 : /*reset directional lights*/
870 1733 : tr_state->local_light_on = 0;
871 1733 : for (i=gf_list_count(ctx->directional_lights); i>0; i--) {
872 0 : dl = (DirectionalLightContext*)gf_list_get(ctx->directional_lights, i-1);
873 0 : gf_node_traverse(dl->dlight, tr_state);
874 0 : gf_free(dl);
875 : }
876 :
877 1733 : if (ctx->has_clipper) visual_3d_reset_clipper_2d(visual);
878 1733 : for (i=0; i<ctx->num_clip_planes; i++) visual_3d_reset_clip_plane(visual);
879 :
880 : /*and destroy*/
881 1733 : gf_list_del(ctx->directional_lights);
882 1733 : gf_free(ctx);
883 : }
884 9547 : tr_state->pixel_metrics = pixel_metrics;
885 9547 : gf_list_reset(tr_state->visual->alpha_nodes_to_draw);
886 9547 : }
887 7627 : static void visual_3d_draw_node(GF_TraverseState *tr_state, GF_Node *root_node)
888 : {
889 : #ifndef GPAC_DISABLE_VRML
890 : GF_Node *fog;
891 : #endif
892 7627 : if (!tr_state->camera || !tr_state->visual) return;
893 :
894 7627 : visual_3d_init_draw(tr_state, 0);
895 :
896 : /*main visual, handle collisions*/
897 7627 : if ((tr_state->visual==tr_state->visual->compositor->visual) && tr_state->camera->is_3D)
898 5682 : visual_3d_check_collisions(tr_state, root_node, NULL);
899 :
900 : #ifndef GPAC_DISABLE_VRML
901 : /*setup fog*/
902 7627 : fog = (GF_Node*) gf_list_get(tr_state->visual->fog_stack, 0);
903 7627 : tr_state->traversing_mode = TRAVERSE_BINDABLE;
904 7627 : if (Bindable_GetIsBound(fog)) gf_node_traverse(fog, tr_state);
905 : #endif
906 :
907 : /*turn global lights on*/
908 7627 : if (tr_state->visual->type_3d>1) {
909 5986 : tr_state->traversing_mode = TRAVERSE_LIGHTING;
910 5986 : gf_node_traverse(root_node, tr_state);
911 : }
912 :
913 : /*sort graph*/
914 7627 : tr_state->traversing_mode = TRAVERSE_SORT;
915 7627 : gf_node_traverse(root_node, tr_state);
916 :
917 : /*and draw*/
918 7627 : visual_3d_flush_contexts(tr_state->visual, tr_state);
919 :
920 : /*and turn off lights*/
921 7627 : visual_3d_clear_all_lights(tr_state->visual);
922 : }
923 :
924 6659 : void visual_3d_setup_clipper(GF_VisualManager *visual, GF_TraverseState *tr_state)
925 : {
926 : GF_Rect rc;
927 : /*setup top clipper*/
928 6659 : if (visual->center_coords) {
929 6658 : rc = gf_rect_center(INT2FIX(visual->width), INT2FIX(visual->height));
930 : } else {
931 1 : rc.width = INT2FIX(visual->width);
932 1 : rc.height = INT2FIX(visual->height);
933 1 : rc.x = 0;
934 1 : rc.y = rc.height;
935 1 : if (visual->compositor->visual==visual) {
936 1 : rc.x += INT2FIX(visual->compositor->vp_x);
937 1 : rc.y += INT2FIX(visual->compositor->vp_y);
938 : }
939 : }
940 :
941 : /*setup viewport*/
942 : #ifndef GPAC_DISABLE_VRML
943 6659 : if (gf_list_count(visual->view_stack)) {
944 2706 : tr_state->traversing_mode = TRAVERSE_BINDABLE;
945 2706 : tr_state->bounds = rc;
946 2706 : gf_node_traverse((GF_Node *) gf_list_get(visual->view_stack, 0), tr_state);
947 : }
948 : #endif
949 :
950 6659 : visual->top_clipper = gf_rect_pixelize(&rc);
951 6659 : tr_state->clipper = rc;
952 13318 : gf_mx_init(tr_state->layer_matrix);
953 6659 : }
954 :
955 6963 : Bool visual_3d_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual)
956 : {
957 : #ifndef GPAC_DISABLE_LOG
958 6963 : u32 time = gf_sys_clock();
959 : #endif
960 :
961 6963 : if (visual->compositor->fbo_id)
962 6905 : compositor_3d_enable_fbo(visual->compositor, GF_TRUE);
963 :
964 6963 : visual_3d_setup(visual);
965 6963 : visual->active_glsl_flags = 0;
966 :
967 : /*setup our traversing state*/
968 6963 : visual_3d_setup_traversing_state(visual, tr_state);
969 :
970 6963 : if (is_root_visual) {
971 : Bool auto_stereo = 0;
972 :
973 6659 : visual_3d_setup_clipper(visual, tr_state);
974 :
975 6659 : if (tr_state->visual->autostereo_type > GF_3D_STEREO_LAST_SINGLE_BUFFER) {
976 166 : visual_3d_init_autostereo(visual);
977 : auto_stereo = 1;
978 : }
979 :
980 : #ifndef GPAC_USE_GLES1X
981 6659 : visual_3d_init_shaders(visual);
982 : #endif
983 :
984 13982 : for (visual->current_view=0; visual->current_view < visual->nb_views; visual->current_view++) {
985 : GF_SceneGraph *sg;
986 : u32 i;
987 7323 : visual_3d_draw_node(tr_state, root);
988 :
989 : /*extra scene graphs*/
990 7323 : i=0;
991 14646 : while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) {
992 0 : tr_state->traversing_mode = TRAVERSE_SORT;
993 0 : gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state);
994 : }
995 :
996 7323 : if (auto_stereo) {
997 830 : visual_3d_end_auto_stereo_pass(visual);
998 : }
999 :
1000 7323 : visual->compositor->reset_graphics = 0;
1001 : }
1002 :
1003 : } else {
1004 304 : visual_3d_draw_node(tr_state, root);
1005 : }
1006 6963 : GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("[RTI] Frame\t%d\t3D drawn in \t%d\tms\n", visual->compositor->frame_number, gf_sys_clock() - time));
1007 :
1008 6963 : if (visual->compositor->fbo_id)
1009 6905 : compositor_3d_enable_fbo(visual->compositor, GF_FALSE);
1010 :
1011 6963 : return 1;
1012 : }
1013 :
1014 : static void reset_collide_cursor(GF_Compositor *compositor)
1015 : {
1016 5214 : if (compositor->sensor_type == GF_CURSOR_COLLIDE) {
1017 : GF_Event evt;
1018 0 : compositor->sensor_type = evt.cursor.cursor_type = GF_CURSOR_NORMAL;
1019 0 : evt.type = GF_EVENT_SET_CURSOR;
1020 0 : compositor->video_out->ProcessEvent(compositor->video_out, &evt);
1021 : }
1022 : }
1023 :
1024 6797 : void visual_3d_check_collisions(GF_TraverseState *tr_state, GF_Node *on_node, GF_ChildNodeItem *node_list)
1025 : {
1026 : SFVec3f n, dir;
1027 : Bool go;
1028 : Fixed diff, pos_diff;
1029 :
1030 : assert(tr_state->visual && tr_state->camera);
1031 : /*don't collide on VP animations or when modes discard collision*/
1032 6797 : if ((tr_state->camera->anim_len && !tr_state->camera->jumping) || !tr_state->visual->compositor->collide_mode || (tr_state->camera->navigate_mode>=GF_NAVIGATE_EXAMINE)) {
1033 : /*reset ground flag*/
1034 1582 : tr_state->camera->last_had_ground = 0;
1035 : /*and avoid reseting move at next collision change*/
1036 1582 : tr_state->camera->last_pos = tr_state->camera->position;
1037 8325 : return;
1038 : }
1039 : /*don't collide if not moved*/
1040 5215 : if (gf_vec_equal(tr_state->camera->position, tr_state->camera->last_pos)) {
1041 5161 : reset_collide_cursor(tr_state->visual->compositor);
1042 : return;
1043 : }
1044 54 : tr_state->traversing_mode = TRAVERSE_COLLIDE;
1045 54 : tr_state->camera->collide_flags = 0;
1046 54 : tr_state->camera->collide_dist = FIX_MAX;
1047 54 : tr_state->camera->ground_dist = FIX_MAX;
1048 54 : if ((tr_state->camera->navigate_mode==GF_NAVIGATE_WALK) && tr_state->visual->compositor->gravity_on) tr_state->camera->collide_flags |= CF_DO_GRAVITY;
1049 :
1050 54 : gf_vec_diff(dir, tr_state->camera->position, tr_state->camera->last_pos);
1051 54 : pos_diff = gf_vec_len(dir);
1052 54 : gf_vec_norm(&dir);
1053 :
1054 : diff = 0;
1055 : go = 1;
1056 54 : tr_state->camera->last_had_col = 0;
1057 : /*some explanation: the current collision detection algo only checks closest distance
1058 : to objects, but doesn't attempt to track object cross during a move. If we step more than
1059 : the collision detection size, we may cross an object without detecting collision. we thus
1060 : break the move in max collision size moves*/
1061 2249 : while (go) {
1062 2142 : if (pos_diff>tr_state->camera->avatar_size.x) {
1063 2089 : pos_diff-=tr_state->camera->avatar_size.x;
1064 2089 : diff += tr_state->camera->avatar_size.x;
1065 : } else {
1066 53 : diff += pos_diff;
1067 : go = 0;
1068 : }
1069 2142 : n = gf_vec_scale(dir, diff);
1070 2142 : gf_vec_add(tr_state->camera->position, tr_state->camera->last_pos, n);
1071 2142 : if (on_node) {
1072 2142 : gf_node_traverse(on_node, tr_state);
1073 0 : } else if (node_list) {
1074 0 : while (node_list) {
1075 0 : gf_node_traverse(node_list->node, tr_state);
1076 0 : node_list = node_list->next;
1077 : }
1078 : }
1079 2142 : if (tr_state->camera->collide_flags & CF_COLLISION) break;
1080 2141 : tr_state->camera->collide_flags &= ~CF_DO_GRAVITY;
1081 : }
1082 :
1083 : /*gravity*/
1084 54 : if (tr_state->camera->collide_flags & CF_GRAVITY) {
1085 0 : diff = tr_state->camera->ground_dist - tr_state->camera->avatar_size.y;
1086 0 : if (tr_state->camera->last_had_ground && (-diff>tr_state->camera->avatar_size.z)) {
1087 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Obstacle detected - too high (dist %g)\n", FIX2FLT(diff)));
1088 0 : tr_state->camera->position = tr_state->camera->last_pos;
1089 0 : tr_state->camera->flags |= CAM_IS_DIRTY;
1090 : } else {
1091 0 : if ((tr_state->camera->jumping && fabs(diff)>tr_state->camera->dheight)
1092 0 : || (!tr_state->camera->jumping && (ABS(diff)>FIX_ONE/1000) )) {
1093 0 : tr_state->camera->last_had_ground = 1;
1094 0 : n = gf_vec_scale(tr_state->camera->up, -diff);
1095 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Ground detected camera position: %g %g %g - offset: %g %g %g (dist %g)\n",
1096 : FIX2FLT(tr_state->camera->position.x), FIX2FLT(tr_state->camera->position.y), FIX2FLT(tr_state->camera->position.z),
1097 : FIX2FLT(n.x), FIX2FLT(n.y), FIX2FLT(n.z), FIX2FLT(diff)));
1098 :
1099 0 : gf_vec_add(tr_state->camera->position, tr_state->camera->position, n);
1100 0 : gf_vec_add(tr_state->camera->target, tr_state->camera->target, n);
1101 0 : gf_vec_add(tr_state->camera->last_pos, tr_state->camera->position, n);
1102 0 : tr_state->camera->flags |= CAM_IS_DIRTY;
1103 : }
1104 : }
1105 : }
1106 : /*collsion found*/
1107 54 : if (tr_state->camera->collide_flags & CF_COLLISION) {
1108 1 : if (tr_state->visual->compositor->sensor_type != GF_CURSOR_COLLIDE) {
1109 : GF_Event evt;
1110 1 : tr_state->camera->last_had_col = 1;
1111 1 : evt.type = GF_EVENT_SET_CURSOR;
1112 1 : tr_state->visual->compositor->sensor_type = evt.cursor.cursor_type = GF_CURSOR_COLLIDE;
1113 1 : tr_state->visual->compositor->video_out->ProcessEvent(tr_state->visual->compositor->video_out, &evt);
1114 : }
1115 :
1116 : /*regular collision*/
1117 1 : if (tr_state->visual->compositor->collide_mode==GF_COLLISION_NORMAL) {
1118 0 : tr_state->camera->position = tr_state->camera->last_pos;
1119 0 : tr_state->camera->flags |= CAM_IS_DIRTY;
1120 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Collision detected - restoring previous avatar position\n"));
1121 : } else {
1122 : /*camera displacement collision*/
1123 1 : if (tr_state->camera->collide_dist) {
1124 1 : if (tr_state->camera->collide_dist>=tr_state->camera->avatar_size.x) {
1125 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Collision] Collision distance %g greater than avatar collide size %g\n", FIX2FLT(tr_state->camera->collide_dist), FIX2FLT(tr_state->camera->avatar_size.x)));
1126 :
1127 : /*safety check due to precision, always stay below collide dist*/
1128 0 : tr_state->camera->collide_dist = tr_state->camera->avatar_size.x;
1129 : }
1130 :
1131 1 : gf_vec_diff(n, tr_state->camera->position, tr_state->camera->collide_point);
1132 1 : gf_vec_norm(&n);
1133 1 : n = gf_vec_scale(n, tr_state->camera->avatar_size.x - tr_state->camera->collide_dist);
1134 1 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] offseting camera: position: %g %g %g - offset: %g %g %g\n",
1135 : FIX2FLT(tr_state->camera->position.x), FIX2FLT(tr_state->camera->position.y), FIX2FLT(tr_state->camera->position.z),
1136 : FIX2FLT(n.x), FIX2FLT(n.y), FIX2FLT(n.z)));
1137 :
1138 1 : gf_vec_add(tr_state->camera->position, tr_state->camera->position, n);
1139 1 : gf_vec_add(tr_state->camera->target, tr_state->camera->target, n);
1140 : } else {
1141 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Collision detected and camera on hit point - restoring previous avatar position\n"));
1142 0 : tr_state->camera->position = tr_state->camera->last_pos;
1143 : }
1144 1 : tr_state->camera->last_pos = tr_state->camera->position;
1145 1 : tr_state->camera->flags |= CAM_IS_DIRTY;
1146 : }
1147 : } else {
1148 53 : reset_collide_cursor(tr_state->visual->compositor);
1149 53 : tr_state->camera->last_pos = tr_state->camera->position;
1150 53 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] no collision found\n"));
1151 : }
1152 :
1153 54 : if (tr_state->camera->flags & CAM_IS_DIRTY) visual_3d_setup_projection(tr_state, 0);
1154 : }
1155 :
1156 : /*uncomment to disable frustum cull*/
1157 : //#define DISABLE_VIEW_CULL
1158 :
1159 : #ifndef GPAC_DISABLE_LOG
1160 : static const char *szPlaneNames [] =
1161 : {
1162 : "Near", "Far", "Left", "Right", "Bottom", "Top"
1163 : };
1164 : #endif
1165 :
1166 34574 : Bool visual_3d_node_cull(GF_TraverseState *tr_state, GF_BBox *bbox, Bool skip_near)
1167 : {
1168 : #ifdef DISABLE_VIEW_CULL
1169 : tr_state->cull_flag = CULL_INSIDE;
1170 : return 1;
1171 : #else
1172 : GF_BBox b;
1173 : Fixed irad, rad;
1174 : GF_Camera *cam;
1175 : Bool do_sphere;
1176 : u32 i, p_idx;
1177 : SFVec3f cdiff, vertices[8];
1178 :
1179 34574 : if (!tr_state->camera || (tr_state->cull_flag == CULL_INSIDE)) return 1;
1180 : assert(tr_state->cull_flag != CULL_OUTSIDE);
1181 :
1182 : /*empty bounds*/
1183 21356 : if (!bbox->is_set) {
1184 720 : tr_state->cull_flag = CULL_OUTSIDE;
1185 720 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node out (bbox not set)\n"));
1186 : return 0;
1187 : }
1188 :
1189 : /*get bbox sphere in world space*/
1190 20636 : b = *bbox;
1191 20636 : gf_mx_apply_bbox_sphere(&tr_state->model_matrix, &b);
1192 20636 : cam = tr_state->camera;
1193 :
1194 : /*if camera is inside bbox consider we intersect*/
1195 20636 : if (gf_bbox_point_inside(&b, &cam->position)) {
1196 423 : tr_state->cull_flag = CULL_INTERSECTS;
1197 423 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node intersect (camera in box test)\n"));
1198 : return 1;
1199 : }
1200 : /*first check: sphere vs frustum sphere intersection, this will discard far objects quite fast*/
1201 20213 : if (tr_state->camera->is_3D) {
1202 20056 : gf_vec_diff(cdiff, cam->center, b.center);
1203 20056 : rad = b.radius + cam->radius;
1204 20056 : if (gf_vec_len(cdiff) > rad) {
1205 0 : tr_state->cull_flag = CULL_OUTSIDE;
1206 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node out (sphere-sphere test)\n"));
1207 : return 0;
1208 : }
1209 : }
1210 :
1211 : /*second check: sphere vs frustum planes intersection, if any intersection is detected switch
1212 : to n/p vertex check.*/
1213 20213 : rad = b.radius;
1214 20213 : irad = -b.radius;
1215 : do_sphere = 1;
1216 :
1217 : /*skip near/far tests in ortho mode, and near in 3D*/
1218 20213 : i = (tr_state->camera->is_3D) ? (skip_near ? 1 : 0) : 2;
1219 95491 : for (; i<6; i++) {
1220 : Fixed d;
1221 103569 : if (do_sphere) {
1222 92684 : d = gf_plane_get_distance(&cam->planes[i], &b.center);
1223 92684 : if (d<irad) {
1224 0 : tr_state->cull_flag = CULL_OUTSIDE;
1225 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node out (sphere-planes test) plane %s\n", szPlaneNames[i]));
1226 : return 0;
1227 : }
1228 : /*intersect, move to n-p vertex test*/
1229 92684 : if (d<rad) {
1230 : /*get full bbox in world coords*/
1231 11306 : b = *bbox;
1232 11306 : gf_mx_apply_bbox(&tr_state->model_matrix, &b);
1233 : /*get box vertices*/
1234 11306 : gf_bbox_get_vertices(b.min_edge, b.max_edge, vertices);
1235 : do_sphere = 0;
1236 : } else {
1237 81378 : continue;
1238 : }
1239 : }
1240 22191 : p_idx = cam->p_idx[i];
1241 : /*check p-vertex: if not in plane, we're out (since p-vertex is the closest point to the plane)*/
1242 22191 : d = gf_plane_get_distance(&cam->planes[i], &vertices[p_idx]);
1243 22191 : if (d<0) {
1244 0 : tr_state->cull_flag = CULL_OUTSIDE;
1245 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node out (p-vertex test) plane %s - Distance %g\n", szPlaneNames[i], FIX2FLT(d) ));
1246 : return 0;
1247 : }
1248 :
1249 : /*check n-vertex: if not in plane, we're intersecting - don't check for near and far planes*/
1250 22191 : if (i>1) {
1251 21579 : d = gf_plane_get_distance(&cam->planes[i], &vertices[7-p_idx]);
1252 21579 : if (d<0) {
1253 8078 : tr_state->cull_flag = CULL_INTERSECTS;
1254 8078 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node intersect (n-vertex test) plane %s - Distance %g\n", szPlaneNames[i], FIX2FLT(d) ));
1255 : return 1;
1256 : }
1257 : }
1258 : }
1259 :
1260 12135 : tr_state->cull_flag = CULL_INSIDE;
1261 12135 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node inside (%s test)\n", do_sphere ? "sphere-planes" : "n-p vertex"));
1262 : return 1;
1263 : #endif
1264 : }
1265 :
1266 510 : Bool visual_3d_setup_ray(GF_VisualManager *visual, GF_TraverseState *tr_state, s32 ix, s32 iy)
1267 : {
1268 : Fixed in_x, in_y, x, y;
1269 : SFVec3f start, end;
1270 : SFVec4f res;
1271 :
1272 510 : x = INT2FIX(ix);
1273 510 : y = INT2FIX(iy);
1274 :
1275 : /*if coordinate system is not centered, move to centered coord before applying camera transform
1276 : because the (un)projection matrices include this transform*/
1277 510 : if (!visual->center_coords) {
1278 0 : x = x - INT2FIX(tr_state->camera->width)/2;
1279 0 : y = INT2FIX(tr_state->camera->height)/2 - y;
1280 : }
1281 :
1282 :
1283 : /*main visual with AR*/
1284 510 : if ((visual->compositor->visual == visual) && visual->compositor->has_size_info) {
1285 492 : Fixed scale = gf_divfix(INT2FIX(visual->width), INT2FIX(visual->compositor->vp_width));
1286 492 : x = gf_mulfix(x, scale);
1287 492 : scale = gf_divfix(INT2FIX(visual->height), INT2FIX(visual->compositor->vp_height));
1288 492 : y = gf_mulfix(y, scale);
1289 : }
1290 :
1291 : #if 0
1292 : start.z = visual->camera.z_near;
1293 : end.z = visual->camera.z_far;
1294 : if (!tr_state->camera->is_3D && !tr_state->pixel_metrics) {
1295 : start.x = end.x = gf_divfix(x, tr_state->min_hsize);
1296 : start.y = end.y = gf_divfix(y, tr_state->min_hsize);
1297 : } else {
1298 : start.x = end.x = x;
1299 : start.y = end.y = y;
1300 : }
1301 : #endif
1302 :
1303 : /*unproject to world coords*/
1304 510 : in_x = 2*x/ (s32) visual->width;
1305 510 : in_y = 2*y/ (s32) visual->height;
1306 :
1307 510 : res.x = in_x;
1308 510 : res.y = in_y;
1309 510 : res.z = -FIX_ONE/2;
1310 510 : res.q = FIX_ONE;
1311 510 : gf_mx_apply_vec_4x4(&visual->camera.unprojection, &res);
1312 510 : if (!res.q) return GF_FALSE;
1313 510 : start.x = gf_divfix(res.x, res.q);
1314 510 : start.y = gf_divfix(res.y, res.q);
1315 510 : start.z = gf_divfix(res.z, res.q);
1316 :
1317 510 : res.x = in_x;
1318 510 : res.y = in_y;
1319 510 : res.z = FIX_ONE/2;
1320 510 : res.q = FIX_ONE;
1321 510 : gf_mx_apply_vec_4x4(&visual->camera.unprojection, &res);
1322 510 : if (!res.q) return GF_FALSE;
1323 510 : end.x = gf_divfix(res.x, res.q);
1324 510 : end.y = gf_divfix(res.y, res.q);
1325 510 : end.z = gf_divfix(res.z, res.q);
1326 :
1327 510 : tr_state->ray = gf_ray(start, end);
1328 : /*also update hit info world ray in case we have a grabbed sensor with mouse off*/
1329 510 : visual->compositor->hit_world_ray = tr_state->ray;
1330 :
1331 510 : return GF_TRUE;
1332 : }
1333 :
1334 510 : void visual_3d_pick_node(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children)
1335 : {
1336 :
1337 510 : visual_3d_setup_traversing_state(visual, tr_state);
1338 510 : visual_3d_setup_projection(tr_state, 0);
1339 :
1340 :
1341 510 : if (!visual_3d_setup_ray(visual, tr_state, ev->mouse.x, ev->mouse.y))
1342 : return;
1343 :
1344 510 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] cast ray Origin %.4f %.4f %.4f Direction %.4f %.4f %.4f\n",
1345 : FIX2FLT(tr_state->ray.orig.x), FIX2FLT(tr_state->ray.orig.y), FIX2FLT(tr_state->ray.orig.z),
1346 : FIX2FLT(tr_state->ray.dir.x), FIX2FLT(tr_state->ray.dir.y), FIX2FLT(tr_state->ray.dir.z)));
1347 :
1348 :
1349 510 : visual->compositor->hit_square_dist = 0;
1350 510 : visual->compositor->hit_node = NULL;
1351 510 : gf_list_reset(visual->compositor->sensors);
1352 :
1353 : /*not the root scene, use children list*/
1354 510 : if (visual->compositor->visual != visual) {
1355 54 : while (children) {
1356 36 : gf_node_traverse(children->node, tr_state);
1357 36 : children = children->next;
1358 : }
1359 : } else {
1360 492 : gf_node_traverse(gf_sg_get_root_node(visual->compositor->scene), tr_state);
1361 : }
1362 : }
1363 :
1364 :
1365 : #ifndef GPAC_DISABLE_VRML
1366 1144 : void visual_3d_vrml_drawable_pick(GF_Node *n, GF_TraverseState *tr_state, GF_Mesh *mesh, Drawable *drawable)
1367 : {
1368 : SFVec3f local_pt, world_pt, vdiff;
1369 : SFVec3f hit_normal;
1370 : SFVec2f text_coords;
1371 : u32 i, count;
1372 : Fixed sqdist;
1373 : Bool node_is_over;
1374 : GF_Compositor *compositor;
1375 : GF_Matrix mx;
1376 : GF_Ray r;
1377 1144 : u32 cull_bckup = tr_state->cull_flag;
1378 :
1379 1935 : if (!mesh && !drawable) return;
1380 :
1381 1144 : count = gf_list_count(tr_state->vrml_sensors);
1382 1144 : compositor = tr_state->visual->compositor;
1383 :
1384 1144 : if (mesh) {
1385 910 : if (mesh->mesh_type!=MESH_TRIANGLES)
1386 : return;
1387 910 : if (!visual_3d_node_cull(tr_state, &mesh->bounds, 0)) {
1388 0 : tr_state->cull_flag = cull_bckup;
1389 0 : return;
1390 : }
1391 : }
1392 1144 : tr_state->cull_flag = cull_bckup;
1393 1144 : r = tr_state->ray;
1394 1144 : gf_mx_copy(mx, tr_state->model_matrix);
1395 1144 : gf_mx_inverse(&mx);
1396 1144 : gf_mx_apply_ray(&mx, &r);
1397 :
1398 : /*if we already have a hit point don't check anything below...*/
1399 1144 : if (compositor->hit_square_dist && !compositor->grabbed_sensor && !tr_state->layer3d) {
1400 : GF_Plane p;
1401 : GF_BBox box;
1402 179 : SFVec3f hit = compositor->hit_world_point;
1403 179 : gf_mx_apply_vec(&mx, &hit);
1404 179 : p.normal = r.dir;
1405 179 : p.d = -1 * gf_vec_dot(p.normal, hit);
1406 179 : if (mesh)
1407 179 : box = mesh->bounds;
1408 : else
1409 0 : gf_bbox_from_rect(&box, &drawable->path->bbox);
1410 :
1411 179 : if (gf_bbox_plane_relation(&box, &p) == GF_BBOX_FRONT) {
1412 132 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] bounding box of node %s (DEF %s) below current hit point - skipping\n", gf_node_get_class_name(n), gf_node_get_name(n)));
1413 132 : return;
1414 : }
1415 : }
1416 1012 : if (drawable) {
1417 : DrawAspect2D asp;
1418 : node_is_over = 0;
1419 234 : if (compositor_get_2d_plane_intersection(&r, &local_pt)) {
1420 234 : if (gf_path_point_over(drawable->path, local_pt.x, local_pt.y)) {
1421 112 : hit_normal.x = hit_normal.y = 0;
1422 112 : hit_normal.z = FIX_ONE;
1423 112 : text_coords.x = gf_divfix(local_pt.x, drawable->path->bbox.width) + FIX_ONE/2;
1424 112 : text_coords.y = gf_divfix(local_pt.y, drawable->path->bbox.height) + FIX_ONE/2;
1425 : node_is_over = 1;
1426 : }
1427 : memset(&asp, 0, sizeof(DrawAspect2D));
1428 234 : drawable_get_aspect_2d_mpeg4(drawable->node, &asp, tr_state);
1429 234 : if (asp.pen_props.width || asp.line_texture ) {
1430 70 : StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, tr_state->appear, NULL, 0, NULL);
1431 70 : if (si && si->outline && gf_path_point_over(si->outline, local_pt.x, local_pt.y)) {
1432 0 : hit_normal.x = hit_normal.y = 0;
1433 0 : hit_normal.z = FIX_ONE;
1434 0 : text_coords.x = gf_divfix(local_pt.x, si->outline->bbox.width) + FIX_ONE/2;
1435 0 : text_coords.y = gf_divfix(local_pt.y, si->outline->bbox.height) + FIX_ONE/2;
1436 : node_is_over = 1;
1437 : }
1438 : }
1439 : }
1440 : } else {
1441 778 : node_is_over = gf_mesh_intersect_ray(mesh, &r, &local_pt, &hit_normal, &text_coords);
1442 : }
1443 :
1444 1012 : if (!node_is_over) return;
1445 :
1446 : /*check distance from user and keep the closest hitpoint*/
1447 353 : world_pt = local_pt;
1448 353 : gf_mx_apply_vec(&tr_state->model_matrix, &world_pt);
1449 :
1450 353 : for (i=0; i<tr_state->num_clip_planes; i++) {
1451 0 : if (gf_plane_get_distance(&tr_state->clip_planes[i], &world_pt) < 0) {
1452 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] node %s (def %s) is not in clipper half space\n", gf_node_get_class_name(n), gf_node_get_name(n)));
1453 : return;
1454 : }
1455 : }
1456 :
1457 353 : gf_vec_diff(vdiff, world_pt, tr_state->ray.orig);
1458 353 : sqdist = gf_vec_lensq(vdiff);
1459 353 : if (compositor->hit_square_dist && (compositor->hit_square_dist+FIX_EPSILON<sqdist)) {
1460 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] node %s (def %s) is farther (%g) than current pick (%g)\n", gf_node_get_class_name(n), gf_node_get_name(n), FIX2FLT(sqdist), FIX2FLT(compositor->hit_square_dist)));
1461 : return;
1462 : }
1463 :
1464 353 : compositor->hit_square_dist = sqdist;
1465 353 : gf_list_reset(compositor->sensors);
1466 586 : for (i=0; i<count; i++) {
1467 233 : gf_list_add(compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
1468 : }
1469 :
1470 353 : gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix);
1471 353 : gf_mx_copy(compositor->hit_local_to_world, mx);
1472 353 : compositor->hit_local_point = local_pt;
1473 353 : compositor->hit_world_point = world_pt;
1474 353 : compositor->hit_world_ray = tr_state->ray;
1475 353 : compositor->hit_normal = hit_normal;
1476 353 : compositor->hit_texcoords = text_coords;
1477 :
1478 353 : if (compositor_is_composite_texture(tr_state->appear)) {
1479 0 : compositor->hit_appear = tr_state->appear;
1480 : } else {
1481 353 : compositor->hit_appear = NULL;
1482 : }
1483 353 : compositor->hit_node = n;
1484 353 : compositor->hit_use_dom_events = 0;
1485 353 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] node %s (def %s) is under mouse - hit %g %g %g\n", gf_node_get_class_name(n), gf_node_get_name(n),
1486 : FIX2FLT(world_pt.x), FIX2FLT(world_pt.y), FIX2FLT(world_pt.z)));
1487 : }
1488 :
1489 :
1490 4928 : void visual_3d_vrml_drawable_collide(GF_Node *node, GF_TraverseState *tr_state)
1491 : {
1492 : SFVec3f pos, v1, v2, collide_pt, last_pos;
1493 : Fixed dist, m_dist;
1494 : GF_Matrix mx;
1495 : u32 cull_backup;
1496 4928 : Drawable3D *st = (Drawable3D *)gf_node_get_private(node);
1497 5923 : if (!st || !st->mesh) return;
1498 :
1499 : /*no collision with lines & points*/
1500 3997 : if (st->mesh->mesh_type != MESH_TRIANGLES) return;
1501 :
1502 : #ifndef GPAC_DISABLE_VRML
1503 : /*no collision with text (vrml)*/
1504 3955 : switch (gf_node_get_tag(node)) {
1505 : case TAG_MPEG4_Text:
1506 : #ifndef GPAC_DISABLE_X3D
1507 : case TAG_X3D_Text:
1508 : #endif
1509 : return;
1510 : }
1511 : #endif
1512 :
1513 : /*cull but don't use near plane to detect objects behind us*/
1514 3955 : cull_backup = tr_state->cull_flag;
1515 3955 : if (!visual_3d_node_cull(tr_state, &st->mesh->bounds, 1)) {
1516 22 : tr_state->cull_flag = cull_backup;
1517 22 : return;
1518 : }
1519 3933 : tr_state->cull_flag = cull_backup;
1520 :
1521 : /*use up & front to get an average size of the collision dist in this space*/
1522 3933 : pos = tr_state->camera->position;
1523 3933 : last_pos = tr_state->camera->last_pos;
1524 3933 : v1 = camera_get_target_dir(tr_state->camera);
1525 3933 : v1 = gf_vec_scale(v1, tr_state->camera->avatar_size.x);
1526 3933 : gf_vec_add(v1, v1, pos);
1527 3933 : v2 = camera_get_right_dir(tr_state->camera);
1528 3933 : v2 = gf_vec_scale(v2, tr_state->camera->avatar_size.x);
1529 3933 : gf_vec_add(v2, v2, pos);
1530 :
1531 3933 : gf_mx_copy(mx, tr_state->model_matrix);
1532 3933 : gf_mx_inverse(&mx);
1533 :
1534 3933 : gf_mx_apply_vec(&mx, &pos);
1535 3933 : gf_mx_apply_vec(&mx, &last_pos);
1536 3933 : gf_mx_apply_vec(&mx, &v1);
1537 3933 : gf_mx_apply_vec(&mx, &v2);
1538 :
1539 3933 : gf_vec_diff(v1, v1, pos);
1540 3933 : gf_vec_diff(v2, v2, pos);
1541 3933 : dist = gf_vec_len(v1);
1542 3933 : m_dist = gf_vec_len(v2);
1543 3933 : if (dist<m_dist) m_dist = dist;
1544 :
1545 : /*check for any collisions*/
1546 3933 : if (gf_mesh_closest_face(st->mesh, pos, m_dist, &collide_pt)) {
1547 : /*get transformed hit*/
1548 1 : gf_mx_apply_vec(&tr_state->model_matrix, &collide_pt);
1549 1 : gf_vec_diff(v2, tr_state->camera->position, collide_pt);
1550 1 : dist = gf_vec_len(v2);
1551 1 : if (dist<tr_state->camera->collide_dist) {
1552 1 : tr_state->camera->collide_dist = dist;
1553 1 : tr_state->camera->collide_flags |= CF_COLLISION;
1554 1 : tr_state->camera->collide_point = collide_pt;
1555 :
1556 : #ifndef GPAC_DISABLE_LOG
1557 1 : if (gf_log_tool_level_on(GF_LOG_COMPOSE, GF_LOG_DEBUG)) {
1558 0 : gf_vec_diff(v1, pos, collide_pt);
1559 0 : gf_vec_norm(&v1);
1560 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] found at %g %g %g (WC) - dist (%g) - local normal %g %g %g\n",
1561 : FIX2FLT(tr_state->camera->collide_point.x), FIX2FLT(tr_state->camera->collide_point.y), FIX2FLT(tr_state->camera->collide_point.z),
1562 : FIX2FLT(dist),
1563 : FIX2FLT(v1.x), FIX2FLT(v1.y), FIX2FLT(v1.z)));
1564 : }
1565 : #endif
1566 : }
1567 : else {
1568 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Existing collision (dist %g) closer than current collsion (dist %g)\n", FIX2FLT(tr_state->camera->collide_dist), FIX2FLT(dist) ));
1569 : }
1570 : }
1571 :
1572 3933 : if (tr_state->camera->collide_flags & CF_DO_GRAVITY) {
1573 : GF_Ray r;
1574 : Bool intersect;
1575 129 : r.orig = tr_state->camera->position;
1576 129 : r.dir = gf_vec_scale(tr_state->camera->up, -FIX_ONE);
1577 129 : gf_mx_apply_ray(&mx, &r);
1578 :
1579 129 : intersect = gf_mesh_intersect_ray(st->mesh, &r, &collide_pt, &v1, NULL);
1580 :
1581 129 : if (intersect) {
1582 0 : gf_mx_apply_vec(&tr_state->model_matrix, &collide_pt);
1583 0 : gf_vec_diff(v2, tr_state->camera->position, collide_pt);
1584 0 : dist = gf_vec_len(v2);
1585 0 : if (dist<tr_state->camera->ground_dist) {
1586 0 : tr_state->camera->ground_dist = dist;
1587 0 : tr_state->camera->collide_flags |= CF_GRAVITY;
1588 0 : tr_state->camera->ground_point = collide_pt;
1589 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Ground found at %g %g %g (WC) - dist %g - local normal %g %g %g\n",
1590 : FIX2FLT(tr_state->camera->ground_point.x), FIX2FLT(tr_state->camera->ground_point.y), FIX2FLT(tr_state->camera->ground_point.z),
1591 : FIX2FLT(dist),
1592 : FIX2FLT(v1.x), FIX2FLT(v1.y), FIX2FLT(v1.z)));
1593 : }
1594 : else {
1595 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Existing ground (dist %g) closer than current (dist %g)\n", FIX2FLT(tr_state->camera->ground_dist), FIX2FLT(dist)));
1596 : }
1597 : }
1598 : }
1599 : }
1600 :
1601 : #endif /*GPAC_DISABLE_VRML*/
1602 :
1603 :
1604 5453 : static GF_TextureHandler *visual_3d_setup_texture_2d(GF_TraverseState *tr_state, DrawAspect2D *asp, GF_Mesh *mesh)
1605 : {
1606 5453 : if (!asp->fill_texture) return NULL;
1607 :
1608 1550 : if (asp->fill_color && (GF_COL_A(asp->fill_color) != 0xFF)) {
1609 6 : visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color);
1610 6 : gf_sc_texture_set_blend_mode(asp->fill_texture, TX_MODULATE);
1611 : } else {
1612 1544 : visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0);
1613 1544 : gf_sc_texture_set_blend_mode(asp->fill_texture, TX_REPLACE);
1614 : }
1615 :
1616 1550 : if (asp->fill_texture->flags & GF_SR_TEXTURE_SVG) {
1617 : GF_Rect rc;
1618 0 : gf_rect_from_bbox(&rc, &mesh->bounds);
1619 0 : tr_state->mesh_num_textures = gf_sc_texture_enable_ex(asp->fill_texture, NULL, &rc);
1620 : } else {
1621 : #ifndef GPAC_DISABLE_VRML
1622 1550 : tr_state->mesh_num_textures = gf_sc_texture_enable(asp->fill_texture, tr_state->appear ? ((M_Appearance *)tr_state->appear)->textureTransform : NULL);
1623 : #endif
1624 : }
1625 1550 : if (tr_state->mesh_num_textures) return asp->fill_texture;
1626 : return NULL;
1627 : }
1628 :
1629 2245 : void visual_3d_set_2d_strike(GF_TraverseState *tr_state, DrawAspect2D *asp)
1630 : {
1631 2245 : if (asp->line_texture) {
1632 : GF_Node *txtrans = NULL;
1633 : #ifndef GPAC_DISABLE_VRML
1634 877 : if (tr_state->appear
1635 877 : && (gf_node_get_tag( ((M_Appearance *)tr_state->appear)->material) == TAG_MPEG4_Material2D)
1636 877 : && (gf_node_get_tag(((M_Material2D *) ((M_Appearance *)tr_state->appear)->material)->lineProps) == TAG_MPEG4_XLineProperties)
1637 : ) {
1638 877 : txtrans = ((M_XLineProperties *) ((M_Material2D *) ((M_Appearance *)tr_state->appear)->material)->lineProps)->textureTransform;
1639 : }
1640 : #endif
1641 :
1642 : /*We forgot to specify this in the spec ...*/
1643 877 : gf_sc_texture_set_blend_mode(asp->line_texture, TX_REPLACE);
1644 : #if 0
1645 : if (asp->line_alpha != FIX_ONE) {
1646 : visual_3d_set_material_2d(tr_state->visual, asp->line_color, asp->line_alpha);
1647 : gf_sc_texture_set_blend_mode(asp->txh, TX_MODULATE);
1648 : } else {
1649 : visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0);
1650 : gf_sc_texture_set_blend_mode(asp->txh, TX_REPLACE);
1651 : }
1652 : #endif
1653 877 : tr_state->mesh_num_textures = gf_sc_texture_enable(asp->line_texture, txtrans);
1654 877 : if (tr_state->mesh_num_textures) return;
1655 : }
1656 : /*no texture or not ready, use color*/
1657 1408 : if (asp->line_color)
1658 1408 : visual_3d_set_material_2d_argb(tr_state->visual, asp->line_color);
1659 : }
1660 :
1661 :
1662 5453 : void visual_3d_draw_2d_with_aspect(Drawable *st, GF_TraverseState *tr_state, DrawAspect2D *asp)
1663 : {
1664 : StrikeInfo2D *si;
1665 : GF_TextureHandler *fill_txh;
1666 :
1667 5453 : fill_txh = visual_3d_setup_texture_2d(tr_state, asp, st->mesh);
1668 :
1669 : /*fill path*/
1670 5453 : if (fill_txh || (GF_COL_A(asp->fill_color)) ) {
1671 3945 : if (!st->mesh) return;
1672 :
1673 3945 : if (asp->fill_color)
1674 2696 : visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color);
1675 1249 : else if (GF_COL_A(asp->line_color) && !(asp->line_color & 0x00FFFFFF)) {
1676 : u32 col = asp->line_color | 0x00FFFFFF;
1677 151 : visual_3d_set_material_2d_argb(tr_state->visual, col);
1678 : }
1679 :
1680 3945 : visual_3d_mesh_paint(tr_state, st->mesh);
1681 : /*reset texturing in case of line texture*/
1682 3945 : if (tr_state->mesh_num_textures) {
1683 1257 : gf_sc_texture_disable(fill_txh);
1684 1257 : tr_state->mesh_num_textures = 0;
1685 : }
1686 : }
1687 :
1688 5453 : if ((tr_state->visual->type_3d == 4) && !asp->line_texture) return;
1689 :
1690 : /*strike path*/
1691 4902 : if (!asp->pen_props.width || !GF_COL_A(asp->line_color)) return;
1692 :
1693 2109 : si = drawable_get_strikeinfo(tr_state->visual->compositor, st, asp, tr_state->appear, NULL, 0, tr_state);
1694 2109 : if (!si) return;
1695 :
1696 2109 : if (!si->mesh_outline) {
1697 759 : si->is_vectorial = asp->line_texture ? GF_TRUE : !tr_state->visual->compositor->linegl;
1698 759 : si->mesh_outline = new_mesh();
1699 : #ifdef GPAC_HAS_GLU
1700 759 : if (si->is_vectorial) {
1701 759 : gf_mesh_tesselate_path(si->mesh_outline, si->outline, asp->line_texture ? 2 : 1);
1702 : } else
1703 : #endif
1704 0 : mesh_get_outline(si->mesh_outline, st->path);
1705 : }
1706 :
1707 2109 : visual_3d_set_2d_strike(tr_state, asp);
1708 2109 : if (asp->line_texture) tr_state->mesh_num_textures = 1;
1709 :
1710 2109 : if (!si->is_vectorial) {
1711 0 : visual_3d_mesh_strike(tr_state, si->mesh_outline, asp->pen_props.width, asp->line_scale, asp->pen_props.dash);
1712 : } else {
1713 2109 : visual_3d_mesh_paint(tr_state, si->mesh_outline);
1714 : }
1715 2109 : if (asp->line_texture) {
1716 877 : gf_sc_texture_disable(asp->line_texture);
1717 877 : tr_state->mesh_num_textures = 0;
1718 : }
1719 : }
1720 :
1721 : #ifndef GPAC_DISABLE_VRML
1722 5106 : void visual_3d_draw_2d(Drawable *st, GF_TraverseState *tr_state)
1723 : {
1724 : DrawAspect2D asp;
1725 : memset(&asp, 0, sizeof(DrawAspect2D));
1726 5106 : drawable_get_aspect_2d_mpeg4(st->node, &asp, tr_state);
1727 5106 : visual_3d_draw_2d_with_aspect(st, tr_state, &asp);
1728 5106 : }
1729 : #endif
1730 :
1731 :
1732 130 : void visual_3d_draw_from_context(DrawableContext *ctx, GF_TraverseState *tr_state)
1733 : {
1734 : GF_Rect rc;
1735 130 : gf_path_get_bounds(ctx->drawable->path, &rc);
1736 130 : visual_3d_draw_2d_with_aspect(ctx->drawable, tr_state, &ctx->aspect);
1737 :
1738 130 : drawable_check_focus_highlight(ctx->drawable->node, tr_state, &rc);
1739 130 : }
1740 :
1741 :
1742 15665 : static GFINLINE Bool visual_3d_setup_material(GF_TraverseState *tr_state, u32 mesh_type, Fixed *diffuse_alpha)
1743 : {
1744 : #ifndef GPAC_DISABLE_VRML
1745 : GF_Node *__mat;
1746 : #endif
1747 : SFColor def;
1748 : def.red = def.green = def.blue = FIX_ONE;
1749 : /*store diffuse alpha*/
1750 15665 : if (diffuse_alpha) *diffuse_alpha = FIX_ONE;
1751 :
1752 15665 : if (!tr_state->appear) {
1753 : /*use material2D to disable lighting*/
1754 1118 : visual_3d_set_material_2d(tr_state->visual, def, FIX_ONE);
1755 1118 : return 1;
1756 : }
1757 :
1758 : #ifndef GPAC_DISABLE_VRML
1759 : #ifndef GPAC_DISABLE_X3D
1760 14547 : if (gf_node_get_tag(tr_state->appear)==TAG_X3D_Appearance) {
1761 2085 : X_FillProperties *fp = (X_FillProperties *) ((X_Appearance*)tr_state->appear)->fillProperties;
1762 2085 : if (fp && !fp->filled) return 0;
1763 : }
1764 : #endif
1765 :
1766 14547 : __mat = ((M_Appearance *)tr_state->appear)->material;
1767 14547 : if (!__mat) {
1768 : /*use material2D to disable lighting (cf VRML specs)*/
1769 682 : visual_3d_set_material_2d(tr_state->visual, def, FIX_ONE);
1770 682 : return 1;
1771 : }
1772 :
1773 13865 : switch (gf_node_get_tag((GF_Node *)__mat)) {
1774 13865 : case TAG_MPEG4_Material:
1775 : #ifndef GPAC_DISABLE_X3D
1776 : case TAG_X3D_Material:
1777 : #endif
1778 : {
1779 : SFColor diff, spec, emi;
1780 : Fixed diff_a, spec_a, emi_a;
1781 : Fixed vec[4];
1782 : Bool has_alpha;
1783 : u32 flag = V3D_STATE_LIGHT /*| V3D_STATE_COLOR*/;
1784 : M_Material *mat = (M_Material *)__mat;
1785 :
1786 13865 : diff = mat->diffuseColor;
1787 13865 : diff_a = FIX_ONE - mat->transparency;
1788 :
1789 : /*if drawing in 2D context or special meshes (lines, points) disable lighting*/
1790 13865 : if (mesh_type || !tr_state->camera->is_3D) {
1791 123 : if (tr_state->camera->is_3D) diff = mat->emissiveColor;
1792 123 : if (!tr_state->color_mat.identity) gf_cmx_apply_fixed(&tr_state->color_mat, &diff_a, &diff.red, &diff.green, &diff.blue);
1793 123 : visual_3d_set_material_2d(tr_state->visual, diff, diff_a);
1794 131 : return 1;
1795 : }
1796 :
1797 13742 : spec = mat->specularColor;
1798 13742 : emi = mat->emissiveColor;
1799 13742 : spec_a = emi_a = FIX_ONE - mat->transparency;
1800 13742 : if (!tr_state->color_mat.identity) {
1801 0 : gf_cmx_apply_fixed(&tr_state->color_mat, &diff_a, &diff.red, &diff.green, &diff.blue);
1802 0 : gf_cmx_apply_fixed(&tr_state->color_mat, &spec_a, &spec.red, &spec.green, &spec.blue);
1803 0 : gf_cmx_apply_fixed(&tr_state->color_mat, &emi_a, &emi.red, &emi.green, &emi.blue);
1804 :
1805 0 : if ((diff_a+FIX_EPSILON<FIX_ONE) || (spec_a+FIX_EPSILON<FIX_ONE) || (emi_a+FIX_EPSILON<FIX_ONE )) {
1806 : has_alpha = 1;
1807 : } else {
1808 : has_alpha = 0;
1809 : }
1810 : } else {
1811 13742 : has_alpha = (mat->transparency>FIX_EPSILON) ? 1 : 0;
1812 : /*100% transparent DON'T DRAW*/
1813 13742 : if (mat->transparency+FIX_EPSILON>=FIX_ONE) return 0;
1814 : }
1815 :
1816 : /*using antialiasing with alpha usually gives bad results (non-edge face segments are visible)*/
1817 13734 : visual_3d_enable_antialias(tr_state->visual, !has_alpha);
1818 13734 : if (has_alpha) {
1819 : flag |= V3D_STATE_BLEND;
1820 1308 : tr_state->mesh_is_transparent = 1;
1821 : }
1822 13734 : visual_3d_set_state(tr_state->visual, flag, 1);
1823 :
1824 13734 : vec[0] = gf_mulfix(diff.red, mat->ambientIntensity);
1825 13734 : vec[1] = gf_mulfix(diff.green, mat->ambientIntensity);
1826 13734 : vec[2] = gf_mulfix(diff.blue, mat->ambientIntensity);
1827 13734 : vec[3] = diff_a;
1828 13734 : visual_3d_set_material(tr_state->visual, V3D_MATERIAL_AMBIENT, vec);
1829 :
1830 : vec[0] = diff.red;
1831 : vec[1] = diff.green;
1832 : vec[2] = diff.blue;
1833 : vec[3] = diff_a;
1834 13734 : visual_3d_set_material(tr_state->visual, V3D_MATERIAL_DIFFUSE, vec);
1835 :
1836 13734 : vec[0] = spec.red;
1837 13734 : vec[1] = spec.green;
1838 13734 : vec[2] = spec.blue;
1839 13734 : vec[3] = spec_a;
1840 13734 : visual_3d_set_material(tr_state->visual, V3D_MATERIAL_SPECULAR, vec);
1841 :
1842 :
1843 13734 : vec[0] = emi.red;
1844 13734 : vec[1] = emi.green;
1845 13734 : vec[2] = emi.blue;
1846 13734 : vec[3] = emi_a;
1847 13734 : visual_3d_set_material(tr_state->visual, V3D_MATERIAL_EMISSIVE, vec);
1848 :
1849 13734 : visual_3d_set_shininess(tr_state->visual, mat->shininess);
1850 13734 : if (diffuse_alpha) *diffuse_alpha = diff_a;
1851 : }
1852 13734 : break;
1853 0 : case TAG_MPEG4_Material2D:
1854 : {
1855 : SFColor emi;
1856 : Fixed emi_a;
1857 : M_Material2D *mat = (M_Material2D *)__mat;
1858 :
1859 0 : emi = mat->emissiveColor;
1860 0 : emi_a = FIX_ONE - mat->transparency;
1861 0 : if (!tr_state->color_mat.identity) gf_cmx_apply_fixed(&tr_state->color_mat, &emi_a, &emi.red, &emi.green, &emi.blue);
1862 : /*100% transparent DON'T DRAW*/
1863 0 : if (emi_a<FIX_EPSILON) return 0;
1864 0 : else if (emi_a+FIX_EPSILON<FIX_ONE) visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 1);
1865 :
1866 :
1867 : /*this is an extra feature: if material2D.filled is FALSE on 3D objects, switch to TX_REPLACE mode
1868 : and enable lighting*/
1869 0 : if (!mat->filled) {
1870 0 : if (mat->transparency) {
1871 0 : emi.red = emi.green = emi.blue = FIX_ONE;
1872 : } else {
1873 0 : GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture);
1874 0 : if (txh) {
1875 0 : gf_sc_texture_set_blend_mode(txh, TX_REPLACE);
1876 0 : visual_3d_set_state(tr_state->visual, V3D_STATE_COLOR, 0);
1877 0 : visual_3d_set_state(tr_state->visual, V3D_STATE_LIGHT, 1);
1878 0 : return 1;
1879 : }
1880 : }
1881 : }
1882 : /*regular mat 2D*/
1883 0 : visual_3d_set_state(tr_state->visual, V3D_STATE_LIGHT | V3D_STATE_COLOR, 0);
1884 0 : visual_3d_set_material_2d(tr_state->visual, emi, emi_a);
1885 : }
1886 0 : break;
1887 : default:
1888 : break;
1889 : }
1890 : return 1;
1891 : #else
1892 : return 0;
1893 : #endif /*GPAC_DISABLE_VRML*/
1894 : }
1895 :
1896 17913 : Bool visual_3d_setup_texture(GF_TraverseState *tr_state, Fixed diffuse_alpha)
1897 : {
1898 : #ifndef GPAC_DISABLE_VRML
1899 : GF_TextureHandler *txh;
1900 17913 : tr_state->mesh_num_textures = 0;
1901 17913 : if (!tr_state->appear) return GF_TRUE;
1902 :
1903 16933 : gf_node_dirty_reset(tr_state->appear, 0);
1904 :
1905 16933 : txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture);
1906 : //no texture, return TRUE (eg draw)
1907 16933 : if (!txh)
1908 : return GF_TRUE;
1909 :
1910 5357 : gf_sc_texture_set_blend_mode(txh, gf_sc_texture_is_transparent(txh) ? TX_MODULATE : TX_REPLACE);
1911 5357 : tr_state->mesh_num_textures = gf_sc_texture_enable(txh, ((M_Appearance *)tr_state->appear)->textureTransform);
1912 5357 : if (tr_state->mesh_num_textures) {
1913 : Fixed v[4];
1914 4862 : switch (txh->pixelformat) {
1915 : /*override diffuse color with full intensity, but keep material alpha (cf VRML lighting)*/
1916 2787 : case GF_PIXEL_RGB:
1917 2787 : if (tr_state->visual->has_material_2d) {
1918 : SFColor c;
1919 : c.red = c.green = c.blue = FIX_ONE;
1920 : visual_3d_set_material_2d(tr_state->visual, c, diffuse_alpha);
1921 : } else {
1922 : v[0] = v[1] = v[2] = FIX_ONE;
1923 : v[3] = diffuse_alpha;
1924 : visual_3d_set_material(tr_state->visual, V3D_MATERIAL_DIFFUSE, v);
1925 : }
1926 : break;
1927 : /*override diffuse color AND material alpha (cf VRML lighting)*/
1928 210 : case GF_PIXEL_RGBA:
1929 210 : if (!tr_state->visual->has_material_2d) {
1930 : v[0] = v[1] = v[2] = v[3] = FIX_ONE;
1931 : visual_3d_set_material(tr_state->visual, V3D_MATERIAL_DIFFUSE, v);
1932 : }
1933 210 : tr_state->mesh_is_transparent = 1;
1934 210 : break;
1935 : }
1936 : }
1937 5357 : return tr_state->mesh_num_textures ? GF_TRUE : GF_FALSE;
1938 : #else
1939 : return GF_TRUE;
1940 : #endif /*GPAC_DISABLE_VRML*/
1941 : }
1942 :
1943 14881 : void visual_3d_disable_texture(GF_TraverseState *tr_state)
1944 : {
1945 14881 : if (tr_state->mesh_num_textures) {
1946 : #ifndef GPAC_DISABLE_VRML
1947 4862 : gf_sc_texture_disable(gf_sc_texture_get_handler( ((M_Appearance *)tr_state->appear)->texture) );
1948 : #endif
1949 4862 : tr_state->mesh_num_textures = 0;
1950 : }
1951 14881 : }
1952 :
1953 15384 : Bool visual_3d_setup_appearance(GF_TraverseState *tr_state)
1954 : {
1955 : Fixed diff_a;
1956 : /*setup material and check if 100% transparent - in which case don't draw*/
1957 15384 : if (!visual_3d_setup_material(tr_state, 0, &diff_a)) return 0;
1958 : /*setup texture*/
1959 15376 : if (! visual_3d_setup_texture(tr_state, diff_a)) return 0;
1960 14881 : return 1;
1961 : }
1962 :
1963 :
1964 15626 : void visual_3d_draw(GF_TraverseState *tr_state, GF_Mesh *mesh)
1965 : {
1966 15626 : if (mesh->mesh_type) {
1967 281 : if (visual_3d_setup_material(tr_state, mesh->mesh_type, NULL)) {
1968 281 : visual_3d_mesh_paint(tr_state, mesh);
1969 : }
1970 15345 : } else if (visual_3d_setup_appearance(tr_state)) {
1971 14842 : visual_3d_mesh_paint(tr_state, mesh);
1972 14842 : visual_3d_disable_texture(tr_state);
1973 :
1974 : #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_USE_GLES1X) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_DISABLE_X3D) && !defined(GPAC_USE_GLES2)
1975 14842 : if (tr_state->appear && gf_node_get_tag(tr_state->appear)==TAG_X3D_Appearance) {
1976 2070 : X_Appearance *ap = (X_Appearance *)tr_state->appear;
1977 2070 : X_FillProperties *fp = ap->fillProperties ? (X_FillProperties *) ap->fillProperties : NULL;
1978 2070 : if (fp && fp->hatched) visual_3d_mesh_hatch(tr_state, mesh, fp->hatchStyle, fp->hatchColor);
1979 : }
1980 : #endif
1981 : }
1982 15626 : }
1983 :
1984 2256 : void visual_3d_projection_matrix_modified(GF_VisualManager *visual)
1985 : {
1986 18358 : visual->needs_projection_matrix_reload = 1;
1987 2256 : }
1988 :
1989 7101 : void visual_3d_enable_headlight(GF_VisualManager *visual, Bool bOn, GF_Camera *cam)
1990 : {
1991 : SFVec3f dir;
1992 : SFColor col;
1993 :
1994 7553 : if (!bOn) return;
1995 : //if we have lights in the scene don't turn the headlight on
1996 6649 : if (visual->has_inactive_lights || visual->num_lights) return;
1997 :
1998 6649 : col.blue = col.red = col.green = FIX_ONE;
1999 6649 : dir.x = dir.y = 0;
2000 6649 : dir.z = -FIX_ONE;
2001 : // if (cam->is_3D) dir = camera_get_target_dir(cam);
2002 : // visual_3d_add_directional_light(visual, 0, col, FIX_ONE, dir, &cam->modelview);
2003 :
2004 6649 : visual_3d_add_directional_light(visual, 0, col, FIX_ONE, dir, NULL);
2005 : }
2006 :
2007 743 : void visual_3d_set_material_2d(GF_VisualManager *visual, SFColor col, Fixed alpha)
2008 : {
2009 3142 : visual->has_material_2d = alpha ? GF_TRUE : GF_FALSE;
2010 3142 : visual->has_material = 0;
2011 1342 : if (visual->has_material_2d) {
2012 3142 : visual->mat_2d.red = col.red;
2013 3142 : visual->mat_2d.green = col.green;
2014 3142 : visual->mat_2d.blue = col.blue;
2015 3142 : visual->mat_2d.alpha = alpha;
2016 : }
2017 :
2018 743 : }
2019 :
2020 3125 : void visual_3d_set_material_2d_argb(GF_VisualManager *visual, u32 col)
2021 : {
2022 7229 : u32 a = GF_COL_A(col);
2023 7386 : visual->has_material_2d = a ? GF_TRUE : GF_FALSE;
2024 7386 : visual->has_material = 0;
2025 7386 : if (visual->has_material_2d) {
2026 7386 : visual->mat_2d.red = INT2FIX( GF_COL_R(col) ) / 255;
2027 7386 : visual->mat_2d.green = INT2FIX( GF_COL_G(col) ) / 255;
2028 7386 : visual->mat_2d.blue = INT2FIX( GF_COL_B(col) ) / 255;
2029 7386 : visual->mat_2d.alpha = INT2FIX( a ) / 255;
2030 : }
2031 3125 : }
2032 :
2033 1740 : void visual_3d_set_clipper_2d(GF_VisualManager *visual, GF_Rect clip, GF_Matrix *mx_at_clipper)
2034 : {
2035 1740 : if (mx_at_clipper)
2036 962 : gf_mx_apply_rect(mx_at_clipper, &clip);
2037 1824 : visual->clipper_2d = gf_rect_pixelize(&clip);
2038 1824 : visual->has_clipper_2d = GF_TRUE;
2039 1740 : }
2040 :
2041 2020 : void visual_3d_reset_clipper_2d(GF_VisualManager *visual)
2042 : {
2043 2104 : visual->has_clipper_2d = GF_FALSE;
2044 2020 : }
2045 :
2046 305 : void visual_3d_set_clip_plane(GF_VisualManager *visual, GF_Plane p, GF_Matrix *mx_at_clipper, Bool is_2d_clip)
2047 : {
2048 305 : if (visual->num_clips==GF_MAX_GL_CLIPS) return;
2049 305 : gf_vec_norm(&p.normal);
2050 305 : visual->clippers[visual->num_clips].p = p;
2051 305 : visual->clippers[visual->num_clips].is_2d_clip = is_2d_clip;
2052 305 : visual->clippers[visual->num_clips].mx_clipper = mx_at_clipper;
2053 305 : visual->num_clips++;
2054 : }
2055 :
2056 305 : void visual_3d_reset_clip_plane(GF_VisualManager *visual)
2057 : {
2058 305 : if (!visual->num_clips) return;
2059 305 : visual->num_clips -= 1;
2060 : }
2061 :
2062 0 : void visual_3d_set_material(GF_VisualManager *visual, u32 material_type, Fixed *rgba)
2063 : {
2064 57456 : visual->materials[material_type].red = rgba[0];
2065 57456 : visual->materials[material_type].green = rgba[1];
2066 57456 : visual->materials[material_type].blue = rgba[2];
2067 57456 : visual->materials[material_type].alpha = rgba[3];
2068 :
2069 57456 : visual->has_material = 1;
2070 57456 : visual->has_material_2d=0;
2071 0 : }
2072 :
2073 0 : void visual_3d_set_shininess(GF_VisualManager *visual, Fixed shininess)
2074 : {
2075 13734 : visual->shininess = shininess;
2076 0 : }
2077 :
2078 8801 : void visual_3d_set_state(GF_VisualManager *visual, u32 flag_mask, Bool setOn)
2079 : {
2080 22535 : if (flag_mask & V3D_STATE_LIGHT) visual->state_light_on = setOn;
2081 24079 : if (flag_mask & V3D_STATE_BLEND) visual->state_blend_on = setOn;
2082 8801 : if (flag_mask & V3D_STATE_COLOR) visual->state_color_on = setOn;
2083 8801 : }
2084 :
2085 :
2086 13484 : void visual_3d_set_texture_matrix(GF_VisualManager *visual, GF_Matrix *mx)
2087 : {
2088 13484 : visual->has_tx_matrix = mx ? 1 : 0;
2089 13484 : if (mx) gf_mx_copy(visual->tx_matrix, *mx);
2090 13484 : }
2091 :
2092 :
2093 0 : void visual_3d_has_inactive_light(GF_VisualManager *visual)
2094 : {
2095 0 : visual->has_inactive_lights = GF_TRUE;
2096 0 : }
2097 :
2098 151 : Bool visual_3d_add_point_light(GF_VisualManager *visual, Fixed ambientIntensity, SFVec3f attenuation, SFColor color, Fixed intensity, SFVec3f location, GF_Matrix *light_mx)
2099 : {
2100 151 : if (visual->num_lights==visual->max_lights) return 0;
2101 151 : visual->lights[visual->num_lights].type = 2;
2102 151 : visual->lights[visual->num_lights].ambientIntensity = ambientIntensity;
2103 151 : visual->lights[visual->num_lights].attenuation = attenuation;
2104 151 : visual->lights[visual->num_lights].color = color;
2105 151 : visual->lights[visual->num_lights].intensity = intensity;
2106 151 : visual->lights[visual->num_lights].position = location;
2107 151 : memcpy(&visual->lights[visual->num_lights].light_mx, light_mx, sizeof(GF_Matrix) );
2108 151 : visual->num_lights++;
2109 151 : return 1;
2110 : }
2111 :
2112 151 : Bool visual_3d_add_spot_light(GF_VisualManager *visual, Fixed ambientIntensity, SFVec3f attenuation, Fixed beamWidth,
2113 : SFColor color, Fixed cutOffAngle, SFVec3f direction, Fixed intensity, SFVec3f location, GF_Matrix *light_mx)
2114 : {
2115 151 : if (visual->num_lights==visual->max_lights) return 0;
2116 151 : visual->lights[visual->num_lights].type = 1;
2117 151 : visual->lights[visual->num_lights].ambientIntensity = ambientIntensity;
2118 151 : visual->lights[visual->num_lights].attenuation = attenuation;
2119 151 : visual->lights[visual->num_lights].beamWidth = beamWidth;
2120 151 : visual->lights[visual->num_lights].cutOffAngle = cutOffAngle;
2121 151 : visual->lights[visual->num_lights].color = color;
2122 151 : visual->lights[visual->num_lights].direction = direction;
2123 151 : visual->lights[visual->num_lights].intensity = intensity;
2124 151 : visual->lights[visual->num_lights].position = location;
2125 151 : memcpy(&visual->lights[visual->num_lights].light_mx, light_mx, sizeof(GF_Matrix) );
2126 151 : visual->num_lights++;
2127 151 : return 1;
2128 : }
2129 :
2130 8136 : Bool visual_3d_add_directional_light(GF_VisualManager *visual, Fixed ambientIntensity, SFColor color, Fixed intensity, SFVec3f direction, GF_Matrix *light_mx)
2131 : {
2132 8136 : if (visual->num_lights==visual->max_lights) return 0;
2133 8136 : visual->lights[visual->num_lights].type = 0;
2134 8136 : visual->lights[visual->num_lights].ambientIntensity = ambientIntensity;
2135 8136 : visual->lights[visual->num_lights].color = color;
2136 8136 : visual->lights[visual->num_lights].intensity = intensity;
2137 8136 : visual->lights[visual->num_lights].direction = direction;
2138 8136 : if (light_mx) {
2139 1487 : memcpy(&visual->lights[visual->num_lights].light_mx, light_mx, sizeof(GF_Matrix) );
2140 : } else {
2141 13298 : gf_mx_init(visual->lights[visual->num_lights].light_mx);
2142 6649 : visual->lights[visual->num_lights].type = 3;
2143 6649 : visual->lights[visual->num_lights].direction.x = 0;
2144 6649 : visual->lights[visual->num_lights].direction.y = 0;
2145 6649 : visual->lights[visual->num_lights].direction.z = -FIX_ONE;
2146 : }
2147 :
2148 8136 : visual->num_lights++;
2149 8136 : return 1;
2150 : }
2151 :
2152 :
2153 1115 : void visual_3d_remove_last_light(GF_VisualManager *visual)
2154 : {
2155 1115 : if (visual->num_lights) {
2156 1115 : visual->num_lights--;
2157 : }
2158 1115 : }
2159 :
2160 1115 : void visual_3d_clear_all_lights(GF_VisualManager *visual)
2161 : {
2162 8742 : visual->num_lights = 0;
2163 8742 : visual->has_inactive_lights = GF_FALSE;
2164 1115 : }
2165 :
2166 1288 : void visual_3d_set_fog(GF_VisualManager *visual, const char *type, SFColor color, Fixed density, Fixed visibility)
2167 : {
2168 1288 : visual->has_fog = GF_TRUE;
2169 1288 : if (!type || !stricmp(type, "LINEAR")) visual->fog_type = 0;
2170 0 : else if (!stricmp(type, "EXPONENTIAL")) visual->fog_type = 1;
2171 0 : else if (!stricmp(type, "EXPONENTIAL2")) visual->fog_type = 2;
2172 :
2173 1288 : visual->fog_color = color;
2174 1288 : visual->fog_density = density;
2175 1288 : visual->fog_visibility = visibility;
2176 1288 : }
2177 :
2178 : #endif
2179 :
|