static Bool NLD_GetMatrix(M_NonLinearDeformer *nld, GF_Matrix *mx) { SFVec3f v1, v2; SFRotation r; Fixed l1, l2, dot; /*compute rotation matrix from NLD axis to 0 0 1*/ v1 = nld->axis; gf_vec_norm(&v1); v2.x = v2.y = 0; v2.z = FIX_ONE; if (gf_vec_equal(v1, v2)) return 0; l1 = gf_vec_len(v1); l2 = gf_vec_len(v2); dot = gf_divfix(gf_vec_dot(v1, v2), gf_mulfix(l1, l2)); r.x = gf_mulfix(v1.y, v2.z) - gf_mulfix(v2.y, v1.z); r.y = gf_mulfix(v1.z, v2.x) - gf_mulfix(v2.z, v1.x); r.z = gf_mulfix(v1.x, v2.y) - gf_mulfix(v2.x, v1.y); r.q = gf_atan2(gf_sqrt(FIX_ONE - gf_mulfix(dot, dot)), dot); gf_mx_init(*mx); gf_mx_add_rotation(mx, r.q, r.x, r.y, r.z); return 1; }
static void camera_frustum_from_matrix(GF_Camera *cam, GF_Matrix *mx) { u32 i; cam->planes[FRUS_LEFT_PLANE].normal.x = mx->m[3] + mx->m[0]; cam->planes[FRUS_LEFT_PLANE].normal.y = mx->m[7] + mx->m[4]; cam->planes[FRUS_LEFT_PLANE].normal.z = mx->m[11] + mx->m[8]; cam->planes[FRUS_LEFT_PLANE].d = mx->m[15] + mx->m[12]; cam->planes[FRUS_RIGHT_PLANE].normal.x = mx->m[3] - mx->m[0]; cam->planes[FRUS_RIGHT_PLANE].normal.y = mx->m[7] - mx->m[4]; cam->planes[FRUS_RIGHT_PLANE].normal.z = mx->m[11] - mx->m[8]; cam->planes[FRUS_RIGHT_PLANE].d = mx->m[15] - mx->m[12]; cam->planes[FRUS_BOTTOM_PLANE].normal.x = mx->m[3] + mx->m[1]; cam->planes[FRUS_BOTTOM_PLANE].normal.y = mx->m[7] + mx->m[5]; cam->planes[FRUS_BOTTOM_PLANE].normal.z = mx->m[11] + mx->m[9]; cam->planes[FRUS_BOTTOM_PLANE].d = mx->m[15] + mx->m[13]; cam->planes[FRUS_TOP_PLANE].normal.x = mx->m[3] - mx->m[1]; cam->planes[FRUS_TOP_PLANE].normal.y = mx->m[7] - mx->m[5]; cam->planes[FRUS_TOP_PLANE].normal.z = mx->m[11] - mx->m[9]; cam->planes[FRUS_TOP_PLANE].d = mx->m[15] - mx->m[13]; cam->planes[FRUS_FAR_PLANE].normal.x = mx->m[3] - mx->m[2]; cam->planes[FRUS_FAR_PLANE].normal.y = mx->m[7] - mx->m[6]; cam->planes[FRUS_FAR_PLANE].normal.z = mx->m[11] - mx->m[10]; cam->planes[FRUS_FAR_PLANE].d = mx->m[15] - mx->m[14]; cam->planes[FRUS_NEAR_PLANE].normal.x = mx->m[3] + mx->m[2]; cam->planes[FRUS_NEAR_PLANE].normal.y = mx->m[7] + mx->m[6]; cam->planes[FRUS_NEAR_PLANE].normal.z = mx->m[11] + mx->m[10]; cam->planes[FRUS_NEAR_PLANE].d = mx->m[15] + mx->m[14]; for (i=0; i<6; ++i) { #ifdef GPAC_FIXED_POINT /*after some testing, it's just safer to move back to float here, the smallest drift will result in completely wrong culling...*/ Float vx, vy, vz, nor; vx = FIX2FLT(cam->planes[i].normal.x); vy = FIX2FLT(cam->planes[i].normal.y); vz = FIX2FLT(cam->planes[i].normal.z); nor = (Float) sqrt(vx*vx + vy*vy + vz*vz); vx /= nor; vy /= nor; vz /= nor; cam->planes[i].d = FLT2FIX (FIX2FLT(cam->planes[i].d) / nor); cam->planes[i].normal.x = FLT2FIX(vx); cam->planes[i].normal.y = FLT2FIX(vy); cam->planes[i].normal.z = FLT2FIX(vz); #else Float len = (Float)(1.0f / gf_vec_len(cam->planes[i].normal)); cam->planes[i].normal = gf_vec_scale(cam->planes[i].normal, len); cam->planes[i].d *= len; #endif /*compute p-vertex idx*/ cam->p_idx[i] = gf_plane_get_p_vertex_idx(&cam->planes[i]); } }
static void TraverseFog(GF_Node *node, void *rs, Bool is_destroy) { Fixed density, vrange; SFVec3f start, end; ViewStack *vp_st; M_Viewpoint *vp; GF_TraverseState *tr_state = (GF_TraverseState *)rs; M_Fog *fog = (M_Fog *) node; ViewStack *st = (ViewStack *) gf_node_get_private(node); if (is_destroy) { DestroyViewStack(node); return; } if (!tr_state->fogs) return; /*first traverse, bound if needed*/ if (gf_list_find(tr_state->fogs, node) < 0) { gf_list_add(tr_state->fogs, node); if (gf_list_get(tr_state->fogs, 0) == fog) { if (!fog->isBound) Bindable_SetIsBound(node, 1); } assert(gf_list_find(st->reg_stacks, tr_state->fogs)==-1); gf_list_add(st->reg_stacks, tr_state->fogs); gf_mx_copy(st->world_view_mx, tr_state->model_matrix); /*in any case don't draw the first time*/ gf_sc_invalidate(tr_state->visual->compositor, NULL); return; } /*not evaluating, return*/ if (tr_state->traversing_mode != TRAVERSE_BINDABLE) { if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) gf_mx_copy(st->world_view_mx, tr_state->model_matrix); return; } /*not bound*/ if (!fog->isBound || !fog->visibilityRange) return; /*fog visibility is expressed in current bound VP so get its matrix*/ vp = (M_Viewpoint*)gf_list_get(tr_state->viewpoints, 0); vp_st = NULL; if (vp && vp->isBound) vp_st = (ViewStack *) gf_node_get_private((GF_Node *)vp); start.x = start.y = start.z = 0; end.x = end.y = 0; end.z = fog->visibilityRange; if (vp_st) { gf_mx_apply_vec(&vp_st->world_view_mx, &start); gf_mx_apply_vec(&vp_st->world_view_mx, &end); } gf_mx_apply_vec(&st->world_view_mx, &start); gf_mx_apply_vec(&st->world_view_mx, &end); gf_vec_diff(end, end, start); vrange = gf_vec_len(end); density = gf_invfix(vrange); visual_3d_set_fog(tr_state->visual, fog->fogType.buffer, fog->color, density, vrange); }
static void TraverseNavigationInfo(GF_Node *node, void *rs, Bool is_destroy) { u32 i; #ifndef GPAC_DISABLE_3D u32 nb_select_mode; SFVec3f start, end; Fixed scale; ViewStack *st = (ViewStack *) gf_node_get_private(node); #endif GF_TraverseState *tr_state = (GF_TraverseState *)rs; M_NavigationInfo *ni = (M_NavigationInfo *) node; if (is_destroy) { DestroyViewStack(node); return; } #ifdef GPAC_DISABLE_3D /*FIXME, we only deal with one node, no bind stack for the current time*/ for (i=0; i<ni->type.count; i++) { if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "NONE")) { tr_state->visual->compositor->navigation_disabled = 1; } } #else if (!tr_state->navigations) return; /*first traverse, bound if needed*/ if (gf_list_find(tr_state->navigations, node) < 0) { gf_list_add(tr_state->navigations, node); if (gf_list_get(tr_state->navigations, 0) == ni) { if (!ni->isBound) Bindable_SetIsBound(node, 1); } assert(gf_list_find(st->reg_stacks, tr_state->navigations)==-1); gf_list_add(st->reg_stacks, tr_state->navigations); gf_mx_copy(st->world_view_mx, tr_state->model_matrix); /*in any case don't draw the first time*/ gf_sc_invalidate(tr_state->visual->compositor, NULL); return; } /*not bound*/ if (!ni->isBound) return; /*not evaluating, return*/ if (tr_state->traversing_mode != TRAVERSE_BINDABLE) { if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) { if (!gf_mx_equal(&st->world_view_mx, &tr_state->model_matrix)) { gf_mx_copy(st->world_view_mx, tr_state->model_matrix); gf_node_dirty_set(node, 0, 0); } } return; } if (!gf_node_dirty_get(node)) return; gf_node_dirty_clear(node, 0); nb_select_mode = 0; tr_state->camera->navigation_flags = 0; tr_state->camera->navigate_mode = 0; for (i=0; i<ni->type.count; i++) { if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "ANY")) tr_state->camera->navigation_flags |= NAV_ANY; else { nb_select_mode++; } if (!tr_state->camera->navigate_mode) { if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "NONE")) tr_state->camera->navigate_mode = GF_NAVIGATE_NONE; else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "WALK")) tr_state->camera->navigate_mode = GF_NAVIGATE_WALK; else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "EXAMINE")) tr_state->camera->navigate_mode = GF_NAVIGATE_EXAMINE; else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "FLY")) tr_state->camera->navigate_mode = GF_NAVIGATE_FLY; else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "VR")) tr_state->camera->navigate_mode = GF_NAVIGATE_VR; } } if (nb_select_mode>1) tr_state->camera->navigation_flags |= NAV_SELECTABLE; if (ni->headlight) tr_state->camera->navigation_flags |= NAV_HEADLIGHT; start.x = start.y = start.z = 0; end.x = end.y = 0; end.z = FIX_ONE; gf_mx_apply_vec(&st->world_view_mx, &start); gf_mx_apply_vec(&st->world_view_mx, &end); gf_vec_diff(end, end, start); scale = gf_vec_len(end); tr_state->camera->speed = gf_mulfix(scale, ni->speed); tr_state->camera->visibility = gf_mulfix(scale, ni->visibilityLimit); if (ni->avatarSize.count) tr_state->camera->avatar_size.x = gf_mulfix(scale, ni->avatarSize.vals[0]); if (ni->avatarSize.count>1) tr_state->camera->avatar_size.y = gf_mulfix(scale, ni->avatarSize.vals[1]); if (ni->avatarSize.count>2) tr_state->camera->avatar_size.z = gf_mulfix(scale, ni->avatarSize.vals[2]); if (0 && tr_state->pixel_metrics) { u32 s = MAX(tr_state->visual->width, tr_state->visual->height); s /= 2; // tr_state->camera->speed = ni->speed; tr_state->camera->visibility *= s; tr_state->camera->avatar_size.x *= s; tr_state->camera->avatar_size.y *= s; tr_state->camera->avatar_size.z *= s; } #endif }
static void TraverseLOD(GF_Node *node, void *rs, Bool is_destroy) { GF_ChildNodeItem *children; MFFloat *ranges; SFVec3f pos, usr; u32 which_child, nb_children; Fixed dist; Bool do_all; GF_Matrix mx; SFVec3f center; GF_TraverseState *tr_state = (GF_TraverseState *)rs; s32 *prev_child = (s32 *)gf_node_get_private(node); if (is_destroy) { gf_free(prev_child); gf_sc_check_focus_upon_destroy(node); return; } /*WARNING: X3D/MPEG4 NOT COMPATIBLE*/ if (gf_node_get_tag(node) == TAG_MPEG4_LOD) { children = ((M_LOD *) node)->level; ranges = &((M_LOD *) node)->range; center = ((M_LOD *) node)->center; #ifndef GPAC_DISABLE_X3D } else { children = ((X_LOD *) node)->children; ranges = &((X_LOD *) node)->range; center = ((X_LOD *) node)->center; #endif } if (!children) return; nb_children = gf_node_list_get_count(children); if (!tr_state->camera) { do_all = 1; which_child = 0; } else { /*can't cache the matrix here*/ usr = tr_state->camera->position; pos = center; gf_mx_copy(mx, tr_state->model_matrix); gf_mx_inverse(&mx); gf_mx_apply_vec(&mx, &usr); gf_vec_diff(pos, pos, usr); dist = gf_vec_len(pos); for (which_child=0; which_child<ranges->count; which_child++) { if (dist<ranges->vals[which_child]) break; } if (which_child>=nb_children) which_child = nb_children-1; /*check if we're traversing the same child or not for audio rendering*/ do_all = 0; if (gf_node_dirty_get(node)) { gf_node_dirty_clear(node, 0); do_all = 1; } else if ((s32) which_child != *prev_child) { *prev_child = which_child; do_all = 1; } } if (do_all) { u32 i; Bool prev_switch = tr_state->switched_off; GF_ChildNodeItem *l = children; tr_state->switched_off = 1; i=0; while (l) { if (i!=which_child) gf_node_traverse(l->node, rs); l = l->next; } tr_state->switched_off = prev_switch; } gf_node_traverse(gf_node_list_get_child(children, which_child), rs); }
static void TraverseBillboard(GF_Node *n, void *rs, Bool is_destroy) { GF_Matrix gf_mx_bckup; TransformStack *st = (TransformStack *)gf_node_get_private(n); M_Billboard *bb = (M_Billboard *)n; GF_TraverseState *tr_state = (GF_TraverseState *)rs; if (is_destroy) { DestroyTransform(n); return; } if (! tr_state->camera) return; /*can't cache the matrix here*/ gf_mx_init(st->mx); if (tr_state->camera->is_3D) { SFVec3f z, axis; Fixed axis_len; SFVec3f user_pos = tr_state->camera->position; gf_mx_apply_vec(&tr_state->model_matrix, &user_pos); gf_vec_norm(&user_pos); axis = bb->axisOfRotation; axis_len = gf_vec_len(axis); if (axis_len<FIX_EPSILON) { SFVec3f x, y, t; /*get user's right in local coord*/ gf_vec_diff(t, tr_state->camera->position, tr_state->camera->target); gf_vec_norm(&t); x = gf_vec_cross(tr_state->camera->up, t); gf_vec_norm(&x); gf_mx_rotate_vector(&tr_state->model_matrix, &x); gf_vec_norm(&x); /*get user's up in local coord*/ y = tr_state->camera->up; gf_mx_rotate_vector(&tr_state->model_matrix, &y); gf_vec_norm(&y); z = gf_vec_cross(x, y); gf_vec_norm(&z); gf_mx_rotation_matrix_from_vectors(&st->mx, x, y, z); gf_mx_inverse(&st->mx); } else { SFVec3f tmp; Fixed d, cosw, sinw, angle; gf_vec_norm(&axis); /*map eye & z into plane with normal axis through 0.0*/ d = -gf_vec_dot(axis, user_pos); tmp = gf_vec_scale(axis, d); gf_vec_add(user_pos, user_pos, tmp); gf_vec_norm(&user_pos); z.x = z.y = 0; z.z = FIX_ONE; d = -gf_vec_dot(axis, z); tmp = gf_vec_scale(axis, d); gf_vec_add(z, z, tmp); gf_vec_norm(&z); cosw = gf_vec_dot(user_pos, z); tmp = gf_vec_cross(user_pos, z); sinw = gf_vec_len(tmp); angle = gf_acos(cosw); gf_vec_norm(&tmp); if ((sinw>0) && (gf_vec_dot(axis, tmp) > 0)) gf_vec_rev(axis); gf_mx_add_rotation(&st->mx, angle, axis.x, axis.y, axis.z); } } gf_mx_copy(gf_mx_bckup, tr_state->model_matrix); gf_mx_add_matrix(&tr_state->model_matrix, &st->mx); /*note we don't clear dirty flag, this is done in traversing*/ group_3d_traverse(n, (GroupingNode *) st, tr_state); gf_mx_copy(tr_state->model_matrix, gf_mx_bckup); if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) gf_mx_apply_bbox(&st->mx, &tr_state->bbox); }
Bool gf_sc_fit_world_to_screen(GF_Compositor *compositor) { GF_TraverseState tr_state; SFVec3f pos, diff; Fixed dist, d; GF_Camera *cam; GF_Node *top; #ifndef GPAC_DISABLE_VRML // if (gf_list_count(compositor->visual->back_stack)) return; if (gf_list_count(compositor->visual->view_stack)) return 0; #endif gf_mx_p(compositor->mx); top = gf_sg_get_root_node(compositor->scene); if (!top) { gf_mx_v(compositor->mx); return 0; } memset(&tr_state, 0, sizeof(GF_TraverseState)); gf_mx_init(tr_state.model_matrix); tr_state.traversing_mode = TRAVERSE_GET_BOUNDS; tr_state.visual = compositor->visual; gf_node_traverse(top, &tr_state); if (gf_node_dirty_get(top)) { tr_state.bbox.is_set = 0; } if (!tr_state.bbox.is_set) { gf_mx_v(compositor->mx); /*empty world ...*/ if (tr_state.bbox.radius==-1) return 1; /*2D world with 3D camera forced*/ if (tr_state.bounds.width&&tr_state.bounds.height) return 1; return 0; } cam = &compositor->visual->camera; cam->world_bbox = tr_state.bbox; /*fit is based on bounding sphere*/ dist = gf_divfix(tr_state.bbox.radius, gf_sin(cam->fieldOfView/2) ); gf_vec_diff(diff, cam->center, tr_state.bbox.center); /*do not update if camera is outside the scene bounding sphere and dist is too close*/ if (gf_vec_len(diff) > tr_state.bbox.radius + cam->radius) { gf_vec_diff(diff, cam->vp_position, tr_state.bbox.center); d = gf_vec_len(diff); if (d<dist) { gf_mx_v(compositor->mx); return 1; } } diff = gf_vec_scale(camera_get_pos_dir(cam), dist); gf_vec_add(pos, tr_state.bbox.center, diff); diff = cam->position; camera_set_vectors(cam, pos, cam->vp_orientation, cam->fieldOfView); cam->position = diff; camera_move_to(cam, pos, cam->target, cam->up); cam->examine_center = tr_state.bbox.center; cam->flags |= CF_STORE_VP; if (cam->z_far < dist) cam->z_far = 10*dist; camera_changed(compositor, cam); gf_mx_v(compositor->mx); return 1; }
static void TraversePlanarExtrusion(GF_Node *node, void *rs, Bool is_destroy) { PlanarExtrusion plane_ext; Drawable *stack_2d; u32 i, j, k; MFVec3f spine_vec; SFVec3f d; Fixed spine_len; GF_Rect bounds; GF_Path *geo, *spine; GF_TraverseState *tr_state = (GF_TraverseState *)rs; Drawable3D *stack = (Drawable3D *)gf_node_get_private(node); if (is_destroy) { drawable_3d_del(node); return; } if (!PlanarExtrusion_GetNode(node, &plane_ext)) return; if (!plane_ext.geometry || !plane_ext.spine) return; if (gf_node_dirty_get(node)) { u32 cur, nb_pts; u32 mode = tr_state->traversing_mode; geo = spine = NULL; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; gf_node_traverse(plane_ext.geometry, tr_state); gf_node_traverse(plane_ext.spine, tr_state); tr_state->traversing_mode = mode; switch (gf_node_get_tag(plane_ext.geometry) ) { case TAG_MPEG4_Circle: case TAG_MPEG4_Ellipse: case TAG_MPEG4_Rectangle: case TAG_MPEG4_Curve2D: case TAG_MPEG4_XCurve2D: case TAG_MPEG4_IndexedFaceSet2D: case TAG_MPEG4_IndexedLineSet2D: stack_2d = (Drawable*)gf_node_get_private(plane_ext.geometry); if (stack_2d) geo = stack_2d->path; break; default: return; } switch (gf_node_get_tag(plane_ext.spine) ) { case TAG_MPEG4_Circle: case TAG_MPEG4_Ellipse: case TAG_MPEG4_Rectangle: case TAG_MPEG4_Curve2D: case TAG_MPEG4_XCurve2D: case TAG_MPEG4_IndexedFaceSet2D: case TAG_MPEG4_IndexedLineSet2D: stack_2d = (Drawable*)gf_node_get_private(plane_ext.spine); if (stack_2d) spine = stack_2d->path; break; default: return; } if (!geo || !spine) return; mesh_reset(stack->mesh); gf_path_flatten(spine); gf_path_get_bounds(spine, &bounds); gf_path_flatten(geo); gf_path_get_bounds(geo, &bounds); cur = 0; for (i=0; i<spine->n_contours; i++) { nb_pts = 1 + spine->contours[i] - cur; spine_vec.vals = NULL; gf_sg_vrml_mf_alloc(&spine_vec, GF_SG_VRML_MFVEC3F, nb_pts); spine_len = 0; for (j=cur; j<nb_pts; j++) { spine_vec.vals[j].x = spine->points[j].x; spine_vec.vals[j].y = spine->points[j].y; spine_vec.vals[j].z = 0; if (j) { gf_vec_diff(d, spine_vec.vals[j], spine_vec.vals[j-1]); spine_len += gf_vec_len(d); } } cur += nb_pts; if (!plane_ext.orientation->count && !plane_ext.scale->count) { mesh_extrude_path_ext(stack->mesh, geo, &spine_vec, plane_ext.creaseAngle, bounds.x, bounds.y-bounds.height, bounds.width, bounds.height, plane_ext.beginCap, plane_ext.endCap, NULL, NULL, plane_ext.txAlongSpine); } /*interpolate orientation and scale along subpath line*/ else { MFRotation ori; MFVec2f scale; Fixed cur_len, frac; ori.vals = NULL; gf_sg_vrml_mf_alloc(&ori, GF_SG_VRML_MFROTATION, nb_pts); scale.vals = NULL; gf_sg_vrml_mf_alloc(&scale, GF_SG_VRML_MFVEC2F, nb_pts); cur_len = 0; if (!plane_ext.orientation->count) ori.vals[0].y = FIX_ONE; if (!plane_ext.scale->count) scale.vals[0].x = scale.vals[0].y = FIX_ONE; for (j=0; j<nb_pts; j++) { if (j) { gf_vec_diff(d, spine_vec.vals[j], spine_vec.vals[j-1]); cur_len += gf_vec_len(d); ori.vals[j] = ori.vals[j-1]; scale.vals[j] = scale.vals[j-1]; } if (plane_ext.orientation->count && (plane_ext.orientation->count == plane_ext.orientationKeys->count)) { frac = gf_divfix(cur_len , spine_len); if (frac < plane_ext.orientationKeys->vals[0]) ori.vals[j] = plane_ext.orientation->vals[0]; else if (frac >= plane_ext.orientationKeys->vals[plane_ext.orientationKeys->count-1]) ori.vals[j] = plane_ext.orientation->vals[plane_ext.orientationKeys->count-1]; else { for (k=1; k<plane_ext.orientationKeys->count; k++) { Fixed kDiff = plane_ext.orientationKeys->vals[k] - plane_ext.orientationKeys->vals[k-1]; if (!kDiff) continue; if (frac < plane_ext.orientationKeys->vals[k-1]) continue; if (frac > plane_ext.orientationKeys->vals[k]) continue; frac = gf_divfix(frac - plane_ext.orientationKeys->vals[k-1], kDiff); break; } ori.vals[j] = gf_sg_sfrotation_interpolate(plane_ext.orientation->vals[k-1], plane_ext.orientation->vals[k], frac); } } if (plane_ext.scale->count == plane_ext.scaleKeys->count) { frac = gf_divfix(cur_len , spine_len); if (frac <= plane_ext.scaleKeys->vals[0]) scale.vals[j] = plane_ext.scale->vals[0]; else if (frac >= plane_ext.scaleKeys->vals[plane_ext.scaleKeys->count-1]) scale.vals[j] = plane_ext.scale->vals[plane_ext.scale->count-1]; else { for (k=1; k<plane_ext.scaleKeys->count; k++) { Fixed kDiff = plane_ext.scaleKeys->vals[k] - plane_ext.scaleKeys->vals[k-1]; if (!kDiff) continue; if (frac < plane_ext.scaleKeys->vals[k-1]) continue; if (frac > plane_ext.scaleKeys->vals[k]) continue; frac = gf_divfix(frac - plane_ext.scaleKeys->vals[k-1], kDiff); break; } scale.vals[j].x = gf_mulfix(plane_ext.scale->vals[k].x - plane_ext.scale->vals[k-1].x, frac) + plane_ext.scale->vals[k-1].x; scale.vals[j].y = gf_mulfix(plane_ext.scale->vals[k].y - plane_ext.scale->vals[k-1].y, frac) + plane_ext.scale->vals[k-1].y; } } } mesh_extrude_path_ext(stack->mesh, geo, &spine_vec, plane_ext.creaseAngle, bounds.x, bounds.y-bounds.height, bounds.width, bounds.height, plane_ext.beginCap, plane_ext.endCap, &ori, &scale, plane_ext.txAlongSpine); gf_sg_vrml_mf_reset(&ori, GF_SG_VRML_MFROTATION); gf_sg_vrml_mf_reset(&scale, GF_SG_VRML_MFVEC2F); } gf_sg_vrml_mf_reset(&spine_vec, GF_SG_VRML_MFVEC3F); } mesh_update_bounds(stack->mesh); gf_mesh_build_aabbtree(stack->mesh); } if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) { visual_3d_draw(tr_state, stack->mesh); } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { tr_state->bbox = stack->mesh->bounds; } }
void camera_update_stereo(GF_Camera *cam, GF_Matrix2D *user_transform, Bool center_coords, Fixed horizontal_shift, Fixed nominal_view_distance, Fixed view_distance_offset, u32 camera_layout) { Fixed vlen, h, w, ar; SFVec3f corner, center; GF_Matrix post_model_view; if (! (cam->flags & CAM_IS_DIRTY)) return; ar = gf_divfix(cam->width, cam->height); gf_mx_init(post_model_view); if (cam->is_3D) { /*setup perspective*/ if (camera_layout==GF_3D_CAMERA_OFFAXIS) { Fixed left, right, top, bottom, shift, wd2, ndfl, viewing_distance; SFVec3f eye, pos, tar, disp; viewing_distance = nominal_view_distance; wd2 = gf_mulfix(cam->z_near, gf_tan(cam->fieldOfView/2)); ndfl = gf_divfix(cam->z_near, viewing_distance); /*compute h displacement*/ shift = gf_mulfix(horizontal_shift, ndfl); top = wd2; bottom = -top; left = -gf_mulfix(ar, wd2) - shift; right = gf_mulfix(ar, wd2) - shift; gf_mx_init(cam->projection); cam->projection.m[0] = gf_divfix(2*cam->z_near, (right-left)); cam->projection.m[5] = gf_divfix(2*cam->z_near, (top-bottom)); cam->projection.m[8] = gf_divfix(right+left, right-left); cam->projection.m[9] = gf_divfix(top+bottom, top-bottom); cam->projection.m[10] = gf_divfix(cam->z_far+cam->z_near, cam->z_near-cam->z_far); cam->projection.m[11] = -FIX_ONE; cam->projection.m[14] = 2*gf_muldiv(cam->z_near, cam->z_far, cam->z_near-cam->z_far); cam->projection.m[15] = 0; gf_vec_diff(eye, cam->target, cam->position); gf_vec_norm(&eye); disp = gf_vec_cross(eye, cam->up); gf_vec_norm(&disp); gf_vec_diff(center, cam->world_bbox.center, cam->position); vlen = gf_vec_len(center); shift = gf_mulfix(horizontal_shift, gf_divfix(vlen, viewing_distance)); pos = gf_vec_scale(disp, shift); gf_vec_add(pos, pos, cam->position); gf_vec_add(tar, pos, eye); /*setup modelview*/ gf_mx_lookat(&cam->modelview, pos, tar, cam->up); } else { gf_mx_perspective(&cam->projection, cam->fieldOfView, ar, cam->z_near, cam->z_far); /*setup modelview*/ gf_mx_lookat(&cam->modelview, cam->position, cam->target, cam->up); } if (!center_coords) { gf_mx_add_scale(&post_model_view, FIX_ONE, -FIX_ONE, FIX_ONE); gf_mx_add_translation(&post_model_view, -cam->width / 2, -cam->height / 2, 0); } /*compute center and radius - CHECK ME!*/ vlen = cam->z_far - cam->z_near; h = gf_mulfix(vlen , gf_tan(cam->fieldOfView / 2)); w = gf_mulfix(h, ar); center.x = 0; center.y = 0; center.z = cam->z_near + vlen / 2; corner.x = w; corner.y = h; corner.z = vlen; gf_vec_diff(corner, corner, center); cam->radius = gf_vec_len(corner); gf_vec_diff(cam->center, cam->target, cam->position); gf_vec_norm(&cam->center); cam->center = gf_vec_scale(cam->center, cam->z_near + vlen/2); gf_vec_add(cam->center, cam->center, cam->position); } else { GF_BBox b; Fixed hw, hh; hw = cam->width / 2; hh = cam->height / 2; cam->z_near = INT2FIX(NEAR_PLANE_2D); cam->z_far = INT2FIX(FAR_PLANE_2D); /*setup ortho*/ gf_mx_ortho(&cam->projection, -hw, hw, -hh, hh, cam->z_near, cam->z_far); /*setup modelview*/ gf_mx_init(cam->modelview); #ifdef FORCE_CAMERA_3D if (! (cam->flags & CAM_NO_LOOKAT)) gf_mx_lookat(&cam->modelview, cam->position, cam->target, cam->up); #endif if (!center_coords) { gf_mx_add_scale(&post_model_view, FIX_ONE, -FIX_ONE, FIX_ONE); gf_mx_add_translation(&post_model_view, -hw, -hh, 0); } if (user_transform) { #ifdef FORCE_CAMERA_3D if (! (cam->flags & CAM_NO_LOOKAT)) { GF_Matrix mx; gf_mx_from_mx2d(&mx, user_transform); mx.m[10] = mx.m[0]; gf_mx_add_matrix(&post_model_view, &mx); } else #endif gf_mx_add_matrix_2d(&post_model_view, user_transform); } if (cam->end_zoom != FIX_ONE) gf_mx_add_scale(&post_model_view, cam->end_zoom, cam->end_zoom, cam->end_zoom); if (cam->flags & CAM_HAS_VIEWPORT) gf_mx_add_matrix(&post_model_view, &cam->viewport); /*compute center & radius*/ b.max_edge.x = hw; b.max_edge.y = hh; b.min_edge.x = -hw; b.min_edge.y = -hh; b.min_edge.z = b.max_edge.z = (cam->z_near+cam->z_far) / 2; gf_bbox_refresh(&b); cam->center = b.center; cam->radius = b.radius; if (camera_layout==GF_3D_CAMERA_OFFAXIS) camera_layout=GF_3D_CAMERA_LINEAR; } if (camera_layout == GF_3D_CAMERA_CIRCULAR) { GF_Matrix mx; Fixed viewing_distance = nominal_view_distance; SFVec3f pos, target; Fixed angle; gf_vec_diff(center, cam->world_bbox.center, cam->position); vlen = gf_vec_len(center); vlen += gf_mulfix(view_distance_offset, gf_divfix(vlen, nominal_view_distance)); gf_vec_diff(pos, cam->target, cam->position); gf_vec_norm(&pos); pos = gf_vec_scale(pos, vlen); gf_vec_add(target, pos, cam->position); gf_mx_init(mx); gf_mx_add_translation(&mx, target.x, target.y, target.z); angle = gf_atan2(horizontal_shift, viewing_distance); gf_mx_add_rotation(&mx, angle, cam->up.x, cam->up.y, cam->up.z); gf_mx_add_translation(&mx, -target.x, -target.y, -target.z); pos = cam->position; gf_mx_apply_vec(&mx, &pos); gf_mx_lookat(&cam->modelview, pos, target, cam->up); } else if (camera_layout == GF_3D_CAMERA_LINEAR) { Fixed viewing_distance = nominal_view_distance + view_distance_offset; GF_Vec eye, disp, pos, tar; gf_vec_diff(center, cam->world_bbox.center, cam->position); vlen = gf_vec_len(center); vlen += gf_mulfix(view_distance_offset, gf_divfix(vlen, nominal_view_distance)); gf_vec_diff(eye, cam->target, cam->position); gf_vec_norm(&eye); tar = gf_vec_scale(eye, vlen); gf_vec_add(tar, tar, cam->position); disp = gf_vec_cross(eye, cam->up); gf_vec_norm(&disp); disp= gf_vec_scale(disp, gf_divfix(gf_mulfix(vlen, horizontal_shift), viewing_distance)); gf_vec_add(pos, cam->position, disp); gf_mx_lookat(&cam->modelview, pos, tar, cam->up); } gf_mx_add_matrix(&cam->modelview, &post_model_view); /*compute frustum planes*/ gf_mx_copy(cam->unprojection, cam->projection); gf_mx_add_matrix_4x4(&cam->unprojection, &cam->modelview); camera_frustum_from_matrix(cam, &cam->unprojection); /*also compute reverse PM for unprojections*/ gf_mx_inverse_4x4(&cam->unprojection); cam->flags &= ~CAM_IS_DIRTY; }
static void TraverseSound(GF_Node *node, void *rs, Bool is_destroy) { GF_TraverseState *tr_state = (GF_TraverseState*) rs; M_Sound *snd = (M_Sound *)node; SoundStack *st = (SoundStack *)gf_node_get_private(node); if (is_destroy) { gf_free(st); return; } if (!snd->source) return; tr_state->sound_holder = &st->snd_ifce; /*forward in case we're switched off*/ if (tr_state->switched_off) { gf_node_traverse((GF_Node *) snd->source, tr_state); } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { /*we can't cull sound since*/ tr_state->disable_cull = 1; } else if (tr_state->traversing_mode==TRAVERSE_SORT) { GF_Matrix mx; SFVec3f usr, snd_dir, pos; Fixed mag, ang; /*this implies no DEF/USE for real location...*/ gf_mx_copy(st->mx, tr_state->model_matrix); gf_mx_copy(mx, tr_state->model_matrix); gf_mx_inverse(&mx); snd_dir = snd->direction; gf_vec_norm(&snd_dir); /*get user location*/ usr = tr_state->camera->position; gf_mx_apply_vec(&mx, &usr); /*recenter to ellipse focal*/ gf_vec_diff(usr, usr, snd->location); mag = gf_vec_len(usr); if (!mag) mag = FIX_ONE/10; ang = gf_divfix(gf_vec_dot(snd_dir, usr), mag); usr.z = gf_mulfix(ang, mag); usr.x = gf_sqrt(gf_mulfix(mag, mag) - gf_mulfix(usr.z, usr.z)); usr.y = 0; if (!gf_vec_equal(usr, st->last_pos)) { st->intensity = snd_compute_gain(snd->minBack, snd->minFront, snd->maxBack, snd->maxFront, usr); st->intensity = gf_mulfix(st->intensity, snd->intensity); st->last_pos = usr; } st->identity = (st->intensity==FIX_ONE) ? 1 : 0; if (snd->spatialize) { Fixed ang, sign; SFVec3f cross; pos = snd->location; gf_mx_apply_vec(&tr_state->model_matrix, &pos); gf_vec_diff(pos, pos, tr_state->camera->position); gf_vec_diff(usr, tr_state->camera->target, tr_state->camera->position); gf_vec_norm(&pos); gf_vec_norm(&usr); ang = gf_acos(gf_vec_dot(usr, pos)); /*get orientation*/ cross = gf_vec_cross(usr, pos); sign = gf_vec_dot(cross, tr_state->camera->up); if (sign>0) ang *= -1; ang = (FIX_ONE + gf_sin(ang)) / 2; st->lgain = (FIX_ONE - gf_mulfix(ang, ang)); st->rgain = FIX_ONE - gf_mulfix(FIX_ONE - ang, FIX_ONE - ang); /*renorm between 0 and 1*/ st->lgain = gf_mulfix(st->lgain, 4*st->intensity/3); st->rgain = gf_mulfix(st->rgain, 4*st->intensity/3); if (st->identity && ((st->lgain!=FIX_ONE) || (st->rgain!=FIX_ONE))) st->identity = 0; } else { st->lgain = st->rgain = FIX_ONE; } gf_node_traverse((GF_Node *) snd->source, tr_state); } tr_state->sound_holder = NULL; }
static Bool OnCylinderSensor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor) { Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0; M_CylinderSensor *cs = (M_CylinderSensor *)sh->sensor; CylinderSensorStack *st = (CylinderSensorStack *) gf_node_get_private(sh->sensor); if (cs->isActive && (!cs->enabled || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)))) ) ) { if (cs->autoOffset) { cs->offset = cs->rotation_changed.q; if (!is_cancel) gf_node_event_out(sh->sensor, 5/*"offset"*/); } cs->isActive = 0; if (!is_cancel) gf_node_event_out(sh->sensor, 6/*"isActive"*/); sh->grabbed = 0; return is_cancel ? 0 : 1; } else if (is_mouse) { if (!cs->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { GF_Ray r; SFVec3f yaxis; Fixed acute, reva; SFVec3f bearing; gf_mx_copy(st->init_matrix, compositor->hit_world_to_local); /*get initial angle & check disk mode*/ r = compositor->hit_world_ray; gf_vec_add(r.dir, r.orig, r.dir); gf_mx_apply_vec(&compositor->hit_world_to_local, &r.orig); gf_mx_apply_vec(&compositor->hit_world_to_local, &r.dir); gf_vec_diff(bearing, r.orig, r.dir); gf_vec_norm(&bearing); yaxis.x = yaxis.z = 0; yaxis.y = FIX_ONE; acute = gf_vec_dot(bearing, yaxis); if (acute < -FIX_ONE) acute = -FIX_ONE; else if (acute > FIX_ONE) acute = FIX_ONE; acute = gf_acos(acute); reva = ABS(GF_PI - acute); if (reva<acute) acute = reva; st->disk_mode = (acute < cs->diskAngle) ? 1 : 0; st->grab_start = compositor->hit_local_point; /*cos we're lazy*/ st->yplane.d = 0; st->yplane.normal.x = st->yplane.normal.z = st->yplane.normal.y = 0; st->zplane = st->xplane = st->yplane; st->xplane.normal.x = FIX_ONE; st->yplane.normal.y = FIX_ONE; st->zplane.normal.z = FIX_ONE; cs->rotation_changed.x = 0; cs->rotation_changed.y = FIX_ONE; cs->rotation_changed.z = 0; cs->isActive = 1; gf_node_event_out(sh->sensor, 6/*"isActive"*/); sh->grabbed = 1; return 1; } else if (cs->isActive) { GF_Ray r; Fixed radius, rot; SFVec3f dir1, dir2, cx; if (is_over) { cs->trackPoint_changed = compositor->hit_local_point; gf_node_event_out(sh->sensor, 8/*"trackPoint_changed"*/); } else { GF_Plane project_to; r = compositor->hit_world_ray; gf_mx_apply_ray(&st->init_matrix, &r); /*no intersection, use intersection with "main" fronting plane*/ if ( ABS(r.dir.z) > ABS(r.dir.y)) { if (ABS(r.dir.z) > ABS(r.dir.x)) project_to = st->xplane; else project_to = st->yplane; } else { if (ABS(r.dir.z) > ABS(r.dir.x)) project_to = st->xplane; else project_to = st->zplane; } if (!gf_plane_intersect_line(&project_to, &r.orig, &r.dir, &compositor->hit_local_point)) return 0; } dir1.x = compositor->hit_local_point.x; dir1.y = 0; dir1.z = compositor->hit_local_point.z; if (st->disk_mode) { radius = FIX_ONE; } else { radius = gf_vec_len(dir1); } gf_vec_norm(&dir1); dir2.x = st->grab_start.x; dir2.y = 0; dir2.z = st->grab_start.z; gf_vec_norm(&dir2); cx = gf_vec_cross(dir2, dir1); gf_vec_norm(&cx); if (gf_vec_len(cx)<FIX_EPSILON) return 0; rot = gf_mulfix(radius, gf_acos(gf_vec_dot(dir2, dir1)) ); if (fabs(cx.y + FIX_ONE) < FIX_EPSILON) rot = -rot; if (cs->autoOffset) rot += cs->offset; if (cs->minAngle < cs->maxAngle) { if (rot < cs->minAngle) rot = cs->minAngle; else if (rot > cs->maxAngle) rot = cs->maxAngle; } cs->rotation_changed.q = rot; gf_node_event_out(sh->sensor, 7/*"rotation_changed"*/); return 1; } } else { if (!cs->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) { cs->isActive = 1; cs->rotation_changed.q = cs->offset; cs->rotation_changed.x = cs->rotation_changed.z = 0; cs->rotation_changed.y = FIX_ONE; gf_node_event_out(sh->sensor, 6/*"isActive"*/); return 1; } else if (cs->isActive && (ev->type==GF_EVENT_KEYDOWN)) { SFFloat res; Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? GF_PI/8 : GF_PI/64; res = cs->rotation_changed.q; switch (ev->key.key_code) { case GF_KEY_LEFT: res -= diff; break; case GF_KEY_RIGHT: res += diff; break; case GF_KEY_HOME: res = cs->offset; break; default: return 0; } /*clip*/ if (cs->minAngle <= cs->maxAngle) { if (res < cs->minAngle) res = cs->minAngle; if (res > cs->maxAngle) res = cs->maxAngle; } cs->rotation_changed.q = res; gf_node_event_out(sh->sensor, 7/*"rotation_changed"*/); return 1; } } return 0; }
static Bool OnSphereSensor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor) { Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0; M_SphereSensor *sphere = (M_SphereSensor *)sh->sensor; SphereSensorStack *st = (SphereSensorStack *) gf_node_get_private(sh->sensor); if (sphere->isActive && (!sphere->enabled || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)))) ) ) { if (sphere->autoOffset) { sphere->offset = sphere->rotation_changed; if (!is_cancel) gf_node_event_out(sh->sensor, 2/*"offset"*/); } sphere->isActive = 0; if (!is_cancel) gf_node_event_out(sh->sensor, 3/*"isActive"*/); sh->grabbed = 0; return is_cancel ? 0 : 1; } else if (is_mouse) { if (!sphere->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { st->center.x = st->center.y = st->center.z = 0; gf_mx_apply_vec(&compositor->hit_local_to_world, &st->center); st->radius = gf_vec_len(compositor->hit_local_point); if (!st->radius) st->radius = FIX_ONE; st->grab_vec = gf_vec_scale(compositor->hit_local_point, gf_invfix(st->radius)); sphere->isActive = 1; gf_node_event_out(sh->sensor, 3/*"isActive"*/); sh->grabbed = 1; return 1; } else if (sphere->isActive) { SFVec3f vec, axis; SFVec4f q1, q2; SFRotation r; Fixed cl; if (is_over) { sphere->trackPoint_changed = compositor->hit_local_point; gf_node_event_out(sh->sensor, 5/*"trackPoint_changed"*/); } else { GF_Ray r; r = compositor->hit_world_ray; gf_mx_apply_ray(&compositor->hit_world_to_local, &r); if (!gf_ray_hit_sphere(&r, NULL, st->radius, &compositor->hit_local_point)) { vec.x = vec.y = vec.z = 0; /*doesn't work properly...*/ compositor->hit_local_point = gf_closest_point_to_line(r.orig, r.dir, vec); } } vec = gf_vec_scale(compositor->hit_local_point, gf_invfix(st->radius)); axis = gf_vec_cross(st->grab_vec, vec); cl = gf_vec_len(axis); if (cl < -FIX_ONE) cl = -FIX_ONE; else if (cl > FIX_ONE) cl = FIX_ONE; r.q = gf_asin(cl); if (gf_vec_dot(st->grab_vec, vec) < 0) r.q += GF_PI / 2; gf_vec_norm(&axis); r.x = axis.x; r.y = axis.y; r.z = axis.z; q1 = gf_quat_from_rotation(r); if (sphere->autoOffset) { q2 = gf_quat_from_rotation(sphere->offset); q1 = gf_quat_multiply(&q1, &q2); } sphere->rotation_changed = gf_quat_to_rotation(&q1); gf_node_event_out(sh->sensor, 4/*"rotation_changed"*/); return 1; } } else { if (!sphere->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) { sphere->isActive = 1; sphere->rotation_changed = sphere->offset; gf_node_event_out(sh->sensor, 3/*"isActive"*/); return 1; } else if (sphere->isActive && (ev->type==GF_EVENT_KEYDOWN)) { SFVec4f res, rot; Fixed diff = GF_PI/64; res = sphere->rotation_changed; switch (ev->key.key_code) { case GF_KEY_LEFT: diff = -diff; case GF_KEY_RIGHT: rot.x = 0; rot.y = FIX_ONE; rot.z = 0; rot.q = diff; res = gf_quat_from_rotation(res); rot = gf_quat_from_rotation(rot); rot = gf_quat_multiply(&rot, &res); res = gf_quat_to_rotation(&rot); break; case GF_KEY_DOWN: diff = -diff; case GF_KEY_UP: if (ev->key.flags & GF_KEY_MOD_SHIFT) { rot.x = 0; rot.z = FIX_ONE; } else { rot.x = FIX_ONE; rot.z = 0; } rot.y = 0; rot.q = diff; res = gf_quat_from_rotation(res); rot = gf_quat_from_rotation(rot); rot = gf_quat_multiply(&rot, &res); res = gf_quat_to_rotation(&rot); break; case GF_KEY_HOME: res = sphere->offset; break; default: return 0; } sphere->rotation_changed = res; gf_node_event_out(sh->sensor, 4/*"rotation_changed"*/); return 1; } } return 0; }
static void OnSphereSensor(SensorHandler *sh, Bool is_over, GF_Event *ev, RayHitInfo *hit_info) { M_SphereSensor *sphere = (M_SphereSensor *)sh->owner; SphereSensorStack *st = (SphereSensorStack *) gf_node_get_private(sh->owner); if (sphere->isActive && (!sphere->enabled || ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) ) ) { if (sphere->autoOffset) { sphere->offset = sphere->rotation_changed; gf_node_event_out_str(sh->owner, "offset"); } sphere->isActive = 0; gf_node_event_out_str(sh->owner, "isActive"); R3D_SetGrabbed(st->compositor, 0); } else if (!sphere->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { st->center.x = st->center.y = st->center.z = 0; gf_mx_apply_vec(&hit_info->local_to_world, &st->center); st->radius = gf_vec_len(hit_info->local_point); if (!st->radius) st->radius = FIX_ONE; st->grab_vec = gf_vec_scale(hit_info->local_point, gf_invfix(st->radius)); sphere->isActive = 1; gf_node_event_out_str(sh->owner, "isActive"); R3D_SetGrabbed(st->compositor, 1); } else if (sphere->isActive) { SFVec3f vec, axis; SFVec4f q1, q2; SFRotation r; Fixed cl; if (is_over) { sphere->trackPoint_changed = hit_info->local_point; gf_node_event_out_str(sh->owner, "trackPoint_changed"); } else { GF_Ray r; r = hit_info->world_ray; gf_mx_apply_ray(&hit_info->world_to_local, &r); if (!gf_ray_hit_sphere(&r, NULL, st->radius, &hit_info->local_point)) { vec.x = vec.y = vec.z = 0; /*doesn't work properly...*/ hit_info->local_point = gf_closest_point_to_line(r.orig, r.dir, vec); } } vec = gf_vec_scale(hit_info->local_point, gf_invfix(st->radius)); axis = gf_vec_cross(st->grab_vec, vec); cl = gf_vec_len(axis); if (cl < -FIX_ONE) cl = -FIX_ONE; else if (cl > FIX_ONE) cl = FIX_ONE; r.q = gf_asin(cl); if (gf_vec_dot(st->grab_vec, vec) < 0) r.q += GF_PI / 2; gf_vec_norm(&axis); r.x = axis.x; r.y = axis.y; r.z = axis.z; q1 = gf_quat_from_rotation(r); if (sphere->autoOffset) { q2 = gf_quat_from_rotation(sphere->offset); q1 = gf_quat_multiply(&q1, &q2); } sphere->rotation_changed = gf_quat_to_rotation(&q1); gf_node_event_out_str(sh->owner, "rotation_changed"); } }
static void OnCylinderSensor(SensorHandler *sh, Bool is_over, GF_Event *ev, RayHitInfo *hit_info) { M_CylinderSensor *cs = (M_CylinderSensor *)sh->owner; CylinderSensorStack *st = (CylinderSensorStack *) gf_node_get_private(sh->owner); if (cs->isActive && (!cs->enabled || ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))) ) { if (cs->autoOffset) { cs->offset = cs->rotation_changed.q; gf_node_event_out_str(sh->owner, "offset"); } cs->isActive = 0; gf_node_event_out_str(sh->owner, "isActive"); R3D_SetGrabbed(st->compositor, 0); } else if (!cs->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { GF_Ray r; SFVec3f yaxis; Fixed acute, reva; SFVec3f bearing; gf_mx_copy(st->init_matrix, hit_info->world_to_local); /*get initial angle & check disk mode*/ r = hit_info->world_ray; gf_vec_add(r.dir, r.orig, r.dir); gf_mx_apply_vec(&hit_info->world_to_local, &r.orig); gf_mx_apply_vec(&hit_info->world_to_local, &r.dir); gf_vec_diff(bearing, r.orig, r.dir); gf_vec_norm(&bearing); yaxis.x = yaxis.z = 0; yaxis.y = FIX_ONE; acute = gf_vec_dot(bearing, yaxis); if (acute < -FIX_ONE) acute = -FIX_ONE; else if (acute > FIX_ONE) acute = FIX_ONE; acute = gf_acos(acute); reva = ABS(GF_PI - acute); if (reva<acute) acute = reva; st->disk_mode = (acute < cs->diskAngle) ? 1 : 0; st->grab_start = hit_info->local_point; /*cos we're lazy*/ st->yplane.d = 0; st->yplane.normal.x = st->yplane.normal.z = st->yplane.normal.y = 0; st->zplane = st->xplane = st->yplane; st->xplane.normal.x = FIX_ONE; st->yplane.normal.y = FIX_ONE; st->zplane.normal.z = FIX_ONE; cs->rotation_changed.x = 0; cs->rotation_changed.y = FIX_ONE; cs->rotation_changed.z = 0; cs->isActive = 1; gf_node_event_out_str(sh->owner, "isActive"); R3D_SetGrabbed(st->compositor, 1); } else if (cs->isActive) { GF_Ray r; Fixed radius, rot; SFVec3f dir1, dir2, cx; if (is_over) { cs->trackPoint_changed = hit_info->local_point; gf_node_event_out_str(sh->owner, "trackPoint_changed"); } else { GF_Plane project_to; r = hit_info->world_ray; gf_mx_apply_ray(&st->init_matrix, &r); /*no intersection, use intersection with "main" fronting plane*/ if ( ABS(r.dir.z) > ABS(r.dir.y)) { if (ABS(r.dir.x) > ABS(r.dir.x)) project_to = st->xplane; else project_to = st->zplane; } else project_to = st->yplane; if (!gf_plane_intersect_line(&project_to, &r.orig, &r.dir, &hit_info->local_point)) return; } dir1.x = hit_info->local_point.x; dir1.y = 0; dir1.z = hit_info->local_point.z; if (st->disk_mode) { radius = FIX_ONE; } else { radius = gf_vec_len(dir1); } gf_vec_norm(&dir1); dir2.x = st->grab_start.x; dir2.y = 0; dir2.z = st->grab_start.z; gf_vec_norm(&dir2); cx = gf_vec_cross(dir2, dir1); gf_vec_norm(&cx); if (gf_vec_len(cx)<FIX_EPSILON) return; rot = gf_mulfix(radius, gf_acos(gf_vec_dot(dir2, dir1)) ); if (fabs(cx.y + FIX_ONE) < FIX_EPSILON) rot = -rot; if (cs->autoOffset) rot += cs->offset; if (cs->minAngle < cs->maxAngle) { if (rot < cs->minAngle) rot = cs->minAngle; else if (rot > cs->maxAngle) rot = cs->maxAngle; } cs->rotation_changed.q = rot; gf_node_event_out_str(sh->owner, "rotation_changed"); } }