void picking_sphere_create(float radius, struct PickingSphere* sphere) { pivot_create(NULL, NULL, &sphere->pivot); sphere->picked = false; vec_copy4f((Vec4f){0.0f, 0.0f, 0.0f, 1.0f}, sphere->ray); sphere->radius = radius; sphere->front = -FLT_MAX; sphere->back = -FLT_MAX; }
void arcball_create(SDL_Window* window, Vec4f eye, Vec4f target, float z_near, float z_far, struct Arcball* arcball) { int32_t width,height; sdl2_debug( SDL_GL_GetDrawableSize(window, &width, &height) ); // - if the user specifies an eye and target so that we are looking along the y-axis (like 0,1,0 and 0,0,0), // then we just adjust the eye a tiny bit upwards to prevent a black screen because the up_axis used in // arcball_event is the y axis, and two parallel axis have no cross product and things become 0 and everything // gets messy, so just add FLOAT_EPSILON to the z coord and be done with it if( eye[0] == target[0] && eye[2] == target[2] ) { bool assert_test = eye[1] != target[1]; log_assert( assert_test == true ); eye[2] += CUTE_EPSILON; } if( z_near < 0.01f ) { log_warn(__FILE__, __LINE__, "you are trying to create a camera with a very small z_near value, " "this would cause problems when rendering vbomeshes with a z_offset, " "this function (arcball_create) will clamp the z_near value to 0.01f\n"); z_near = 0.01f; } camera_create(width, height, CAMERA_PERSPECTIVE, &arcball->camera); float top = (z_near/width) * height/2.0f; float bottom = -top; camera_set_frustum(&arcball->camera, -z_near/2.0f, z_near/2.0f, bottom, top, z_near, z_far); vec_copy4f(eye, arcball->camera.pivot.position); arcball->flipped = pivot_lookat(&arcball->camera.pivot, target); arcball->rotate_button = INPUT_MOUSE_ARCBALL_ROTATE; arcball->translate_button = INPUT_MOUSE_ARCBALL_TRANSLATE; arcball->translate_factor = 500.0f; arcball->zoom_factor = 10.0f; vec_copy4f(target, arcball->target); }
int32_t pivot_lookat(struct Pivot* pivot, const Vec3f target) { int32_t result = -1; Vec4f right_axis = RIGHT_AXIS; Vec4f up_axis = UP_AXIS; Vec4f forward_axis = FORWARD_AXIS; struct Pivot world_pivot; pivot_combine(pivot->parent, pivot, &world_pivot); Vec4f target_direction; vec_sub(target, world_pivot.position, target_direction); float dot_yaw = vec_dot(target_direction, forward_axis); Quat rotation = {0}; if( fabs(dot_yaw + 1.0f) < CUTE_EPSILON ) { // vector a and b point exactly in the opposite direction, // so it is a 180 degrees turn around the up-axis Quat rotation = {0}; quat_from_axis_angle(up_axis, PI, rotation); quat_mul(world_pivot.orientation, rotation, rotation); } else if( fabs(dot_yaw - (1.0f)) < CUTE_EPSILON ) { // vector a and b point exactly in the same direction // so we return the identity quaternion quat_copy(world_pivot.orientation, rotation); } else { // - I look at the target by turning the pivot orientation using only // yaw and pitch movement // - I always rotate the pivot from its initial orientation (up and forward // basis vectors like initialized above), so this does not incrementally // advance the orientation quat_identity(rotation); // - to find the amount of yaw I project the target_direction into the // up_axis plane, resulting in up_projection which is a vector that // points from the up_axis plane to the tip of the target_direction Vec4f up_projection = {0}; vec_mul1f(up_axis, vec_dot(target_direction, up_axis), up_projection); // - so then by subtracting the up_projection from the target_direction, // I get a vector lying in the up_axis plane, pointing towards the target Vec4f yaw_direction = {0}; vec_sub(target_direction, up_projection, yaw_direction); // - angle between yaw_direction and forward_axis is the amount of yaw we // need to point the forward_axis toward the target float yaw = 0.0f; vec_angle(yaw_direction, forward_axis, &yaw); log_assert( ! isnan(yaw), "vec_angle(%f %f %f, %f %f %f, %f);\n", yaw_direction[0], yaw_direction[1], yaw_direction[2], forward_axis[0], forward_axis[1], forward_axis[2], yaw ); // - I have to compute the cross product between yaw_direction and // forward_axis and use the resulting yaw_axis Vec4f yaw_axis = {0}; vec_cross(yaw_direction, forward_axis, yaw_axis); if( vec_nullp(yaw_axis) ) { vec_copy4f(up_axis, yaw_axis); } // - compute the yaw rotation Quat yaw_rotation = {0}; quat_from_axis_angle(yaw_axis, yaw, yaw_rotation); // - to compute, just as with the yaw, I want an axis that lies on the plane that // is spanned in this case by the right_axis, when the camera points toward the // target // - I could compute an axis, but I already have a direction vector that points // toward the target, the yaw_direction, I just have to normalize it to make it // an axis (and put the result in forward_axis, since it now is the forward_axis // of the yaw turned camera) Vec4f yaw_forward_axis = {0}; vec_normalize(yaw_direction, yaw_forward_axis); // - then use the new forward axis with the old target_direction to compute the angle // between those float pitch = 0.0f; vec_angle(target_direction, yaw_forward_axis, &pitch); log_assert( ! isnan(pitch), "vec_angle(%f %f %f, %f %f %f, %f);\n", target_direction[0], target_direction[1], target_direction[2], yaw_forward_axis[0], yaw_forward_axis[1], yaw_forward_axis[2], pitch ); // - and just as in the yaw case we compute an rotation pitch_axis Vec4f pitch_axis = {0}; vec_cross(target_direction, yaw_forward_axis, pitch_axis); if( vec_nullp(pitch_axis) ) { vec_copy4f(right_axis, pitch_axis); } // - and finally compute the pitch rotation and combine it with the yaw_rotation // in the same step Quat pitch_rotation; quat_from_axis_angle(pitch_axis, pitch, pitch_rotation); Quat yaw_pitch_rotation; quat_mul(yaw_rotation, pitch_rotation, yaw_pitch_rotation); Quat inverted_orientation = {0}; quat_invert(world_pivot.orientation, inverted_orientation); // - the int32_t I want to return indicates the cameras 'flip' status, that is, it is // one when the camera angle was pitched so much that it flipped over and its // up axis is now pointing downwards // - to find out if I am flipped over, I compute the flipped up_axis called // flip_axis and then use the dot product between the flip_axis and up_axis // to decide if I am flipped Vec4f flip_axis = {0}; vec_rotate(up_axis, inverted_orientation, flip_axis); vec_rotate(flip_axis, yaw_pitch_rotation, flip_axis); float dot_pitch = vec_dot(up_axis, flip_axis); Vec4f target_axis = {0}; vec_normalize(target_direction, target_axis); // - check if we are flipped and if we are, set result to 1 meaning we are flipped // - turn the camera around PI so that we can continue pitching, otherwise we just get // stuck when trying to flip the camera over if( dot_pitch < 0.0f ) { result = 1; quat_from_axis_angle(target_axis, PI, rotation); quat_mul(yaw_pitch_rotation, rotation, yaw_pitch_rotation); } quat_copy(yaw_pitch_rotation, rotation); } if( ! isnan(rotation[0]) && ! isnan(rotation[1]) && ! isnan(rotation[2]) && ! isnan(rotation[3]) ) { quat_copy(rotation, pivot->orientation); } pivot->eye_distance = vec_length(target_direction); return result; }