/*based on Copyright (C) 1995 Stephen Chenney ([email protected])*/ SFRotation camera_get_orientation(SFVec3f pos, SFVec3f target, SFVec3f up) { SFVec3f dir, tmp, v, axis, new_y; SFVec4f norm, inv_norm, y_quat, ny_quat, rot_y, rot; gf_vec_diff(dir, target, pos); gf_vec_norm(&dir); tmp = gf_vec_scale(dir, gf_vec_dot(up, dir)); gf_vec_diff(v, up, tmp); gf_vec_norm(&v); axis.x = dir.y; axis.y = -dir.x; axis.z = 0; if (gf_vec_dot(axis, axis) < FIX_EPSILON) { if (dir.z> 0) { norm.x = 0; norm.y = FIX_ONE; norm.z = 0; norm.q = 0; } else { norm.x = 0; norm.y = 0; norm.z = 0; norm.q = FIX_ONE; } } else { gf_vec_norm(&axis); norm = gf_quat_from_axis_cos(axis, -dir.z); } /* Find the inverse rotation. */ inv_norm.x = -norm.x; inv_norm.y = -norm.y; inv_norm.z = -norm.z; inv_norm.q = norm.q; /* Rotate the y. */ y_quat.x = y_quat.z = y_quat.q = 0; y_quat.y = FIX_ONE; ny_quat = gf_quat_multiply(&norm, &y_quat); ny_quat = gf_quat_multiply(&ny_quat, &inv_norm); new_y.x = ny_quat.x; new_y.y = ny_quat.y; new_y.z = ny_quat.z; tmp = gf_vec_cross(new_y, v); if (gf_vec_dot(tmp, tmp) < FIX_EPSILON) { /* The old and new may be pointing in the same or opposite. Need ** to generate a vector perpendicular to the old or new y. */ tmp.x = 0; tmp.y = -v.z; tmp.z = v.y; if (gf_vec_dot(tmp, tmp) < FIX_EPSILON) { tmp.x = v.z; tmp.y = 0; tmp.z = -v.x; } } gf_vec_norm(&tmp); rot_y = gf_quat_from_axis_cos(tmp, gf_vec_dot(new_y, v)); /* rot_y holds the rotation about the initial camera direction needed ** to align the up vectors in the final position. */ /* Put the 2 rotations together. */ rot = gf_quat_multiply(&rot_y, &norm); return gf_quat_to_rotation(&rot); }
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; }
Bool compositor_get_2d_plane_intersection(GF_Ray *ray, SFVec3f *res) { GF_Plane p; Fixed t, t2; if (!ray->dir.x && !ray->dir.y) { res->x = ray->orig.x; res->y = ray->orig.y; res->z = 0; return 1; } p.normal.x = p.normal.y = 0; p.normal.z = FIX_ONE; p.d = 0; t2 = gf_vec_dot(p.normal, ray->dir); if (t2 == 0) return 0; t = - gf_divfix(gf_vec_dot(p.normal, ray->orig) + p.d, t2); if (t<0) return 0; *res = gf_vec_scale(ray->dir, t); gf_vec_add(*res, ray->orig, *res); return 1; }
static void OnPlaneSensor(SensorHandler *sh, Bool is_over, GF_Event *ev, RayHitInfo *hit_info) { M_PlaneSensor *ps = (M_PlaneSensor *)sh->owner; PSStack *stack = (PSStack *) gf_node_get_private(sh->owner); if (ps->isActive && (!ps->enabled || ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) )) { if (ps->autoOffset) { ps->offset = ps->translation_changed; gf_node_event_out_str(sh->owner, "offset"); } ps->isActive = 0; gf_node_event_out_str(sh->owner, "isActive"); R3D_SetGrabbed(stack->compositor, 0); } else if (!ps->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { gf_mx_copy(stack->initial_matrix, hit_info->local_to_world); gf_vec_diff(stack->start_drag, hit_info->local_point, ps->offset); stack->tracker.normal.x = stack->tracker.normal.y = 0; stack->tracker.normal.z = FIX_ONE; stack->tracker.d = - gf_vec_dot(stack->start_drag, stack->tracker.normal); ps->isActive = 1; gf_node_event_out_str(sh->owner, "isActive"); R3D_SetGrabbed(stack->compositor, 1); } else if (ps->isActive) { GF_Ray loc_ray; SFVec3f res; loc_ray = hit_info->world_ray; gf_mx_apply_ray(&stack->initial_matrix, &loc_ray); gf_plane_intersect_line(&stack->tracker, &loc_ray.orig, &loc_ray.dir, &res); ps->trackPoint_changed = res; gf_node_event_out_str(sh->owner, "trackPoint_changed"); gf_vec_diff(res, res, stack->start_drag); /*clip*/ if (ps->minPosition.x <= ps->maxPosition.x) { if (res.x < ps->minPosition.x) res.x = ps->minPosition.x; if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x; } if (ps->minPosition.y <= ps->maxPosition.y) { if (res.y < ps->minPosition.y) res.y = ps->minPosition.y; if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y; } ps->translation_changed = res; gf_node_event_out_str(sh->owner, "translation_changed"); } }
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); }
void svg_drawable_3d_pick(Drawable *drawable, GF_TraverseState *tr_state, DrawAspect2D *asp) { SFVec3f local_pt, world_pt, vdiff; SFVec3f hit_normal; SFVec2f text_coords; u32 i, count; Fixed sqdist; Bool node_is_over; GF_Compositor *compositor; GF_Matrix mx; GF_Ray r; compositor = tr_state->visual->compositor; node_is_over = 0; r = tr_state->ray; gf_mx_copy(mx, tr_state->model_matrix); gf_mx_inverse(&mx); gf_mx_apply_ray(&mx, &r); /*if we already have a hit point don't check anything below...*/ if (compositor->hit_square_dist && !compositor->grabbed_sensor && !tr_state->layer3d) { GF_Plane p; GF_BBox box; SFVec3f hit = compositor->hit_world_point; gf_mx_apply_vec(&mx, &hit); p.normal = r.dir; p.d = -1 * gf_vec_dot(p.normal, hit); gf_bbox_from_rect(&box, &drawable->path->bbox); if (gf_bbox_plane_relation(&box, &p) == GF_BBOX_FRONT) { GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] bounding box of node %s (DEF %s) below current hit point - skipping\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node))); return; } } node_is_over = 0; if (compositor_get_2d_plane_intersection(&r, &local_pt)) { node_is_over = svg_drawable_is_over(drawable, local_pt.x, local_pt.y, asp, tr_state, NULL); } if (!node_is_over) return; hit_normal.x = hit_normal.y = 0; hit_normal.z = FIX_ONE; text_coords.x = gf_divfix(local_pt.x, drawable->path->bbox.width) + FIX_ONE/2; text_coords.y = gf_divfix(local_pt.y, drawable->path->bbox.height) + FIX_ONE/2; /*check distance from user and keep the closest hitpoint*/ world_pt = local_pt; gf_mx_apply_vec(&tr_state->model_matrix, &world_pt); for (i=0; i<tr_state->num_clip_planes; i++) { if (gf_plane_get_distance(&tr_state->clip_planes[i], &world_pt) < 0) { GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is not in clipper half space\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node))); return; } } gf_vec_diff(vdiff, world_pt, tr_state->ray.orig); sqdist = gf_vec_lensq(vdiff); if (compositor->hit_square_dist && (compositor->hit_square_dist+FIX_EPSILON<sqdist)) { GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is farther (%g) than current pick (%g)\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node), FIX2FLT(sqdist), FIX2FLT(compositor->hit_square_dist))); return; } compositor->hit_square_dist = sqdist; /*also stack any VRML sensors present at the current level. If the event is not catched by a listener in the SVG tree, the event will be forwarded to the VRML tree*/ gf_list_reset(compositor->sensors); count = gf_list_count(tr_state->vrml_sensors); for (i=0; i<count; i++) { gf_list_add(compositor->sensors, gf_list_get(tr_state->vrml_sensors, i)); } gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix); gf_mx_copy(compositor->hit_local_to_world, mx); compositor->hit_local_point = local_pt; compositor->hit_world_point = world_pt; compositor->hit_world_ray = tr_state->ray; compositor->hit_normal = hit_normal; compositor->hit_texcoords = text_coords; svg_clone_use_stack(compositor, tr_state); /*not use in SVG patterns*/ compositor->hit_appear = NULL; compositor->hit_node = drawable->node; compositor->hit_text = NULL; compositor->hit_use_dom_events = 1; GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is under mouse - hit %g %g %g\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node), FIX2FLT(world_pt.x), FIX2FLT(world_pt.y), FIX2FLT(world_pt.z))); }
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 OnPlaneSensor(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_PlaneSensor *ps = (M_PlaneSensor *)sh->sensor; PSStack *stack = (PSStack *) gf_node_get_private(sh->sensor); if (ps->isActive && ( /*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 (ps->autoOffset) { ps->offset = ps->translation_changed; if (!is_cancel) gf_node_event_out(sh->sensor, 4/*"offset"*/); } ps->isActive = 0; if (!is_cancel) gf_node_event_out(sh->sensor, 5/*"isActive"*/); sh->grabbed = 0; return is_cancel ? 0 : 1; } /*mouse*/ else if (is_mouse) { if (!ps->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT) ) { gf_mx_copy(stack->initial_matrix, compositor->hit_local_to_world); gf_vec_diff(stack->start_drag, compositor->hit_local_point, ps->offset); stack->tracker.normal.x = stack->tracker.normal.y = 0; stack->tracker.normal.z = FIX_ONE; stack->tracker.d = - gf_vec_dot(stack->start_drag, stack->tracker.normal); ps->isActive = 1; gf_node_event_out(sh->sensor, 5/*"isActive"*/); sh->grabbed = 1; return 1; } else if (ps->isActive) { GF_Ray loc_ray; SFVec3f res; loc_ray = compositor->hit_world_ray; gf_mx_apply_ray(&stack->initial_matrix, &loc_ray); gf_plane_intersect_line(&stack->tracker, &loc_ray.orig, &loc_ray.dir, &res); ps->trackPoint_changed = res; gf_node_event_out(sh->sensor, 6/*"trackPoint_changed"*/); gf_vec_diff(res, res, stack->start_drag); /*clip*/ if (ps->minPosition.x <= ps->maxPosition.x) { if (res.x < ps->minPosition.x) res.x = ps->minPosition.x; if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x; } if (ps->minPosition.y <= ps->maxPosition.y) { if (res.y < ps->minPosition.y) res.y = ps->minPosition.y; if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y; } ps->translation_changed = res; gf_node_event_out(sh->sensor, 7/*"translation_changed"*/); return 1; } } else { if (!ps->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) { ps->isActive = 1; stack->start_drag = ps->offset; gf_node_event_out(sh->sensor, 5/*"isActive"*/); return 1; } else if (ps->isActive && (ev->type==GF_EVENT_KEYDOWN)) { SFVec3f res; Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? 5*FIX_ONE : FIX_ONE; if (!gf_sg_use_pixel_metrics(gf_node_get_graph(sh->sensor))) diff = gf_divfix(diff, INT2FIX(compositor->vp_width/2)); res = stack->start_drag; switch (ev->key.key_code) { case GF_KEY_LEFT: res.x -= diff; break; case GF_KEY_RIGHT: res.x += diff; break; case GF_KEY_UP: res.y += diff; break; case GF_KEY_DOWN: res.y -= diff; break; case GF_KEY_HOME: res = ps->offset; break; default: return 0; } /*clip*/ if (ps->minPosition.x <= ps->maxPosition.x) { if (res.x < ps->minPosition.x) res.x = ps->minPosition.x; if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x; } if (ps->minPosition.y <= ps->maxPosition.y) { if (res.y < ps->minPosition.y) res.y = ps->minPosition.y; if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y; } stack->start_drag = res; ps->translation_changed = res; gf_node_event_out(sh->sensor, 7/*"translation_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"); } }