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