LCOV - code coverage report
Current view: top level - compositor - visual_manager_3d.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 859 1058 81.2 %
Date: 2021-04-29 23:48:07 Functions: 47 50 94.0 %

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

Generated by: LCOV version 1.13