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*/
drawable_3d_new(GF_Node * node)38 Drawable3D *drawable_3d_new(GF_Node *node)
39 {
40 	Drawable3D *tmp;
41 	GF_SAFEALLOC(tmp, Drawable3D);
42 	if (!tmp) {
43 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate drawable 3D stack\n"));
44 		return NULL;
45 	}
46 	tmp->mesh = new_mesh();
47 	gf_node_set_private(node, tmp);
48 	return tmp;
49 }
drawable_3d_del(GF_Node * n)50 void drawable_3d_del(GF_Node *n)
51 {
52 	Drawable3D *d = (Drawable3D *)gf_node_get_private(n);
53 	if (d) {
54 		if (d->mesh) mesh_free(d->mesh);
55 		gf_free(d);
56 	}
57 	gf_sc_check_focus_upon_destroy(n);
58 }
59 
60 
drawable3d_check_focus_highlight(GF_Node * node,GF_TraverseState * tr_state,GF_BBox * orig_bounds)61 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 	GF_Compositor *compositor = tr_state->visual->compositor;
69 
70 	if (compositor->disable_focus_highlight) return;
71 
72 	if (compositor->focus_node!=node) return;
73 
74 	hlight = compositor->focus_highlight;
75 	if (!hlight) return;
76 
77 	/*check if focus node has changed*/
78 	prev_node = gf_node_get_private(hlight->node);
79 	if (prev_node != node) {
80 		gf_node_set_private(hlight->node, node);
81 
82 		drawable_reset_path(hlight);
83 		gf_path_reset(hlight->path);
84 	}
85 	/*this is a grouping node, get its bounds*/
86 	if (!orig_bounds) {
87 		gf_mx_copy(cur, tr_state->model_matrix);
88 		gf_mx_init(tr_state->model_matrix);
89 		prev_mode = tr_state->traversing_mode;
90 		tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
91 		tr_state->bbox.is_set = 0;
92 
93 //		gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, tr_state, 1);
94 		gf_node_traverse_children(node, tr_state);
95 
96 		tr_state->traversing_mode = prev_mode;
97 		gf_mx_copy(tr_state->model_matrix, cur);
98 		bounds = &tr_state->bbox;
99 	} else {
100 		bounds = orig_bounds;
101 	}
102 	visual_3d_draw_bbox(tr_state, bounds, GF_FALSE);
103 }
104 
105 
visual_3d_setup_traversing_state(GF_VisualManager * visual,GF_TraverseState * tr_state)106 static void visual_3d_setup_traversing_state(GF_VisualManager *visual, GF_TraverseState *tr_state)
107 {
108 	tr_state->visual = visual;
109 	tr_state->camera = &visual->camera;
110 #ifndef GPAC_DISABLE_VRML
111 	tr_state->backgrounds = visual->back_stack;
112 	tr_state->viewpoints = visual->view_stack;
113 	tr_state->fogs = visual->fog_stack;
114 	tr_state->navigations = visual->navigation_stack;
115 #endif
116 	tr_state->color_mat.identity = 1;
117 	tr_state->camera->vp.x = tr_state->camera->vp.y = 0;
118 	tr_state->min_hsize = INT2FIX(MIN(visual->width, visual->height) / 2);
119 	if (!tr_state->min_hsize) tr_state->min_hsize = FIX_ONE;
120 
121 
122 	/*main visual, set AR*/
123 	if (visual->compositor->visual==visual) {
124 		if (tr_state->visual->compositor->has_size_info) {
125 			tr_state->camera->vp.x = INT2FIX(tr_state->visual->compositor->vp_x);
126 			tr_state->camera->vp.y = INT2FIX(tr_state->visual->compositor->vp_y);
127 			tr_state->camera->vp.width = INT2FIX(tr_state->visual->compositor->vp_width);
128 			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 			if (visual->type_3d==0) {
132 				tr_state->camera->width = INT2FIX(tr_state->visual->width);
133 				tr_state->camera->height = INT2FIX(tr_state->visual->height);
134 			} else {
135 				tr_state->camera->width = INT2FIX(tr_state->visual->compositor->vp_width);
136 				tr_state->camera->height = INT2FIX(tr_state->visual->compositor->vp_height);
137 			}
138 		} else {
139 			Fixed sw, sh;
140 			sw = INT2FIX(tr_state->visual->compositor->vp_width);
141 			sh = INT2FIX(tr_state->visual->compositor->vp_height);
142 			/*AR changed, rebuild camera*/
143 
144 			if (tr_state->visual->compositor->recompute_ar
145 			        || (sw!=tr_state->camera->vp.width)
146 			        || (sh!=tr_state->camera->vp.height)) {
147 				tr_state->camera->width = tr_state->camera->vp.width = INT2FIX(tr_state->visual->compositor->vp_width);
148 				tr_state->camera->height = tr_state->camera->vp.height = INT2FIX(tr_state->visual->compositor->vp_height);
149 				tr_state->camera->flags |= CAM_IS_DIRTY;
150 			}
151 		}
152 	}
153 	/*composite visual, no AR*/
154 	else {
155 		tr_state->camera->vp.width = tr_state->camera->width = INT2FIX(visual->width);
156 		tr_state->camera->vp.height = tr_state->camera->height = INT2FIX(visual->height);
157 	}
158 
159 	if (!tr_state->pixel_metrics) {
160 		if (tr_state->camera->height > tr_state->camera->width) {
161 			tr_state->camera->height = 2*gf_divfix(tr_state->camera->height , tr_state->camera->width);
162 			tr_state->camera->width = 2*FIX_ONE;
163 		} else {
164 			tr_state->camera->width = 2 * gf_divfix(tr_state->camera->width, tr_state->camera->height);
165 			tr_state->camera->height = 2 * FIX_ONE;
166 		}
167 	}
168 	/*setup bounds*/
169 	tr_state->bbox.max_edge.x = tr_state->camera->width / 2;
170 	tr_state->bbox.min_edge.x = -tr_state->bbox.max_edge.x;
171 	tr_state->bbox.max_edge.y = tr_state->camera->height / 2;
172 	tr_state->bbox.min_edge.y = -tr_state->bbox.max_edge.y;
173 	tr_state->bbox.max_edge.z = tr_state->bbox.min_edge.z = 0;
174 	tr_state->bbox.is_set = 1;
175 }
176 
177 
visual_3d_viewpoint_change(GF_TraverseState * tr_state,GF_Node * vp,Bool animate_change,Fixed fieldOfView,SFVec3f position,SFRotation orientation,SFVec3f local_center)178 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 	tr_state->camera->z_near = tr_state->camera->avatar_size.x ;
186 
187 	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 	else if (tr_state->camera->z_near>=FIX_ONE) tr_state->camera->z_near = FIX_ONE/2;
190 	tr_state->camera->z_near /= 2;
191 	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 	if (tr_state->camera->z_far<=0) {
207 		Fixed ar = gf_divfix(tr_state->vp_size.x, tr_state->vp_size.y);
208 		if (ar>FIX_ONE) ar = FIX_ONE;
209 		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 		if (tr_state->camera->z_far <= tr_state->camera->z_near) {
217 			tr_state->camera->z_far = FIX_MAX/4;
218 		}
219 	}
220 	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 	else if (tr_state->pixel_metrics) {
237 		if (tr_state->visual != tr_state->visual->compositor->visual) {
238 			position.z = gf_divfix(tr_state->camera->width, 2*gf_tan(fieldOfView/2) );
239 		} else {
240 			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 	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 		dispdist = tr_state->visual->compositor->video_out->dispdist * tr_state->visual->compositor->video_out->dpi_x;
250 		dispdist = gf_divfix(dispdist , FLT2FIX(2.54f) );
251 		disparity = INT2FIX(tr_state->visual->compositor->video_out->disparity);
252 
253 		if (tr_state->visual->depth_vp_range) {
254 			position.z = dispdist;
255 			tr_state->camera->z_near = dispdist - tr_state->visual->depth_vp_position + tr_state->visual->depth_vp_range/2;
256 			tr_state->camera->z_far = dispdist - tr_state->visual->depth_vp_position - tr_state->visual->depth_vp_range/2;
257 		}
258 		else if (disparity) {
259 			/*3,4 cm = 1,3386 inch -> pixels*/
260 			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 			tr_state->camera->z_near = dispdist -
268 			                           gf_divfix( gf_mulfix(disparity,dispdist), (half_interocular_dist_pixel + disparity));
269 		}
270 		else if (tr_state->visual->compositor->dispdepth) {
271 			dist = INT2FIX(tr_state->visual->compositor->dispdepth);
272 			if (dist<0) dist = INT2FIX(tr_state->visual->height);
273 
274 #if 1
275 			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 			tr_state->camera->z_near = dispdist - 2*dist/3;
284 			tr_state->camera->z_far = dispdist + dist/2;
285 		}
286 	}
287 #endif
288 
289 	gf_vec_diff(d, position, local_center);
290 	dist = gf_vec_len(d);
291 
292 	if (!dist || (dist<tr_state->camera->z_near) || (dist > tr_state->camera->z_far)) {
293 		if (dist > tr_state->camera->z_far)
294 			tr_state->camera->z_far = 2*dist;
295 
296 		dist = 10 * tr_state->camera->avatar_size.x;
297 		if ((dist<tr_state->camera->z_near) || (dist > tr_state->camera->z_far))
298 			dist = (tr_state->camera->avatar_size.x + tr_state->camera->z_far) / 5;
299 	}
300 	tr_state->camera->vp_dist = dist;
301 	tr_state->camera->vp_position = position;
302 	tr_state->camera->vp_orientation = orientation;
303 	tr_state->camera->vp_fov = fieldOfView;
304 	tr_state->camera->examine_center = local_center;
305 
306 	camera_reset_viewpoint(tr_state->camera, animate_change);
307 	if (tr_state->layer3d) gf_node_dirty_set(tr_state->layer3d, GF_SG_VRML_BINDABLE_DIRTY, 0);
308 	gf_sc_invalidate(tr_state->visual->compositor, NULL);
309 }
310 
visual_3d_setup_projection(GF_TraverseState * tr_state,Bool is_layer)311 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 	u32 mode = tr_state->traversing_mode;
317 	tr_state->traversing_mode = TRAVERSE_BINDABLE;
318 
319 	/*setup viewpoint (this directly modifies the frustum)*/
320 #ifndef GPAC_DISABLE_VRML
321 	bindable = (GF_Node*)gf_list_get(tr_state->viewpoints, 0);
322 	if (Bindable_GetIsBound(bindable)) {
323 		gf_node_traverse(bindable, tr_state);
324 		tr_state->camera->had_viewpoint = 1;
325 	} else
326 #endif
327 		if (tr_state->camera->had_viewpoint) {
328 			u32 had_vp = tr_state->camera->had_viewpoint;
329 			tr_state->camera->had_viewpoint = 0;
330 			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 				if (tr_state->visual->compositor->autocal && tr_state->visual->compositor->video_out->dispdist) {
337 					/*get view distance in pixels*/
338 					Fixed dispdist = tr_state->visual->compositor->video_out->dispdist * tr_state->visual->compositor->video_out->dpi_x;
339 					dispdist = gf_divfix(dispdist , FLT2FIX(2.54f) );
340 
341 					fov = 2*gf_atan2( INT2FIX(tr_state->visual->compositor->video_out->max_screen_width)/2, dispdist);
342 				}
343 #endif
344 
345 				/*default viewpoint*/
346 				pos.x = pos.y = 0;
347 				pos.z = INT2FIX(10);
348 				center.x = center.y = center.z = 0;
349 				r.q = r.x = r.z = 0;
350 				r.y = FIX_ONE;
351 				/*this takes care of pixelMetrics*/
352 				visual_3d_viewpoint_change(tr_state, NULL, 0, fov, pos, r, center);
353 				/*initial vp compute, don't animate*/
354 				if (had_vp == 2) {
355 					camera_stop_anim(tr_state->camera);
356 					camera_reset_viewpoint(tr_state->camera, 0);
357 					/*scene not yet ready, force a recompute of world bounds at next frame*/
358 					if (!is_layer && gf_sc_fit_world_to_screen(tr_state->visual->compositor) == 0) {
359 						tr_state->camera->had_viewpoint = 2;
360 						gf_sc_invalidate(tr_state->visual->compositor, NULL);
361 					}
362 				}
363 			} else {
364 				tr_state->camera->flags &= ~CAM_HAS_VIEWPORT;
365 				tr_state->camera->flags |= CAM_IS_DIRTY;
366 			}
367 		}
368 
369 
370 	tr_state->camera_was_dirty = GF_FALSE;
371 
372 	if (tr_state->visual->nb_views>1) {
373 		s32 view_idx;
374 		Fixed interocular_dist_pixel;
375 		Fixed delta = 0;
376 
377 		interocular_dist_pixel = tr_state->visual->compositor->iod + tr_state->visual->compositor->interoccular_offset;
378 
379 		view_idx = tr_state->visual->current_view;
380 		view_idx -= tr_state->visual->nb_views/2;
381 		delta = interocular_dist_pixel * view_idx;
382 		if (! (tr_state->visual->nb_views % 2)) {
383 			delta += interocular_dist_pixel/2;
384 		}
385 		if (tr_state->visual->compositor->rview) delta = - delta;
386 
387 		tr_state->camera->flags |= CAM_IS_DIRTY;
388 		tr_state->camera_was_dirty = GF_TRUE;
389 		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 		if (tr_state->camera->flags & CAM_IS_DIRTY) tr_state->camera_was_dirty = GF_TRUE;
392 		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 	visual_3d_projection_matrix_modified(tr_state->visual);
399 	gf_mx_init(tr_state->model_matrix);
400 
401 	tr_state->traversing_mode = mode;
402 #ifdef GF_SR_USE_DEPTH
403 	tr_state->depth_offset = 0;
404 	tr_state->depth_gain = FIX_ONE;
405 #endif
406 }
407 
visual_3d_draw_background(GF_TraverseState * tr_state,u32 layer_type)408 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 	mode = tr_state->traversing_mode;
417 	tr_state->traversing_mode = TRAVERSE_BINDABLE;
418 
419 	/*if in layer clear z buffer (even if background)*/
420 	if (layer_type) visual_3d_clear_depth(tr_state->visual);
421 
422 	/*clear requested - do it before background drawing for layer3D (transparent background)*/
423 	if (layer_type==2) {
424 		SFColor col;
425 		col.red = INT2FIX((tr_state->visual->compositor->back_color>>16)&0xFF) / 255;
426 		col.green = INT2FIX((tr_state->visual->compositor->back_color>>8)&0xFF) / 255;
427 		col.blue = INT2FIX((tr_state->visual->compositor->back_color)&0xFF) / 255;
428 		visual_3d_clear(tr_state->visual, col, 0);
429 	}
430 
431 #ifndef GPAC_DISABLE_VRML
432 	bindable = (GF_Node*) gf_list_get(tr_state->backgrounds, 0);
433 	if (Bindable_GetIsBound(bindable)) {
434 		gf_node_traverse(bindable, tr_state);
435 	}
436 	/*clear if not in layer*/
437 	else
438 #endif
439 		if (!layer_type) {
440 			SFColor col;
441 			Fixed alpha = 0;
442 			col.red = INT2FIX((tr_state->visual->compositor->back_color>>16)&0xFF) / 255;
443 			col.green = INT2FIX((tr_state->visual->compositor->back_color>>8)&0xFF) / 255;
444 			col.blue = INT2FIX((tr_state->visual->compositor->back_color)&0xFF) / 255;
445 			/*if composite visual, clear with alpha = 0*/
446 			if (tr_state->visual==tr_state->visual->compositor->visual) {
447 				alpha = FIX_ONE;
448 				if (tr_state->visual->compositor->init_flags & GF_TERM_WINDOW_TRANSPARENT) {
449 					alpha = 0;
450 				} else if (tr_state->visual->compositor->dyn_filter_mode) {
451 					alpha = 0;
452 				}
453 			}
454 			visual_3d_clear(tr_state->visual, col, alpha);
455 		}
456 	tr_state->traversing_mode = mode;
457 
458 }
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*/
visual_3d_draw_background_on_axis(GF_TraverseState * tr_state,u32 layer_type)463 static void visual_3d_draw_background_on_axis(GF_TraverseState *tr_state, u32 layer_type)
464 {
465 	GF_Matrix proj, model;
466 	GF_Camera *cam = &tr_state->visual->camera;
467 	Fixed ar = gf_divfix(cam->width, cam->height);
468 
469 	tr_state->visual->camlay = 0;
470 	gf_mx_copy(proj, cam->projection);
471 	gf_mx_copy(model, cam->modelview);
472 
473 	gf_mx_perspective(&cam->projection, cam->fieldOfView, ar, cam->z_near, cam->z_far);
474 	visual_3d_projection_matrix_modified(tr_state->visual);
475 
476 	/*setup modelview*/
477 	gf_mx_lookat(&cam->modelview, cam->position, cam->target, cam->up);
478 
479 	visual_3d_draw_background(tr_state, layer_type);
480 
481 	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 }
487 
visual_3d_init_draw(GF_TraverseState * tr_state,u32 layer_type)488 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 	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 	tr_state->traversing_mode = TRAVERSE_BINDABLE;
498 #ifndef GPAC_DISABLE_VRML
499 	bindable = tr_state->navigations ? (GF_Node*) gf_list_get(tr_state->navigations, 0) : NULL;
500 	if (Bindable_GetIsBound(bindable)) {
501 		gf_node_traverse(bindable, tr_state);
502 		tr_state->camera->had_nav_info = 1;
503 	} else
504 #endif
505 		if (tr_state->camera->had_nav_info) {
506 			/*if no navigation specified, use default VRML one*/
507 			tr_state->camera->avatar_size.x = FLT2FIX(0.25f);
508 			tr_state->camera->avatar_size.y = FLT2FIX(1.6f);
509 			tr_state->camera->avatar_size.z = FLT2FIX(0.75f);
510 			tr_state->camera->visibility = 0;
511 			tr_state->camera->speed = FIX_ONE;
512 			/*not specified in the spec, but by default we forbid navigation in layer*/
513 			if (layer_type) {
514 				tr_state->camera->navigation_flags = NAV_HEADLIGHT;
515 				tr_state->camera->navigate_mode = GF_NAVIGATE_NONE;
516 			} else {
517 				tr_state->camera->navigation_flags = NAV_ANY | NAV_HEADLIGHT;
518 				if (tr_state->camera->is_3D) {
519 					if (tr_state->visual->compositor->nav != GF_NAVIGATE_NONE) {
520 						tr_state->camera->navigate_mode = tr_state->visual->compositor->nav;
521 					} else {
522 						/*X3D is by default examine, VRML/MPEG4 is WALK*/
523 						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 					tr_state->camera->navigate_mode = GF_NAVIGATE_NONE;
533 				}
534 			}
535 			tr_state->camera->had_nav_info = 0;
536 
537 			if (tr_state->pixel_metrics) {
538 				tr_state->camera->visibility = gf_mulfix(tr_state->camera->visibility, tr_state->min_hsize);
539 				tr_state->camera->avatar_size.x = gf_mulfix(tr_state->camera->avatar_size.x, tr_state->min_hsize);
540 				tr_state->camera->avatar_size.y = gf_mulfix(tr_state->camera->avatar_size.y, tr_state->min_hsize);
541 				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 	if (camera_animate(tr_state->camera, tr_state->visual->compositor)) {
547 		if (tr_state->visual->compositor->active_layer) gf_node_dirty_set(tr_state->visual->compositor->active_layer, 0, 1);
548 
549 		tr_state->visual->compositor->force_next_frame_redraw = GF_TRUE;
550 	}
551 
552 
553 	/*turn off depth buffer in 2D*/
554 	visual_3d_enable_depth_buffer(tr_state->visual, tr_state->camera->is_3D);
555 
556 	tr_state->camera->proj_vp = tr_state->camera->vp;
557 
558 	if ((tr_state->visual->autostereo_type==GF_3D_STEREO_SIDE) || (tr_state->visual->autostereo_type==GF_3D_STEREO_HEADSET)) {
559 		GF_Rect orig_vp = tr_state->camera->vp;
560 		Fixed vp_width = orig_vp.width;
561 //		Fixed vp_height = orig_vp.height;
562 		Fixed old_w = tr_state->camera->width;
563 		Fixed old_h = tr_state->camera->height;
564 
565 		//fill up the entire screen matchin AR
566 		if (tr_state->visual->autostereo_type==GF_3D_STEREO_HEADSET) {
567 			Fixed max_width = INT2FIX(tr_state->visual->compositor->display_width) / tr_state->visual->nb_views;
568 			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 				tr_state->camera->vp.width = max_width;
582 				tr_state->camera->vp.height = max_height;
583 #endif
584 				tr_state->camera->width = max_width;
585 				tr_state->camera->height = max_height;
586 
587 			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 			tr_state->camera->vp.y = (INT2FIX(tr_state->visual->compositor->display_height) - tr_state->camera->vp.height)/2;
589 
590 		} else {
591 			tr_state->camera->vp.width = vp_width / tr_state->visual->nb_views;
592 			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 		if (!tr_state->visual->current_view) {
596 			SFColor col;
597 			col.red = INT2FIX((tr_state->visual->compositor->back_color>>16)&0xFF) / 255;
598 			col.green = INT2FIX((tr_state->visual->compositor->back_color>>8)&0xFF) / 255;
599 			col.blue = INT2FIX((tr_state->visual->compositor->back_color)&0xFF) / 255;
600 			visual_3d_set_viewport(tr_state->visual, orig_vp);
601 			visual_3d_clear(tr_state->visual, col, 0);
602 		}
603 
604 		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 		visual_3d_set_scissor(tr_state->visual, &tr_state->camera->vp);
607 
608 		if (off_axis_background) {
609 			visual_3d_draw_background_on_axis(tr_state, layer_type);
610 		}
611 
612 		/*setup projection*/
613 		visual_3d_setup_projection(tr_state, layer_type);
614 
615 		tr_state->camera->proj_vp = tr_state->camera->vp;
616 		tr_state->camera->vp = orig_vp;
617 		tr_state->camera->width = old_w;
618 		tr_state->camera->height = old_h;
619 
620 	} else if (tr_state->visual->autostereo_type==GF_3D_STEREO_TOP) {
621 		GF_Rect orig_vp;
622 		orig_vp = tr_state->camera->vp;
623 
624 		tr_state->camera->vp.height = tr_state->camera->vp.height / tr_state->visual->nb_views;
625 
626 		if (tr_state->visual == tr_state->visual->compositor->visual) {
627 			Fixed remain = INT2FIX(tr_state->visual->compositor->output_height - orig_vp.height) / tr_state->visual->nb_views;
628 
629 			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 			tr_state->camera->vp.y = tr_state->visual->height - tr_state->camera->vp.y - tr_state->camera->vp.height;
632 		}
633 		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 		visual_3d_set_scissor(tr_state->visual, &tr_state->camera->vp);
636 
637 		if (off_axis_background) {
638 			visual_3d_draw_background_on_axis(tr_state, layer_type);
639 		}
640 
641 		/*setup projection*/
642 		visual_3d_setup_projection(tr_state, layer_type);
643 
644 		tr_state->camera->proj_vp = tr_state->camera->vp;
645 		tr_state->camera->vp = orig_vp;
646 	} else {
647 		visual_3d_set_viewport(tr_state->visual, tr_state->camera->vp);
648 
649 		if (off_axis_background) {
650 			visual_3d_draw_background_on_axis(tr_state, layer_type);
651 		}
652 
653 		/*setup projection*/
654 		visual_3d_setup_projection(tr_state, layer_type);
655 
656 		if (tr_state->visual->autostereo_type) {
657 			visual_3d_clear_depth(tr_state->visual);
658 		}
659 	}
660 
661 	/*regular background draw*/
662 	if (!tr_state->camera->is_3D || tr_state->visual->camlay != GF_3D_CAMERA_OFFAXIS) {
663 		visual_3d_draw_background(tr_state, layer_type);
664 	}
665 
666 	/*set headlight if any*/
667 	if (tr_state->visual->type_3d>1) {
668 		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 	visual_3d_set_scissor(tr_state->visual, NULL);
673 }
674 
675 
visual_3d_has_alpha(GF_TraverseState * tr_state,GF_Node * geom)676 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 	if (tr_state->appear) {
683 		GF_Node *mat = ((M_Appearance *)tr_state->appear)->material;
684 		if (mat) {
685 			u32 tag = gf_node_get_tag(mat);
686 			switch (tag) {
687 			/*for M2D: if filled & transparent we're transparent - otherwise we must check texture*/
688 			case TAG_MPEG4_Material2D:
689 				if (((M_Material2D *)mat)->filled && ((M_Material2D *)mat)->transparency) return 1;
690 				break;
691 			case TAG_MPEG4_Material:
692 #ifndef GPAC_DISABLE_X3D
693 			case TAG_X3D_Material:
694 #endif
695 				is_mat3D = GF_TRUE;
696 				if ( ((M_Material *)mat)->transparency) return 1;
697 				break;
698 			case TAG_MPEG4_MaterialKey:
699 				return 1;
700 				break;
701 			}
702 		} else if (tr_state->camera->is_3D) {
703 			GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture);
704 			if (txh && txh->transparent) return 1;
705 		}
706 	}
707 
708 	/*check alpha texture in3D or with bitmap*/
709 	if (tr_state->appear && ( is_mat3D || (gf_node_get_tag(geom)==TAG_MPEG4_Bitmap))) {
710 		GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture);
711 		if (txh && txh->transparent) return 1;
712 	}
713 
714 #endif /*GPAC_DISABLE_VRML*/
715 
716 	/*TODO - FIXME check alpha only...*/
717 	if (!tr_state->color_mat.identity) return 1;
718 
719 	stack = (Drawable3D *)gf_node_get_private(geom);
720 	if (stack && stack->mesh && (stack->mesh->flags & MESH_HAS_ALPHA)) return 1;
721 	return 0;
722 }
723 
visual_3d_register_context(GF_TraverseState * tr_state,GF_Node * geometry)724 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 	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 	if (!tr_state->camera->is_3D || !visual_3d_has_alpha(tr_state, geometry) || !drawable->mesh) {
738 		tr_state->traversing_mode = TRAVERSE_DRAW_3D;
739 		/*layout/form clipper, set it in world coords only*/
740 		if (tr_state->has_clip) {
741 			visual_3d_set_clipper_2d(tr_state->visual, tr_state->clipper, NULL);
742 		}
743 
744 		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 		if (tr_state->appear)
750 			gf_node_dirty_clear(tr_state->appear, 0);
751 
752 		/*back to SORT*/
753 		tr_state->traversing_mode = TRAVERSE_SORT;
754 
755 		if (tr_state->has_clip) visual_3d_reset_clipper_2d(tr_state->visual);
756 		return;
757 	}
758 
759 	GF_SAFEALLOC(ctx, Drawable3DContext);
760 	if (!ctx) {
761 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate drawable 3D context\n"));
762 		return;
763 	}
764 
765 	ctx->directional_lights = gf_list_new();
766 	ctx->geometry = geometry;
767 	ctx->appearance = tr_state->appear;
768 
769 	memcpy(&ctx->model_matrix, &tr_state->model_matrix, sizeof(GF_Matrix));
770 	ctx->color_mat.identity = tr_state->color_mat.identity;
771 	if (!tr_state->color_mat.identity) memcpy(&ctx->color_mat, &tr_state->color_mat, sizeof(GF_ColorMatrix));
772 
773 	ctx->pixel_metrics = tr_state->pixel_metrics;
774 	ctx->text_split_idx = tr_state->text_split_idx;
775 
776 	i=0;
777 	while ((ol = (DirectionalLightContext*)gf_list_enum(tr_state->local_lights, &i))) {
778 		DirectionalLightContext *nl = (DirectionalLightContext*)gf_malloc(sizeof(DirectionalLightContext));
779 		memcpy(nl, ol, sizeof(DirectionalLightContext));
780 		gf_list_add(ctx->directional_lights, nl);
781 	}
782 	ctx->clipper = tr_state->clipper;
783 	ctx->has_clipper = tr_state->has_clip;
784 	ctx->cull_flag = tr_state->cull_flag;
785 
786 	if ((ctx->num_clip_planes = tr_state->num_clip_planes))
787 		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 	tr_state->bbox = drawable->mesh->bounds;
791 
792 	gf_mx_apply_bbox(&ctx->model_matrix, &tr_state->bbox);
793 	gf_mx_apply_bbox(&tr_state->camera->modelview, &tr_state->bbox);
794 	ctx->zmax = tr_state->bbox.max_edge.z;
795 #ifdef GF_SR_USE_DEPTH
796 	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 	count = gf_list_count(tr_state->visual->alpha_nodes_to_draw);
802 	for (i=0; i<count; i++) {
803 		Drawable3DContext *next = (Drawable3DContext *)gf_list_get(tr_state->visual->alpha_nodes_to_draw, i);
804 		if (next->zmax>ctx->zmax) {
805 			gf_list_insert(tr_state->visual->alpha_nodes_to_draw, ctx, i);
806 			return;
807 		}
808 	}
809 	gf_list_add(tr_state->visual->alpha_nodes_to_draw, ctx);
810 }
811 
visual_3d_flush_contexts(GF_VisualManager * visual,GF_TraverseState * tr_state)812 void visual_3d_flush_contexts(GF_VisualManager *visual, GF_TraverseState *tr_state)
813 {
814 	u32 i, idx, count;
815 	Bool pixel_metrics = tr_state->pixel_metrics;
816 
817 	tr_state->traversing_mode = TRAVERSE_DRAW_3D;
818 
819 	count = gf_list_count(visual->alpha_nodes_to_draw);
820 	for (idx=0; idx<count; idx++) {
821 		DirectionalLightContext *dl;
822 		Drawable3DContext *ctx = (Drawable3DContext *)gf_list_get(visual->alpha_nodes_to_draw, idx);
823 
824 		/*apply directional lights*/
825 		tr_state->local_light_on = 1;
826 		i=0;
827 		while ((dl = (DirectionalLightContext*)gf_list_enum(ctx->directional_lights, &i))) {
828 			GF_Matrix mx;
829 			gf_mx_copy(mx, tr_state->model_matrix);
830 
831 			gf_mx_add_matrix(&tr_state->model_matrix, &dl->light_matrix);
832 			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 		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 		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 		memcpy(&tr_state->model_matrix, &ctx->model_matrix, sizeof(GF_Matrix));
848 		tr_state->color_mat.identity = ctx->color_mat.identity;
849 		if (!tr_state->color_mat.identity) memcpy(&tr_state->color_mat, &ctx->color_mat, sizeof(GF_ColorMatrix));
850 		tr_state->text_split_idx = ctx->text_split_idx;
851 		tr_state->pixel_metrics = ctx->pixel_metrics;
852 		/*restore cull flag in case we're completely inside (avoids final frustum/AABB tree culling)*/
853 		tr_state->cull_flag = ctx->cull_flag;
854 
855 		tr_state->appear = ctx->appearance;
856 #ifdef GF_SR_USE_DEPTH
857 		tr_state->depth_offset = ctx->depth_offset;
858 #endif
859 		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 		if (tr_state->appear) {
865 			gf_node_dirty_clear(tr_state->appear, 0);
866 			tr_state->appear = NULL;
867 		}
868 
869 		/*reset directional lights*/
870 		tr_state->local_light_on = 0;
871 		for (i=gf_list_count(ctx->directional_lights); i>0; i--) {
872 			dl = (DirectionalLightContext*)gf_list_get(ctx->directional_lights, i-1);
873 			gf_node_traverse(dl->dlight, tr_state);
874 			gf_free(dl);
875 		}
876 
877 		if (ctx->has_clipper) visual_3d_reset_clipper_2d(visual);
878 		for (i=0; i<ctx->num_clip_planes; i++) visual_3d_reset_clip_plane(visual);
879 
880 		/*and destroy*/
881 		gf_list_del(ctx->directional_lights);
882 		gf_free(ctx);
883 	}
884 	tr_state->pixel_metrics = pixel_metrics;
885 	gf_list_reset(tr_state->visual->alpha_nodes_to_draw);
886 }
visual_3d_draw_node(GF_TraverseState * tr_state,GF_Node * root_node)887 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 	if (!tr_state->camera || !tr_state->visual) return;
893 
894 	visual_3d_init_draw(tr_state, 0);
895 
896 	/*main visual, handle collisions*/
897 	if ((tr_state->visual==tr_state->visual->compositor->visual) && tr_state->camera->is_3D)
898 		visual_3d_check_collisions(tr_state, root_node, NULL);
899 
900 #ifndef GPAC_DISABLE_VRML
901 	/*setup fog*/
902 	fog = (GF_Node*) gf_list_get(tr_state->visual->fog_stack, 0);
903 	tr_state->traversing_mode = TRAVERSE_BINDABLE;
904 	if (Bindable_GetIsBound(fog)) gf_node_traverse(fog, tr_state);
905 #endif
906 
907 	/*turn global lights on*/
908 	if (tr_state->visual->type_3d>1) {
909 		tr_state->traversing_mode = TRAVERSE_LIGHTING;
910 		gf_node_traverse(root_node, tr_state);
911 	}
912 
913 	/*sort graph*/
914 	tr_state->traversing_mode = TRAVERSE_SORT;
915 	gf_node_traverse(root_node, tr_state);
916 
917 	/*and draw*/
918 	visual_3d_flush_contexts(tr_state->visual, tr_state);
919 
920 	/*and turn off lights*/
921 	visual_3d_clear_all_lights(tr_state->visual);
922 }
923 
visual_3d_setup_clipper(GF_VisualManager * visual,GF_TraverseState * tr_state)924 void visual_3d_setup_clipper(GF_VisualManager *visual, GF_TraverseState *tr_state)
925 {
926 	GF_Rect rc;
927 	/*setup top clipper*/
928 	if (visual->center_coords) {
929 		rc = gf_rect_center(INT2FIX(visual->width), INT2FIX(visual->height));
930 	} else {
931 		rc.width = INT2FIX(visual->width);
932 		rc.height = INT2FIX(visual->height);
933 		rc.x = 0;
934 		rc.y = rc.height;
935 		if (visual->compositor->visual==visual) {
936 			rc.x += INT2FIX(visual->compositor->vp_x);
937 			rc.y += INT2FIX(visual->compositor->vp_y);
938 		}
939 	}
940 
941 	/*setup viewport*/
942 #ifndef GPAC_DISABLE_VRML
943 	if (gf_list_count(visual->view_stack)) {
944 		tr_state->traversing_mode = TRAVERSE_BINDABLE;
945 		tr_state->bounds = rc;
946 		gf_node_traverse((GF_Node *) gf_list_get(visual->view_stack, 0), tr_state);
947 	}
948 #endif
949 
950 	visual->top_clipper = gf_rect_pixelize(&rc);
951 	tr_state->clipper = rc;
952 	gf_mx_init(tr_state->layer_matrix);
953 }
954 
visual_3d_draw_frame(GF_VisualManager * visual,GF_Node * root,GF_TraverseState * tr_state,Bool is_root_visual)955 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 	u32 time = gf_sys_clock();
959 #endif
960 
961 	if (visual->compositor->fbo_id)
962 		compositor_3d_enable_fbo(visual->compositor, GF_TRUE);
963 
964 	visual_3d_setup(visual);
965 	visual->active_glsl_flags = 0;
966 
967 	/*setup our traversing state*/
968 	visual_3d_setup_traversing_state(visual, tr_state);
969 
970 	if (is_root_visual) {
971 		Bool auto_stereo = 0;
972 
973 		visual_3d_setup_clipper(visual, tr_state);
974 
975 		if (tr_state->visual->autostereo_type > GF_3D_STEREO_LAST_SINGLE_BUFFER) {
976 			visual_3d_init_autostereo(visual);
977 			auto_stereo = 1;
978 		}
979 
980 #ifndef GPAC_USE_GLES1X
981 		visual_3d_init_shaders(visual);
982 #endif
983 
984 		for (visual->current_view=0; visual->current_view < visual->nb_views; visual->current_view++) {
985 			GF_SceneGraph *sg;
986 			u32 i;
987 			visual_3d_draw_node(tr_state, root);
988 
989 			/*extra scene graphs*/
990 			i=0;
991 			while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) {
992 				tr_state->traversing_mode = TRAVERSE_SORT;
993 				gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state);
994 			}
995 
996 			if (auto_stereo) {
997 				visual_3d_end_auto_stereo_pass(visual);
998 			}
999 
1000 			visual->compositor->reset_graphics = 0;
1001 		}
1002 
1003 	} else {
1004 		visual_3d_draw_node(tr_state, root);
1005 	}
1006 	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 	if (visual->compositor->fbo_id)
1009 		compositor_3d_enable_fbo(visual->compositor, GF_FALSE);
1010 
1011 	return 1;
1012 }
1013 
reset_collide_cursor(GF_Compositor * compositor)1014 static void reset_collide_cursor(GF_Compositor *compositor)
1015 {
1016 	if (compositor->sensor_type == GF_CURSOR_COLLIDE) {
1017 		GF_Event evt;
1018 		compositor->sensor_type = evt.cursor.cursor_type = GF_CURSOR_NORMAL;
1019 		evt.type = GF_EVENT_SET_CURSOR;
1020 		compositor->video_out->ProcessEvent(compositor->video_out, &evt);
1021 	}
1022 }
1023 
visual_3d_check_collisions(GF_TraverseState * tr_state,GF_Node * on_node,GF_ChildNodeItem * node_list)1024 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 	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 		tr_state->camera->last_had_ground = 0;
1035 		/*and avoid reseting move at next collision change*/
1036 		tr_state->camera->last_pos = tr_state->camera->position;
1037 		return;
1038 	}
1039 	/*don't collide if not moved*/
1040 	if (gf_vec_equal(tr_state->camera->position, tr_state->camera->last_pos)) {
1041 		reset_collide_cursor(tr_state->visual->compositor);
1042 		return;
1043 	}
1044 	tr_state->traversing_mode = TRAVERSE_COLLIDE;
1045 	tr_state->camera->collide_flags = 0;
1046 	tr_state->camera->collide_dist = FIX_MAX;
1047 	tr_state->camera->ground_dist = FIX_MAX;
1048 	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 	gf_vec_diff(dir, tr_state->camera->position, tr_state->camera->last_pos);
1051 	pos_diff = gf_vec_len(dir);
1052 	gf_vec_norm(&dir);
1053 
1054 	diff = 0;
1055 	go = 1;
1056 	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 	while (go) {
1062 		if (pos_diff>tr_state->camera->avatar_size.x) {
1063 			pos_diff-=tr_state->camera->avatar_size.x;
1064 			diff += tr_state->camera->avatar_size.x;
1065 		} else {
1066 			diff += pos_diff;
1067 			go = 0;
1068 		}
1069 		n = gf_vec_scale(dir, diff);
1070 		gf_vec_add(tr_state->camera->position, tr_state->camera->last_pos, n);
1071 		if (on_node) {
1072 			gf_node_traverse(on_node, tr_state);
1073 		} else if (node_list) {
1074 			while (node_list) {
1075 				gf_node_traverse(node_list->node, tr_state);
1076 				node_list = node_list->next;
1077 			}
1078 		}
1079 		if (tr_state->camera->collide_flags & CF_COLLISION) break;
1080 		tr_state->camera->collide_flags &= ~CF_DO_GRAVITY;
1081 	}
1082 
1083 	/*gravity*/
1084 	if (tr_state->camera->collide_flags & CF_GRAVITY) {
1085 		diff = tr_state->camera->ground_dist - tr_state->camera->avatar_size.y;
1086 		if (tr_state->camera->last_had_ground && (-diff>tr_state->camera->avatar_size.z)) {
1087 			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Obstacle detected - too high (dist %g)\n", FIX2FLT(diff)));
1088 			tr_state->camera->position = tr_state->camera->last_pos;
1089 			tr_state->camera->flags |= CAM_IS_DIRTY;
1090 		} else {
1091 			if ((tr_state->camera->jumping && fabs(diff)>tr_state->camera->dheight)
1092 			        || (!tr_state->camera->jumping && (ABS(diff)>FIX_ONE/1000) )) {
1093 				tr_state->camera->last_had_ground = 1;
1094 				n = gf_vec_scale(tr_state->camera->up, -diff);
1095 				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 				gf_vec_add(tr_state->camera->position, tr_state->camera->position, n);
1100 				gf_vec_add(tr_state->camera->target, tr_state->camera->target, n);
1101 				gf_vec_add(tr_state->camera->last_pos, tr_state->camera->position, n);
1102 				tr_state->camera->flags |= CAM_IS_DIRTY;
1103 			}
1104 		}
1105 	}
1106 	/*collsion found*/
1107 	if (tr_state->camera->collide_flags & CF_COLLISION) {
1108 		if (tr_state->visual->compositor->sensor_type != GF_CURSOR_COLLIDE) {
1109 			GF_Event evt;
1110 			tr_state->camera->last_had_col = 1;
1111 			evt.type = GF_EVENT_SET_CURSOR;
1112 			tr_state->visual->compositor->sensor_type = evt.cursor.cursor_type = GF_CURSOR_COLLIDE;
1113 			tr_state->visual->compositor->video_out->ProcessEvent(tr_state->visual->compositor->video_out, &evt);
1114 		}
1115 
1116 		/*regular collision*/
1117 		if (tr_state->visual->compositor->collide_mode==GF_COLLISION_NORMAL) {
1118 			tr_state->camera->position = tr_state->camera->last_pos;
1119 			tr_state->camera->flags |= CAM_IS_DIRTY;
1120 			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Collision detected - restoring previous avatar position\n"));
1121 		} else {
1122 			/*camera displacement collision*/
1123 			if (tr_state->camera->collide_dist) {
1124 				if (tr_state->camera->collide_dist>=tr_state->camera->avatar_size.x) {
1125 					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 					tr_state->camera->collide_dist = tr_state->camera->avatar_size.x;
1129 				}
1130 
1131 				gf_vec_diff(n, tr_state->camera->position, tr_state->camera->collide_point);
1132 				gf_vec_norm(&n);
1133 				n = gf_vec_scale(n, tr_state->camera->avatar_size.x - tr_state->camera->collide_dist);
1134 				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 				gf_vec_add(tr_state->camera->position, tr_state->camera->position, n);
1139 				gf_vec_add(tr_state->camera->target, tr_state->camera->target, n);
1140 			} else {
1141 				GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Collision detected and camera on hit point - restoring previous avatar position\n"));
1142 				tr_state->camera->position = tr_state->camera->last_pos;
1143 			}
1144 			tr_state->camera->last_pos = tr_state->camera->position;
1145 			tr_state->camera->flags |= CAM_IS_DIRTY;
1146 		}
1147 	} else {
1148 		reset_collide_cursor(tr_state->visual->compositor);
1149 		tr_state->camera->last_pos = tr_state->camera->position;
1150 		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] no collision found\n"));
1151 	}
1152 
1153 	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 
visual_3d_node_cull(GF_TraverseState * tr_state,GF_BBox * bbox,Bool skip_near)1166 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 	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 	if (!bbox->is_set) {
1184 		tr_state->cull_flag = CULL_OUTSIDE;
1185 		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 	b = *bbox;
1191 	gf_mx_apply_bbox_sphere(&tr_state->model_matrix, &b);
1192 	cam = tr_state->camera;
1193 
1194 	/*if camera is inside bbox consider we intersect*/
1195 	if (gf_bbox_point_inside(&b, &cam->position)) {
1196 		tr_state->cull_flag = CULL_INTERSECTS;
1197 		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 	if (tr_state->camera->is_3D) {
1202 		gf_vec_diff(cdiff, cam->center, b.center);
1203 		rad = b.radius + cam->radius;
1204 		if (gf_vec_len(cdiff) > rad) {
1205 			tr_state->cull_flag = CULL_OUTSIDE;
1206 			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 	rad = b.radius;
1214 	irad = -b.radius;
1215 	do_sphere = 1;
1216 
1217 	/*skip near/far tests in ortho mode, and near in 3D*/
1218 	i = (tr_state->camera->is_3D) ? (skip_near ? 1 : 0) : 2;
1219 	for (; i<6; i++) {
1220 		Fixed d;
1221 		if (do_sphere) {
1222 			d = gf_plane_get_distance(&cam->planes[i], &b.center);
1223 			if (d<irad) {
1224 				tr_state->cull_flag = CULL_OUTSIDE;
1225 				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 			if (d<rad) {
1230 				/*get full bbox in world coords*/
1231 				b = *bbox;
1232 				gf_mx_apply_bbox(&tr_state->model_matrix, &b);
1233 				/*get box vertices*/
1234 				gf_bbox_get_vertices(b.min_edge, b.max_edge, vertices);
1235 				do_sphere = 0;
1236 			} else {
1237 				continue;
1238 			}
1239 		}
1240 		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 		d = gf_plane_get_distance(&cam->planes[i], &vertices[p_idx]);
1243 		if (d<0) {
1244 			tr_state->cull_flag = CULL_OUTSIDE;
1245 			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 		if (i>1) {
1251 			d = gf_plane_get_distance(&cam->planes[i], &vertices[7-p_idx]);
1252 			if (d<0) {
1253 				tr_state->cull_flag = CULL_INTERSECTS;
1254 				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 	tr_state->cull_flag = CULL_INSIDE;
1261 	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 
visual_3d_setup_ray(GF_VisualManager * visual,GF_TraverseState * tr_state,s32 ix,s32 iy)1266 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 	x = INT2FIX(ix);
1273 	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 	if (!visual->center_coords) {
1278 		x = x - INT2FIX(tr_state->camera->width)/2;
1279 		y = INT2FIX(tr_state->camera->height)/2 - y;
1280 	}
1281 
1282 
1283 	/*main visual with AR*/
1284 	if ((visual->compositor->visual == visual) && visual->compositor->has_size_info) {
1285 		Fixed scale = gf_divfix(INT2FIX(visual->width), INT2FIX(visual->compositor->vp_width));
1286 		x = gf_mulfix(x, scale);
1287 		scale = gf_divfix(INT2FIX(visual->height), INT2FIX(visual->compositor->vp_height));
1288 		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 	in_x = 2*x/ (s32) visual->width;
1305 	in_y = 2*y/ (s32) visual->height;
1306 
1307 	res.x = in_x;
1308 	res.y = in_y;
1309 	res.z = -FIX_ONE/2;
1310 	res.q = FIX_ONE;
1311 	gf_mx_apply_vec_4x4(&visual->camera.unprojection, &res);
1312 	if (!res.q) return GF_FALSE;
1313 	start.x = gf_divfix(res.x, res.q);
1314 	start.y = gf_divfix(res.y, res.q);
1315 	start.z = gf_divfix(res.z, res.q);
1316 
1317 	res.x = in_x;
1318 	res.y = in_y;
1319 	res.z = FIX_ONE/2;
1320 	res.q = FIX_ONE;
1321 	gf_mx_apply_vec_4x4(&visual->camera.unprojection, &res);
1322 	if (!res.q) return GF_FALSE;
1323 	end.x = gf_divfix(res.x, res.q);
1324 	end.y = gf_divfix(res.y, res.q);
1325 	end.z = gf_divfix(res.z, res.q);
1326 
1327 	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 	visual->compositor->hit_world_ray = tr_state->ray;
1330 
1331 	return GF_TRUE;
1332 }
1333 
visual_3d_pick_node(GF_VisualManager * visual,GF_TraverseState * tr_state,GF_Event * ev,GF_ChildNodeItem * children)1334 void visual_3d_pick_node(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children)
1335 {
1336 
1337 	visual_3d_setup_traversing_state(visual, tr_state);
1338 	visual_3d_setup_projection(tr_state, 0);
1339 
1340 
1341 	if (!visual_3d_setup_ray(visual, tr_state, ev->mouse.x, ev->mouse.y))
1342 		return;
1343 
1344 	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 	visual->compositor->hit_square_dist = 0;
1350 	visual->compositor->hit_node = NULL;
1351 	gf_list_reset(visual->compositor->sensors);
1352 
1353 	/*not the root scene, use children list*/
1354 	if (visual->compositor->visual != visual) {
1355 		while (children) {
1356 			gf_node_traverse(children->node, tr_state);
1357 			children = children->next;
1358 		}
1359 	} else {
1360 		gf_node_traverse(gf_sg_get_root_node(visual->compositor->scene), tr_state);
1361 	}
1362 }
1363 
1364 
1365 #ifndef GPAC_DISABLE_VRML
visual_3d_vrml_drawable_pick(GF_Node * n,GF_TraverseState * tr_state,GF_Mesh * mesh,Drawable * drawable)1366 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 	u32 cull_bckup = tr_state->cull_flag;
1378 
1379 	if (!mesh && !drawable) return;
1380 
1381 	count = gf_list_count(tr_state->vrml_sensors);
1382 	compositor = tr_state->visual->compositor;
1383 
1384 	if (mesh) {
1385 		if (mesh->mesh_type!=MESH_TRIANGLES)
1386 			return;
1387 		if (!visual_3d_node_cull(tr_state, &mesh->bounds, 0)) {
1388 			tr_state->cull_flag = cull_bckup;
1389 			return;
1390 		}
1391 	}
1392 	tr_state->cull_flag = cull_bckup;
1393 	r = tr_state->ray;
1394 	gf_mx_copy(mx, tr_state->model_matrix);
1395 	gf_mx_inverse(&mx);
1396 	gf_mx_apply_ray(&mx, &r);
1397 
1398 	/*if we already have a hit point don't check anything below...*/
1399 	if (compositor->hit_square_dist && !compositor->grabbed_sensor && !tr_state->layer3d) {
1400 		GF_Plane p;
1401 		GF_BBox box;
1402 		SFVec3f hit = compositor->hit_world_point;
1403 		gf_mx_apply_vec(&mx, &hit);
1404 		p.normal = r.dir;
1405 		p.d = -1 * gf_vec_dot(p.normal, hit);
1406 		if (mesh)
1407 			box = mesh->bounds;
1408 		else
1409 			gf_bbox_from_rect(&box, &drawable->path->bbox);
1410 
1411 		if (gf_bbox_plane_relation(&box, &p) == GF_BBOX_FRONT) {
1412 			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 			return;
1414 		}
1415 	}
1416 	if (drawable) {
1417 		DrawAspect2D asp;
1418 		node_is_over = 0;
1419 		if (compositor_get_2d_plane_intersection(&r, &local_pt)) {
1420 			if (gf_path_point_over(drawable->path, local_pt.x, local_pt.y)) {
1421 				hit_normal.x = hit_normal.y = 0;
1422 				hit_normal.z = FIX_ONE;
1423 				text_coords.x = gf_divfix(local_pt.x, drawable->path->bbox.width) + FIX_ONE/2;
1424 				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 			drawable_get_aspect_2d_mpeg4(drawable->node, &asp, tr_state);
1429 			if (asp.pen_props.width || asp.line_texture ) {
1430 				StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, tr_state->appear, NULL, 0, NULL);
1431 				if (si && si->outline && gf_path_point_over(si->outline, local_pt.x, local_pt.y)) {
1432 					hit_normal.x = hit_normal.y = 0;
1433 					hit_normal.z = FIX_ONE;
1434 					text_coords.x = gf_divfix(local_pt.x, si->outline->bbox.width) + FIX_ONE/2;
1435 					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 		node_is_over = gf_mesh_intersect_ray(mesh, &r, &local_pt, &hit_normal, &text_coords);
1442 	}
1443 
1444 	if (!node_is_over) return;
1445 
1446 	/*check distance from user and keep the closest hitpoint*/
1447 	world_pt = local_pt;
1448 	gf_mx_apply_vec(&tr_state->model_matrix, &world_pt);
1449 
1450 	for (i=0; i<tr_state->num_clip_planes; i++) {
1451 		if (gf_plane_get_distance(&tr_state->clip_planes[i], &world_pt) < 0) {
1452 			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 	gf_vec_diff(vdiff, world_pt, tr_state->ray.orig);
1458 	sqdist = gf_vec_lensq(vdiff);
1459 	if (compositor->hit_square_dist && (compositor->hit_square_dist+FIX_EPSILON<sqdist)) {
1460 		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 	compositor->hit_square_dist = sqdist;
1465 	gf_list_reset(compositor->sensors);
1466 	for (i=0; i<count; i++) {
1467 		gf_list_add(compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
1468 	}
1469 
1470 	gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix);
1471 	gf_mx_copy(compositor->hit_local_to_world, mx);
1472 	compositor->hit_local_point = local_pt;
1473 	compositor->hit_world_point = world_pt;
1474 	compositor->hit_world_ray = tr_state->ray;
1475 	compositor->hit_normal = hit_normal;
1476 	compositor->hit_texcoords = text_coords;
1477 
1478 	if (compositor_is_composite_texture(tr_state->appear)) {
1479 		compositor->hit_appear = tr_state->appear;
1480 	} else {
1481 		compositor->hit_appear = NULL;
1482 	}
1483 	compositor->hit_node = n;
1484 	compositor->hit_use_dom_events = 0;
1485 	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 
visual_3d_vrml_drawable_collide(GF_Node * node,GF_TraverseState * tr_state)1490 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 	Drawable3D *st = (Drawable3D *)gf_node_get_private(node);
1497 	if (!st || !st->mesh) return;
1498 
1499 	/*no collision with lines & points*/
1500 	if (st->mesh->mesh_type != MESH_TRIANGLES) return;
1501 
1502 #ifndef GPAC_DISABLE_VRML
1503 	/*no collision with text (vrml)*/
1504 	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 	cull_backup = tr_state->cull_flag;
1515 	if (!visual_3d_node_cull(tr_state, &st->mesh->bounds, 1)) {
1516 		tr_state->cull_flag = cull_backup;
1517 		return;
1518 	}
1519 	tr_state->cull_flag = cull_backup;
1520 
1521 	/*use up & front to get an average size of the collision dist in this space*/
1522 	pos = tr_state->camera->position;
1523 	last_pos = tr_state->camera->last_pos;
1524 	v1 = camera_get_target_dir(tr_state->camera);
1525 	v1 = gf_vec_scale(v1, tr_state->camera->avatar_size.x);
1526 	gf_vec_add(v1, v1, pos);
1527 	v2 = camera_get_right_dir(tr_state->camera);
1528 	v2 = gf_vec_scale(v2, tr_state->camera->avatar_size.x);
1529 	gf_vec_add(v2, v2, pos);
1530 
1531 	gf_mx_copy(mx, tr_state->model_matrix);
1532 	gf_mx_inverse(&mx);
1533 
1534 	gf_mx_apply_vec(&mx, &pos);
1535 	gf_mx_apply_vec(&mx, &last_pos);
1536 	gf_mx_apply_vec(&mx, &v1);
1537 	gf_mx_apply_vec(&mx, &v2);
1538 
1539 	gf_vec_diff(v1, v1, pos);
1540 	gf_vec_diff(v2, v2, pos);
1541 	dist = gf_vec_len(v1);
1542 	m_dist = gf_vec_len(v2);
1543 	if (dist<m_dist) m_dist = dist;
1544 
1545 	/*check for any collisions*/
1546 	if (gf_mesh_closest_face(st->mesh, pos, m_dist, &collide_pt)) {
1547 		/*get transformed hit*/
1548 		gf_mx_apply_vec(&tr_state->model_matrix, &collide_pt);
1549 		gf_vec_diff(v2, tr_state->camera->position, collide_pt);
1550 		dist = gf_vec_len(v2);
1551 		if (dist<tr_state->camera->collide_dist) {
1552 			tr_state->camera->collide_dist = dist;
1553 			tr_state->camera->collide_flags |= CF_COLLISION;
1554 			tr_state->camera->collide_point = collide_pt;
1555 
1556 #ifndef GPAC_DISABLE_LOG
1557 			if (gf_log_tool_level_on(GF_LOG_COMPOSE, GF_LOG_DEBUG)) {
1558 				gf_vec_diff(v1, pos, collide_pt);
1559 				gf_vec_norm(&v1);
1560 				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 			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 	if (tr_state->camera->collide_flags & CF_DO_GRAVITY) {
1573 		GF_Ray r;
1574 		Bool intersect;
1575 		r.orig = tr_state->camera->position;
1576 		r.dir = gf_vec_scale(tr_state->camera->up, -FIX_ONE);
1577 		gf_mx_apply_ray(&mx, &r);
1578 
1579 		intersect = gf_mesh_intersect_ray(st->mesh, &r, &collide_pt, &v1, NULL);
1580 
1581 		if (intersect) {
1582 			gf_mx_apply_vec(&tr_state->model_matrix, &collide_pt);
1583 			gf_vec_diff(v2, tr_state->camera->position, collide_pt);
1584 			dist = gf_vec_len(v2);
1585 			if (dist<tr_state->camera->ground_dist) {
1586 				tr_state->camera->ground_dist = dist;
1587 				tr_state->camera->collide_flags |= CF_GRAVITY;
1588 				tr_state->camera->ground_point = collide_pt;
1589 				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 				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 
visual_3d_setup_texture_2d(GF_TraverseState * tr_state,DrawAspect2D * asp,GF_Mesh * mesh)1604 static GF_TextureHandler *visual_3d_setup_texture_2d(GF_TraverseState *tr_state, DrawAspect2D *asp, GF_Mesh *mesh)
1605 {
1606 	if (!asp->fill_texture) return NULL;
1607 
1608 	if (asp->fill_color && (GF_COL_A(asp->fill_color) != 0xFF)) {
1609 		visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color);
1610 		gf_sc_texture_set_blend_mode(asp->fill_texture, TX_MODULATE);
1611 	} else {
1612 		visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0);
1613 		gf_sc_texture_set_blend_mode(asp->fill_texture, TX_REPLACE);
1614 	}
1615 
1616 	if (asp->fill_texture->flags & GF_SR_TEXTURE_SVG) {
1617 		GF_Rect rc;
1618 		gf_rect_from_bbox(&rc, &mesh->bounds);
1619 		tr_state->mesh_num_textures = gf_sc_texture_enable_ex(asp->fill_texture, NULL, &rc);
1620 	} else {
1621 #ifndef GPAC_DISABLE_VRML
1622 		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 	if (tr_state->mesh_num_textures) return asp->fill_texture;
1626 	return NULL;
1627 }
1628 
visual_3d_set_2d_strike(GF_TraverseState * tr_state,DrawAspect2D * asp)1629 void visual_3d_set_2d_strike(GF_TraverseState *tr_state, DrawAspect2D *asp)
1630 {
1631 	if (asp->line_texture) {
1632 		GF_Node *txtrans = NULL;
1633 #ifndef GPAC_DISABLE_VRML
1634 		if (tr_state->appear
1635 		        && (gf_node_get_tag( ((M_Appearance *)tr_state->appear)->material) == TAG_MPEG4_Material2D)
1636 		        && (gf_node_get_tag(((M_Material2D *) ((M_Appearance *)tr_state->appear)->material)->lineProps) == TAG_MPEG4_XLineProperties)
1637 		   ) {
1638 			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 		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 		tr_state->mesh_num_textures = gf_sc_texture_enable(asp->line_texture, txtrans);
1654 		if (tr_state->mesh_num_textures) return;
1655 	}
1656 	/*no texture or not ready, use color*/
1657 	if (asp->line_color)
1658 		visual_3d_set_material_2d_argb(tr_state->visual, asp->line_color);
1659 }
1660 
1661 
visual_3d_draw_2d_with_aspect(Drawable * st,GF_TraverseState * tr_state,DrawAspect2D * asp)1662 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 	fill_txh = visual_3d_setup_texture_2d(tr_state, asp, st->mesh);
1668 
1669 	/*fill path*/
1670 	if (fill_txh || (GF_COL_A(asp->fill_color)) ) {
1671 		if (!st->mesh) return;
1672 
1673 		if (asp->fill_color)
1674 			visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color);
1675 		else if (GF_COL_A(asp->line_color) && !(asp->line_color & 0x00FFFFFF)) {
1676 			u32 col = asp->line_color | 0x00FFFFFF;
1677 			visual_3d_set_material_2d_argb(tr_state->visual, col);
1678 		}
1679 
1680 		visual_3d_mesh_paint(tr_state, st->mesh);
1681 		/*reset texturing in case of line texture*/
1682 		if (tr_state->mesh_num_textures) {
1683 			gf_sc_texture_disable(fill_txh);
1684 			tr_state->mesh_num_textures = 0;
1685 		}
1686 	}
1687 
1688 	if ((tr_state->visual->type_3d == 4) && !asp->line_texture) return;
1689 
1690 	/*strike path*/
1691 	if (!asp->pen_props.width || !GF_COL_A(asp->line_color)) return;
1692 
1693 	si = drawable_get_strikeinfo(tr_state->visual->compositor, st, asp, tr_state->appear, NULL, 0, tr_state);
1694 	if (!si) return;
1695 
1696 	if (!si->mesh_outline) {
1697 		si->is_vectorial = asp->line_texture ? GF_TRUE : !tr_state->visual->compositor->linegl;
1698 		si->mesh_outline = new_mesh();
1699 #ifdef GPAC_HAS_GLU
1700 		if (si->is_vectorial) {
1701 			gf_mesh_tesselate_path(si->mesh_outline, si->outline, asp->line_texture ? 2 : 1);
1702 		} else
1703 #endif
1704 			mesh_get_outline(si->mesh_outline, st->path);
1705 	}
1706 
1707 	visual_3d_set_2d_strike(tr_state, asp);
1708 	if (asp->line_texture) tr_state->mesh_num_textures = 1;
1709 
1710 	if (!si->is_vectorial) {
1711 		visual_3d_mesh_strike(tr_state, si->mesh_outline, asp->pen_props.width, asp->line_scale, asp->pen_props.dash);
1712 	} else {
1713 		visual_3d_mesh_paint(tr_state, si->mesh_outline);
1714 	}
1715 	if (asp->line_texture) {
1716 		gf_sc_texture_disable(asp->line_texture);
1717 		tr_state->mesh_num_textures = 0;
1718 	}
1719 }
1720 
1721 #ifndef GPAC_DISABLE_VRML
visual_3d_draw_2d(Drawable * st,GF_TraverseState * tr_state)1722 void visual_3d_draw_2d(Drawable *st, GF_TraverseState *tr_state)
1723 {
1724 	DrawAspect2D asp;
1725 	memset(&asp, 0, sizeof(DrawAspect2D));
1726 	drawable_get_aspect_2d_mpeg4(st->node, &asp, tr_state);
1727 	visual_3d_draw_2d_with_aspect(st, tr_state, &asp);
1728 }
1729 #endif
1730 
1731 
visual_3d_draw_from_context(DrawableContext * ctx,GF_TraverseState * tr_state)1732 void visual_3d_draw_from_context(DrawableContext *ctx, GF_TraverseState *tr_state)
1733 {
1734 	GF_Rect rc;
1735 	gf_path_get_bounds(ctx->drawable->path, &rc);
1736 	visual_3d_draw_2d_with_aspect(ctx->drawable, tr_state, &ctx->aspect);
1737 
1738 	drawable_check_focus_highlight(ctx->drawable->node, tr_state, &rc);
1739 }
1740 
1741 
visual_3d_setup_material(GF_TraverseState * tr_state,u32 mesh_type,Fixed * diffuse_alpha)1742 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 	if (diffuse_alpha) *diffuse_alpha = FIX_ONE;
1751 
1752 	if (!tr_state->appear) {
1753 		/*use material2D to disable lighting*/
1754 		visual_3d_set_material_2d(tr_state->visual, def, FIX_ONE);
1755 		return 1;
1756 	}
1757 
1758 #ifndef GPAC_DISABLE_VRML
1759 #ifndef GPAC_DISABLE_X3D
1760 	if (gf_node_get_tag(tr_state->appear)==TAG_X3D_Appearance) {
1761 		X_FillProperties *fp = (X_FillProperties *) ((X_Appearance*)tr_state->appear)->fillProperties;
1762 		if (fp && !fp->filled) return 0;
1763 	}
1764 #endif
1765 
1766 	__mat = ((M_Appearance *)tr_state->appear)->material;
1767 	if (!__mat) {
1768 		/*use material2D to disable lighting (cf VRML specs)*/
1769 		visual_3d_set_material_2d(tr_state->visual, def, FIX_ONE);
1770 		return 1;
1771 	}
1772 
1773 	switch (gf_node_get_tag((GF_Node *)__mat)) {
1774 	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 		diff = mat->diffuseColor;
1787 		diff_a = FIX_ONE - mat->transparency;
1788 
1789 		/*if drawing in 2D context or special meshes (lines, points) disable lighting*/
1790 		if (mesh_type || !tr_state->camera->is_3D) {
1791 			if (tr_state->camera->is_3D) diff = mat->emissiveColor;
1792 			if (!tr_state->color_mat.identity) gf_cmx_apply_fixed(&tr_state->color_mat, &diff_a, &diff.red, &diff.green, &diff.blue);
1793 			visual_3d_set_material_2d(tr_state->visual, diff, diff_a);
1794 			return 1;
1795 		}
1796 
1797 		spec = mat->specularColor;
1798 		emi = mat->emissiveColor;
1799 		spec_a = emi_a = FIX_ONE - mat->transparency;
1800 		if (!tr_state->color_mat.identity) {
1801 			gf_cmx_apply_fixed(&tr_state->color_mat, &diff_a, &diff.red, &diff.green, &diff.blue);
1802 			gf_cmx_apply_fixed(&tr_state->color_mat, &spec_a, &spec.red, &spec.green, &spec.blue);
1803 			gf_cmx_apply_fixed(&tr_state->color_mat, &emi_a, &emi.red, &emi.green, &emi.blue);
1804 
1805 			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 			has_alpha = (mat->transparency>FIX_EPSILON) ? 1 : 0;
1812 			/*100% transparent DON'T DRAW*/
1813 			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 		visual_3d_enable_antialias(tr_state->visual, !has_alpha);
1818 		if (has_alpha) {
1819 			flag |= V3D_STATE_BLEND;
1820 			tr_state->mesh_is_transparent = 1;
1821 		}
1822 		visual_3d_set_state(tr_state->visual, flag, 1);
1823 
1824 		vec[0] = gf_mulfix(diff.red, mat->ambientIntensity);
1825 		vec[1] = gf_mulfix(diff.green, mat->ambientIntensity);
1826 		vec[2] = gf_mulfix(diff.blue, mat->ambientIntensity);
1827 		vec[3] = diff_a;
1828 		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 		visual_3d_set_material(tr_state->visual, V3D_MATERIAL_DIFFUSE, vec);
1835 
1836 		vec[0] = spec.red;
1837 		vec[1] = spec.green;
1838 		vec[2] = spec.blue;
1839 		vec[3] = spec_a;
1840 		visual_3d_set_material(tr_state->visual, V3D_MATERIAL_SPECULAR, vec);
1841 
1842 
1843 		vec[0] = emi.red;
1844 		vec[1] = emi.green;
1845 		vec[2] = emi.blue;
1846 		vec[3] = emi_a;
1847 		visual_3d_set_material(tr_state->visual, V3D_MATERIAL_EMISSIVE, vec);
1848 
1849 		visual_3d_set_shininess(tr_state->visual, mat->shininess);
1850 		if (diffuse_alpha) *diffuse_alpha = diff_a;
1851 	}
1852 	break;
1853 	case TAG_MPEG4_Material2D:
1854 	{
1855 		SFColor emi;
1856 		Fixed emi_a;
1857 		M_Material2D *mat = (M_Material2D *)__mat;
1858 
1859 		emi = mat->emissiveColor;
1860 		emi_a = FIX_ONE - mat->transparency;
1861 		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 		if (emi_a<FIX_EPSILON) return 0;
1864 		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 		if (!mat->filled) {
1870 			if (mat->transparency) {
1871 				emi.red = emi.green = emi.blue = FIX_ONE;
1872 			} else {
1873 				GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture);
1874 				if (txh) {
1875 					gf_sc_texture_set_blend_mode(txh, TX_REPLACE);
1876 					visual_3d_set_state(tr_state->visual, V3D_STATE_COLOR, 0);
1877 					visual_3d_set_state(tr_state->visual, V3D_STATE_LIGHT, 1);
1878 					return 1;
1879 				}
1880 			}
1881 		}
1882 		/*regular mat 2D*/
1883 		visual_3d_set_state(tr_state->visual, V3D_STATE_LIGHT | V3D_STATE_COLOR, 0);
1884 		visual_3d_set_material_2d(tr_state->visual, emi, emi_a);
1885 	}
1886 	break;
1887 	default:
1888 		break;
1889 	}
1890 	return 1;
1891 #else
1892 	return 0;
1893 #endif	/*GPAC_DISABLE_VRML*/
1894 }
1895 
visual_3d_setup_texture(GF_TraverseState * tr_state,Fixed diffuse_alpha)1896 Bool visual_3d_setup_texture(GF_TraverseState *tr_state, Fixed diffuse_alpha)
1897 {
1898 #ifndef GPAC_DISABLE_VRML
1899 	GF_TextureHandler *txh;
1900 	tr_state->mesh_num_textures = 0;
1901 	if (!tr_state->appear) return GF_TRUE;
1902 
1903 	gf_node_dirty_reset(tr_state->appear, 0);
1904 
1905 	txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture);
1906 	//no texture, return TRUE (eg draw)
1907 	if (!txh)
1908 		return GF_TRUE;
1909 
1910 	gf_sc_texture_set_blend_mode(txh, gf_sc_texture_is_transparent(txh) ? TX_MODULATE : TX_REPLACE);
1911 	tr_state->mesh_num_textures = gf_sc_texture_enable(txh, ((M_Appearance *)tr_state->appear)->textureTransform);
1912 	if (tr_state->mesh_num_textures) {
1913 		Fixed v[4];
1914 		switch (txh->pixelformat) {
1915 		/*override diffuse color with full intensity, but keep material alpha (cf VRML lighting)*/
1916 		case GF_PIXEL_RGB:
1917 			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 		case GF_PIXEL_RGBA:
1929 			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 			tr_state->mesh_is_transparent = 1;
1934 			break;
1935 		}
1936 	}
1937 	return tr_state->mesh_num_textures ? GF_TRUE : GF_FALSE;
1938 #else
1939 	return GF_TRUE;
1940 #endif /*GPAC_DISABLE_VRML*/
1941 }
1942 
visual_3d_disable_texture(GF_TraverseState * tr_state)1943 void visual_3d_disable_texture(GF_TraverseState *tr_state)
1944 {
1945 	if (tr_state->mesh_num_textures) {
1946 #ifndef GPAC_DISABLE_VRML
1947 		gf_sc_texture_disable(gf_sc_texture_get_handler( ((M_Appearance *)tr_state->appear)->texture) );
1948 #endif
1949 		tr_state->mesh_num_textures = 0;
1950 	}
1951 }
1952 
visual_3d_setup_appearance(GF_TraverseState * tr_state)1953 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 	if (!visual_3d_setup_material(tr_state, 0, &diff_a)) return 0;
1958 	/*setup texture*/
1959 	if (! visual_3d_setup_texture(tr_state, diff_a)) return 0;
1960 	return 1;
1961 }
1962 
1963 
visual_3d_draw(GF_TraverseState * tr_state,GF_Mesh * mesh)1964 void visual_3d_draw(GF_TraverseState *tr_state, GF_Mesh *mesh)
1965 {
1966 	if (mesh->mesh_type) {
1967 		if (visual_3d_setup_material(tr_state, mesh->mesh_type, NULL)) {
1968 			visual_3d_mesh_paint(tr_state, mesh);
1969 		}
1970 	} else if (visual_3d_setup_appearance(tr_state)) {
1971 		visual_3d_mesh_paint(tr_state, mesh);
1972 		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 		if (tr_state->appear && gf_node_get_tag(tr_state->appear)==TAG_X3D_Appearance) {
1976 			X_Appearance *ap = (X_Appearance *)tr_state->appear;
1977 			X_FillProperties *fp = ap->fillProperties ? (X_FillProperties *) ap->fillProperties : NULL;
1978 			if (fp && fp->hatched) visual_3d_mesh_hatch(tr_state, mesh, fp->hatchStyle, fp->hatchColor);
1979 		}
1980 #endif
1981 	}
1982 }
1983 
visual_3d_projection_matrix_modified(GF_VisualManager * visual)1984 void visual_3d_projection_matrix_modified(GF_VisualManager *visual)
1985 {
1986 	visual->needs_projection_matrix_reload = 1;
1987 }
1988 
visual_3d_enable_headlight(GF_VisualManager * visual,Bool bOn,GF_Camera * cam)1989 void visual_3d_enable_headlight(GF_VisualManager *visual, Bool bOn, GF_Camera *cam)
1990 {
1991 	SFVec3f dir;
1992 	SFColor col;
1993 
1994 	if (!bOn) return;
1995 	//if we have lights in the scene don't turn the headlight on
1996 	if (visual->has_inactive_lights || visual->num_lights) return;
1997 
1998 	col.blue = col.red = col.green = FIX_ONE;
1999 	dir.x = dir.y = 0;
2000 	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 	visual_3d_add_directional_light(visual, 0, col, FIX_ONE, dir, NULL);
2005 }
2006 
visual_3d_set_material_2d(GF_VisualManager * visual,SFColor col,Fixed alpha)2007 void visual_3d_set_material_2d(GF_VisualManager *visual, SFColor col, Fixed alpha)
2008 {
2009 	visual->has_material_2d = alpha ? GF_TRUE : GF_FALSE;
2010 	visual->has_material = 0;
2011 	if (visual->has_material_2d) {
2012 		visual->mat_2d.red = col.red;
2013 		visual->mat_2d.green = col.green;
2014 		visual->mat_2d.blue = col.blue;
2015 		visual->mat_2d.alpha = alpha;
2016 	}
2017 
2018 }
2019 
visual_3d_set_material_2d_argb(GF_VisualManager * visual,u32 col)2020 void visual_3d_set_material_2d_argb(GF_VisualManager *visual, u32 col)
2021 {
2022 	u32 a = GF_COL_A(col);
2023 	visual->has_material_2d = a ? GF_TRUE : GF_FALSE;
2024 	visual->has_material = 0;
2025 	if (visual->has_material_2d) {
2026 		visual->mat_2d.red = INT2FIX( GF_COL_R(col) ) / 255;
2027 		visual->mat_2d.green = INT2FIX( GF_COL_G(col) ) / 255;
2028 		visual->mat_2d.blue = INT2FIX( GF_COL_B(col) ) / 255;
2029 		visual->mat_2d.alpha = INT2FIX( a ) / 255;
2030 	}
2031 }
2032 
visual_3d_set_clipper_2d(GF_VisualManager * visual,GF_Rect clip,GF_Matrix * mx_at_clipper)2033 void visual_3d_set_clipper_2d(GF_VisualManager *visual, GF_Rect clip, GF_Matrix *mx_at_clipper)
2034 {
2035 	if (mx_at_clipper)
2036 		gf_mx_apply_rect(mx_at_clipper, &clip);
2037 	visual->clipper_2d = gf_rect_pixelize(&clip);
2038 	visual->has_clipper_2d = GF_TRUE;
2039 }
2040 
visual_3d_reset_clipper_2d(GF_VisualManager * visual)2041 void visual_3d_reset_clipper_2d(GF_VisualManager *visual)
2042 {
2043 	visual->has_clipper_2d = GF_FALSE;
2044 }
2045 
visual_3d_set_clip_plane(GF_VisualManager * visual,GF_Plane p,GF_Matrix * mx_at_clipper,Bool is_2d_clip)2046 void visual_3d_set_clip_plane(GF_VisualManager *visual, GF_Plane p, GF_Matrix *mx_at_clipper, Bool is_2d_clip)
2047 {
2048 	if (visual->num_clips==GF_MAX_GL_CLIPS) return;
2049 	gf_vec_norm(&p.normal);
2050 	visual->clippers[visual->num_clips].p = p;
2051 	visual->clippers[visual->num_clips].is_2d_clip = is_2d_clip;
2052 	visual->clippers[visual->num_clips].mx_clipper = mx_at_clipper;
2053 	visual->num_clips++;
2054 }
2055 
visual_3d_reset_clip_plane(GF_VisualManager * visual)2056 void visual_3d_reset_clip_plane(GF_VisualManager *visual)
2057 {
2058 	if (!visual->num_clips) return;
2059 	visual->num_clips -= 1;
2060 }
2061 
visual_3d_set_material(GF_VisualManager * visual,u32 material_type,Fixed * rgba)2062 void visual_3d_set_material(GF_VisualManager *visual, u32 material_type, Fixed *rgba)
2063 {
2064 	visual->materials[material_type].red = rgba[0];
2065 	visual->materials[material_type].green = rgba[1];
2066 	visual->materials[material_type].blue = rgba[2];
2067 	visual->materials[material_type].alpha = rgba[3];
2068 
2069 	visual->has_material = 1;
2070 	visual->has_material_2d=0;
2071 }
2072 
visual_3d_set_shininess(GF_VisualManager * visual,Fixed shininess)2073 void visual_3d_set_shininess(GF_VisualManager *visual, Fixed shininess)
2074 {
2075 	visual->shininess = shininess;
2076 }
2077 
visual_3d_set_state(GF_VisualManager * visual,u32 flag_mask,Bool setOn)2078 void visual_3d_set_state(GF_VisualManager *visual, u32 flag_mask, Bool setOn)
2079 {
2080 	if (flag_mask & V3D_STATE_LIGHT) visual->state_light_on = setOn;
2081 	if (flag_mask & V3D_STATE_BLEND) visual->state_blend_on = setOn;
2082 	if (flag_mask & V3D_STATE_COLOR) visual->state_color_on = setOn;
2083 }
2084 
2085 
visual_3d_set_texture_matrix(GF_VisualManager * visual,GF_Matrix * mx)2086 void visual_3d_set_texture_matrix(GF_VisualManager *visual, GF_Matrix *mx)
2087 {
2088 	visual->has_tx_matrix = mx ? 1 : 0;
2089 	if (mx) gf_mx_copy(visual->tx_matrix, *mx);
2090 }
2091 
2092 
visual_3d_has_inactive_light(GF_VisualManager * visual)2093 void visual_3d_has_inactive_light(GF_VisualManager *visual)
2094 {
2095 	visual->has_inactive_lights = GF_TRUE;
2096 }
2097 
visual_3d_add_point_light(GF_VisualManager * visual,Fixed ambientIntensity,SFVec3f attenuation,SFColor color,Fixed intensity,SFVec3f location,GF_Matrix * light_mx)2098 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 	if (visual->num_lights==visual->max_lights) return 0;
2101 	visual->lights[visual->num_lights].type = 2;
2102 	visual->lights[visual->num_lights].ambientIntensity = ambientIntensity;
2103 	visual->lights[visual->num_lights].attenuation = attenuation;
2104 	visual->lights[visual->num_lights].color = color;
2105 	visual->lights[visual->num_lights].intensity = intensity;
2106 	visual->lights[visual->num_lights].position = location;
2107 	memcpy(&visual->lights[visual->num_lights].light_mx, light_mx, sizeof(GF_Matrix) );
2108 	visual->num_lights++;
2109 	return 1;
2110 }
2111 
visual_3d_add_spot_light(GF_VisualManager * visual,Fixed ambientIntensity,SFVec3f attenuation,Fixed beamWidth,SFColor color,Fixed cutOffAngle,SFVec3f direction,Fixed intensity,SFVec3f location,GF_Matrix * light_mx)2112 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 	if (visual->num_lights==visual->max_lights) return 0;
2116 	visual->lights[visual->num_lights].type = 1;
2117 	visual->lights[visual->num_lights].ambientIntensity = ambientIntensity;
2118 	visual->lights[visual->num_lights].attenuation = attenuation;
2119 	visual->lights[visual->num_lights].beamWidth = beamWidth;
2120 	visual->lights[visual->num_lights].cutOffAngle = cutOffAngle;
2121 	visual->lights[visual->num_lights].color = color;
2122 	visual->lights[visual->num_lights].direction = direction;
2123 	visual->lights[visual->num_lights].intensity = intensity;
2124 	visual->lights[visual->num_lights].position = location;
2125 	memcpy(&visual->lights[visual->num_lights].light_mx, light_mx, sizeof(GF_Matrix) );
2126 	visual->num_lights++;
2127 	return 1;
2128 }
2129 
visual_3d_add_directional_light(GF_VisualManager * visual,Fixed ambientIntensity,SFColor color,Fixed intensity,SFVec3f direction,GF_Matrix * light_mx)2130 Bool visual_3d_add_directional_light(GF_VisualManager *visual, Fixed ambientIntensity, SFColor color, Fixed intensity, SFVec3f direction, GF_Matrix *light_mx)
2131 {
2132 	if (visual->num_lights==visual->max_lights) return 0;
2133 	visual->lights[visual->num_lights].type = 0;
2134 	visual->lights[visual->num_lights].ambientIntensity = ambientIntensity;
2135 	visual->lights[visual->num_lights].color = color;
2136 	visual->lights[visual->num_lights].intensity = intensity;
2137 	visual->lights[visual->num_lights].direction = direction;
2138 	if (light_mx) {
2139 		memcpy(&visual->lights[visual->num_lights].light_mx, light_mx, sizeof(GF_Matrix) );
2140 	} else {
2141 		gf_mx_init(visual->lights[visual->num_lights].light_mx);
2142 		visual->lights[visual->num_lights].type = 3;
2143 		visual->lights[visual->num_lights].direction.x = 0;
2144 		visual->lights[visual->num_lights].direction.y = 0;
2145 		visual->lights[visual->num_lights].direction.z = -FIX_ONE;
2146 	}
2147 
2148 	visual->num_lights++;
2149 	return 1;
2150 }
2151 
2152 
visual_3d_remove_last_light(GF_VisualManager * visual)2153 void visual_3d_remove_last_light(GF_VisualManager *visual)
2154 {
2155 	if (visual->num_lights) {
2156 		visual->num_lights--;
2157 	}
2158 }
2159 
visual_3d_clear_all_lights(GF_VisualManager * visual)2160 void visual_3d_clear_all_lights(GF_VisualManager *visual)
2161 {
2162 	visual->num_lights = 0;
2163 	visual->has_inactive_lights = GF_FALSE;
2164 }
2165 
visual_3d_set_fog(GF_VisualManager * visual,const char * type,SFColor color,Fixed density,Fixed visibility)2166 void visual_3d_set_fog(GF_VisualManager *visual, const char *type, SFColor color, Fixed density, Fixed visibility)
2167 {
2168 	visual->has_fog = GF_TRUE;
2169 	if (!type || !stricmp(type, "LINEAR")) visual->fog_type = 0;
2170 	else if (!stricmp(type, "EXPONENTIAL")) visual->fog_type = 1;
2171 	else if (!stricmp(type, "EXPONENTIAL2")) visual->fog_type = 2;
2172 
2173 	visual->fog_color = color;
2174 	visual->fog_density = density;
2175 	visual->fog_visibility = visibility;
2176 }
2177 
2178 #endif
2179 
2180