void DXPrimitives::DrawLineCircle(vec3 position, float radius, vec3 axisOfRotation) { vec3 referenceAxis = UNIT_X; if(axisOfRotation == referenceAxis) referenceAxis = UNIT_Z; vec3 rotPoint = cross(axisOfRotation, referenceAxis) * radius; const int numSegments = 30; float* verts = new float[numSegments * vertexSize]; for(int i = 0; i < numSegments; i++) { float theta = 2.0f * M_PI * float(i) / float(numSegments - 1); quaternion quat = quat_from_axis_angle(axisOfRotation, theta); vec3 point = quat * rotPoint; verts[i*vertexSize+0] = point.x + position.x; verts[i*vertexSize+1] = point.y + position.y; verts[i*vertexSize+2] = point.z + position.z; verts[i*vertexSize+3] = 1.0f; verts[i*vertexSize+4] = 0.5f; verts[i*vertexSize+5] = 0.5f; } DrawPrimitives(verts, numSegments, D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP); delete[] verts; }
void quat_mul_axis_angle(const Quat q, const Vec3f axis, const float angle, Quat r) { if( (fabs(axis[0]) < CUTE_EPSILON && fabs(axis[1]) < CUTE_EPSILON && fabs(axis[2]) < CUTE_EPSILON) || fabs(angle) < CUTE_EPSILON ) { quat_copy(q, r); } Quat rotation = {0}; quat_from_axis_angle(axis, angle, rotation); quat_mul(q, rotation, r); }
void quat_from_vec_pair(const Vec3f a, const Vec3f b, Quat q) { Vec4f axis; vec_cross(a,b,axis); float angle; vec_angle(a,b,&angle); if( (fabs(axis[0]) < CUTE_EPSILON && fabs(axis[1]) < CUTE_EPSILON && fabs(axis[2]) < CUTE_EPSILON) || fabs(angle) < CUTE_EPSILON ) { quat_identity(q); } quat_from_axis_angle(axis, angle, q); }
int32_t main(int32_t argc, char *argv[]) { if( init_sdl2() ) { return 1; } int width = 1280; int height = 720; SDL_Window* window; sdl2_window("cute3d: " __FILE__, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, &window); SDL_GLContext* context; sdl2_glcontext(3, 2, window, &context); if( init_shader() ) { return 1; } if( init_canvas(width, height) ) { return 1; } canvas_create("global_dynamic_canvas", &global_dynamic_canvas); canvas_create("global_static_canvas", &global_static_canvas); struct Vbo vbo = {0}; vbo_create(&vbo); vbo_add_buffer(&vbo, SHADER_ATTRIBUTE_VERTEX, 3, GL_FLOAT, GL_STATIC_DRAW); vbo_add_buffer(&vbo, SHADER_ATTRIBUTE_VERTEX_NORMAL, 3, GL_FLOAT, GL_STATIC_DRAW); vbo_add_buffer(&vbo, SHADER_ATTRIBUTE_VERTEX_COLOR, 4, GL_UNSIGNED_BYTE, GL_STATIC_DRAW); vbo_add_buffer(&vbo, SHADER_ATTRIBUTE_VERTEX_NORMAL, NORMAL_SIZE, GL_FLOAT, GL_STATIC_DRAW); struct Ibo ibo = {0}; ibo_create(GL_TRIANGLES, GL_UNSIGNED_INT, GL_STATIC_DRAW, &ibo); struct SolidTetrahedron hard_tetrahedron = {0}; struct SolidBox hard_cube = {0}; struct SolidSphere16 hard_sphere16 = {0}; struct SolidSphere32 hard_sphere32 = {0}; solid_tetrahedron_create(1.0, (Color){255, 0, 0, 255}, &hard_tetrahedron); solid_cube_create(0.5, (Color){0, 255, 0, 255}, &hard_cube); solid_sphere16_create(16, 8, 0.75, (Color){0, 255, 255, 255}, &hard_sphere16); solid_sphere32_create(32, 16, 0.75, (Color){255, 255, 0, 255}, &hard_sphere32); solid_optimize((struct Solid*)&hard_tetrahedron); solid_optimize((struct Solid*)&hard_cube); solid_optimize((struct Solid*)&hard_sphere16); solid_optimize((struct Solid*)&hard_sphere32); struct VboMesh hard_tetrahedron_mesh, hard_box_mesh, hard_cube_mesh, hard_sphere16_mesh, hard_sphere32_mesh; vbo_mesh_create_from_solid((struct Solid*)&hard_tetrahedron, &vbo, &ibo, &hard_tetrahedron_mesh); vbo_mesh_create_from_solid((struct Solid*)&hard_cube, &vbo, &ibo, &hard_cube_mesh); vbo_mesh_create_from_solid((struct Solid*)&hard_sphere16, &vbo, &ibo, &hard_sphere16_mesh); vbo_mesh_create_from_solid((struct Solid*)&hard_sphere32, &vbo, &ibo, &hard_sphere32_mesh); struct SolidTetrahedron smooth_tetrahedron = {0}; struct SolidBox smooth_cube = {0}; struct SolidSphere16 smooth_sphere16 = {0}; struct SolidSphere32 smooth_sphere32 = {0}; solid_tetrahedron_create(1.0, (Color){255, 0, 0, 255}, &smooth_tetrahedron); solid_cube_create(0.5, (Color){0, 255, 0, 255}, &smooth_cube); solid_sphere16_create(16, 8, 0.75, (Color){0, 255, 255, 255}, &smooth_sphere16); solid_sphere32_create(32, 16, 0.75, (Color){255, 255, 0, 255}, &smooth_sphere32); solid_optimize((struct Solid*)&smooth_tetrahedron); solid_optimize((struct Solid*)&smooth_cube); solid_optimize((struct Solid*)&smooth_sphere16); solid_optimize((struct Solid*)&smooth_sphere32); solid_smooth_normals((struct Solid*)&smooth_tetrahedron, smooth_tetrahedron.normals, smooth_tetrahedron.normals); solid_smooth_normals((struct Solid*)&smooth_cube, smooth_cube.normals, smooth_cube.normals); solid_smooth_normals((struct Solid*)&smooth_sphere16, smooth_sphere16.normals, smooth_sphere16.normals); solid_smooth_normals((struct Solid*)&smooth_sphere32, smooth_sphere32.normals, smooth_sphere32.normals); struct VboMesh smooth_tetrahedron_mesh, smooth_box_mesh, smooth_cube_mesh, smooth_sphere16_mesh, smooth_sphere32_mesh; vbo_mesh_create_from_solid((struct Solid*)&smooth_tetrahedron, &vbo, &ibo, &smooth_tetrahedron_mesh); vbo_mesh_create_from_solid((struct Solid*)&smooth_cube, &vbo, &ibo, &smooth_cube_mesh); vbo_mesh_create_from_solid((struct Solid*)&smooth_sphere16, &vbo, &ibo, &smooth_sphere16_mesh); vbo_mesh_create_from_solid((struct Solid*)&smooth_sphere32, &vbo, &ibo, &smooth_sphere32_mesh); struct Arcball arcball = {0}; arcball_create(width, height, (Vec4f){2.5,17.0,17.0,1.0}, (Vec4f){2.5,0.0,0.0,1.0}, 0.1, 100.0, &arcball); float circular_motion_angle = 0.0f; float circular_motion_speed = (2.0f*PI)/30; float circular_motion_radius = 12.0f; Vec3f light_position = { circular_motion_radius, 10.0, circular_motion_radius }; Vec3f light_direction = {0}; vec_sub((Vec3f){0.0f, 0.0f, 0.0f}, light_position, light_direction); vec_normalize(light_direction, light_direction); Vec3f eye_position = {0}; vec_copy3f(arcball.camera.pivot.position, eye_position); Color ambiance = {50, 25, 150, 255}; Color specular = {255, 255, 255, 255}; float material_shininess = 1.0; Vec4f material_coefficients = { 0.8, 0.2, 0.0, 0.0 }; // flat struct Shader flat_shader = {0}; shader_create(&flat_shader); shader_attach(&flat_shader, GL_VERTEX_SHADER, "prefix.vert", 1, "flat_shading.vert"); shader_attach(&flat_shader, GL_FRAGMENT_SHADER, "prefix.frag", 1, "flat_shading.frag"); shader_make_program(&flat_shader, SHADER_DEFAULT_NAMES, "flat_shader"); shader_set_uniform_3f(&flat_shader, flat_shader.program, SHADER_UNIFORM_LIGHT_DIRECTION, 3, GL_FLOAT, light_direction); shader_set_uniform_4f(&flat_shader, flat_shader.program, SHADER_UNIFORM_AMBIENT_LIGHT, 4, GL_UNSIGNED_BYTE, ambiance); // gouraud struct Shader gouraud_shader = {0}; shader_create(&gouraud_shader); shader_attach(&gouraud_shader, GL_VERTEX_SHADER, "prefix.vert", 1, "gouraud_shading.vert"); shader_attach(&gouraud_shader, GL_FRAGMENT_SHADER, "prefix.frag", 1, "gouraud_shading.frag"); shader_make_program(&gouraud_shader, SHADER_DEFAULT_NAMES, "gouraud_shader"); shader_set_uniform_3f(&gouraud_shader, gouraud_shader.program, SHADER_UNIFORM_LIGHT_POSITION, 3, GL_FLOAT, light_position); shader_set_uniform_3f(&gouraud_shader, gouraud_shader.program, SHADER_UNIFORM_EYE_POSITION, 3, GL_FLOAT, eye_position); shader_set_uniform_4f(&gouraud_shader, gouraud_shader.program, SHADER_UNIFORM_AMBIENT_LIGHT, 4, GL_UNSIGNED_BYTE, ambiance); shader_set_uniform_4f(&gouraud_shader, gouraud_shader.program, SHADER_UNIFORM_SPECULAR_LIGHT, 4, GL_UNSIGNED_BYTE, specular); shader_set_uniform_1f(&gouraud_shader, gouraud_shader.program, SHADER_UNIFORM_MATERIAL_SHININESS, 1, GL_FLOAT, &material_shininess); shader_set_uniform_4f(&gouraud_shader, gouraud_shader.program, SHADER_UNIFORM_MATERIAL_COEFFICIENTS, 4, GL_FLOAT, material_coefficients); shader_set_uniform_3f(&gouraud_shader, gouraud_shader.program, SHADER_UNIFORM_EYE_POSITION, 3, GL_FLOAT, &arcball.camera.pivot.position); Mat identity = IDENTITY_MAT; draw_grid(&global_static_canvas, 0, identity, (Color){127, 127, 127, 255}, 0.03f, 12.0f, 12.0f, 12); Mat shading_label_transform = IDENTITY_MAT; Quat text_rotation = IDENTITY_QUAT; quat_from_axis_angle((Vec3f)X_AXIS, -PI/2.0f, text_rotation); mat_rotate(shading_label_transform, text_rotation, shading_label_transform); mat_translate(shading_label_transform, (float[4]){ 6.5, 0.0, -2.5, 1.0 }, shading_label_transform);
bool arcball_event(struct Arcball* arcball, SDL_Event event) { static int32_t mouse_down = 0; static const float rotation_slowness_factor = 0.25f; static int32_t next_flipped = 0; // - arcball rotation is performed by dragging the mouse, so I just keep track of when // a mouse button is pressed and released between calls to this function by setting a // static variable mouse_down to the button number when a button is pressed and back // to 0 when that button is released if( event.type == SDL_MOUSEBUTTONDOWN && mouse_down == 0 ) { mouse_down = event.button.button; } else if( event.type == SDL_MOUSEBUTTONUP && mouse_down == event.button.button ) { arcball->flipped = next_flipped; mouse_down = 0; } if( mouse_down == arcball->translate_button && event.type == SDL_MOUSEMOTION ) { SDL_MouseMotionEvent mouse = event.motion; float eye_distance = arcball->camera.pivot.eye_distance; // - when an mouse motion event occurs, and mouse_down to the translation_button so we compute // a camera translation // - the camera should pan around on the x-z-plane, keeping its height and orientation Quat inverted_orientation = {0}; quat_invert(arcball->camera.pivot.orientation, inverted_orientation); // - the sideways translation is computed by taking the right_axis and orienting it with // the cameras orientation, the way I set up the lookat implementation this should always // result in a vector parallel to the x-z-plane Vec4f right_axis = RIGHT_AXIS; vec_rotate4f(right_axis, inverted_orientation, right_axis); if( mouse.xrel != 0 ) { // - then we'll just multiply the resulting axis with the mouse x relative movement, inversely // scaled by how far we are away from what we are looking at (farer means faster, nearer // means slower), the translation_factor is just a value that felt good when this was implemented Vec4f x_translation = {0}; vec_mul1f(right_axis, (float)mouse.xrel/arcball->translate_factor*eye_distance, x_translation); // - finally just add the x_translation to the target and position so that the whole arcball moves vec_add(arcball->target, x_translation, arcball->target); vec_add(arcball->camera.pivot.position, x_translation, arcball->camera.pivot.position); } // - the z translation can't be done along the orientated forward axis because that would include // the camera pitch, here, same as above, we need an axis that is parallel to the x-z-plane Vec4f up_axis = UP_AXIS; if( mouse.yrel != 0 ) { // - luckily such an axis is easily computed from the crossproduct of the orientated right_axis and // the default up_axis, the result is an axis pointing in the direction of the cameras forward axis, // while still being parallel to the x-z-plane Vec4f forward_axis; vec_cross(right_axis, up_axis, forward_axis); // - same as above Vec4f z_translation; vec_mul1f(forward_axis, (float)mouse.yrel/arcball->translate_factor*eye_distance, z_translation); // - dito vec_add(arcball->target, z_translation, arcball->target); vec_add(arcball->camera.pivot.position, z_translation, arcball->camera.pivot.position); } } else if( mouse_down == arcball->rotate_button && event.type == SDL_MOUSEMOTION ) { SDL_MouseMotionEvent mouse = event.motion; // - above case was translation, this is an rotation occuring: mouse_down is equal to the // rotation_button and the event is a mouse motion // - the camera needs to rotate around the target while keeping its height, orientation and // without _any_ rolling // - we want only yaw and pitch movement // - yaw is easy, just use the fixed up_axis and create a rotation the rotates around it // by the mouse x relative movement (converted to radians) // - the flipped value indicates if the camera is flipped over, so we'll just use that to // change the sign of the yaw to make the mouse movement on the screen always correctly // relates to the movement of the rotation Vec4f up_axis = UP_AXIS; Quat yaw_rotation = {0}; quat_from_axis_angle(up_axis, arcball->flipped * PI/180 * mouse.xrel * rotation_slowness_factor, yaw_rotation); // - pitch is a little more involved, I need to compute the orientated right axis and use // that to compute the pitch_rotation Quat inverted_orientation = {0}; quat_invert(arcball->camera.pivot.orientation, inverted_orientation); Vec4f right_axis = RIGHT_AXIS; vec_rotate4f(right_axis, inverted_orientation, right_axis); Quat pitch_rotation = {0}; quat_from_axis_angle(right_axis, -PI/180 * mouse.yrel * rotation_slowness_factor, pitch_rotation); // - combine yaw and pitch into a single rotation Quat rotation = {0}; quat_mul(yaw_rotation, pitch_rotation, rotation); // - orbit is the position translated to the coordinate root // - the yaw and pitch rotation is applied to the orbit // - orbit is translated back and replaces the camera position Vec4f orbit = {0}; vec_sub(arcball->camera.pivot.position, arcball->target, orbit); vec_rotate4f(orbit, rotation, orbit); vec_add(arcball->target, orbit, arcball->camera.pivot.position); // - after updating the position we just call lookat to compute the new // orientation, and also set the flipped state next_flipped = pivot_lookat(&arcball->camera.pivot, arcball->target); } if( event.type == SDL_MOUSEWHEEL ) { SDL_MouseWheelEvent wheel = event.wheel; // - zooming when mouse wheel event happens float* eye_distance = &arcball->camera.pivot.eye_distance; if( (*eye_distance > arcball->camera.frustum.z_near || wheel.y < 0) && (*eye_distance < arcball->camera.frustum.z_far || wheel.y > 0)) { // - just going back and forth along the oriented forward axis, using wheel // y motion inversly scaled by the eye_distance, similar to what is done // for the translation above (farer == faster zoom, nearer == slower zoom) Quat inverted_orientation = {0}; quat_invert(arcball->camera.pivot.orientation, inverted_orientation); Vec4f forward_axis = FORWARD_AXIS; vec_rotate4f(forward_axis, inverted_orientation, forward_axis); Vec4f zoom = {0}; vec_mul1f(forward_axis, wheel.y/arcball->zoom_factor*(*eye_distance), zoom); vec_add(arcball->camera.pivot.position, zoom, arcball->camera.pivot.position); // - eye_distance is kept in camera state, so we need to update it here *eye_distance = vlength(arcball->camera.pivot.position); } } return true; }
#include <math.h> #include <stdlib.h> #include "catch.hpp" XMFLOAT3 up(0, 1, 0); SCENARIO("Quaternion and Axis-Rotation Transforms behave equivalently") { GIVEN("Two equivalent transforms of different rotation implementations") { AxisTransform at(XMFLOAT3(1, 1, 0), XMFLOAT3(1, 1, 1), up, 90); // we must convert to the equivalent quaternion-- putting the hard-coded values // in ourselves is counterintuitive (unless you're really good at 4D geometry) auto quat = quat_from_axis_angle(up, 90); QuaternionTransform qt(XMFLOAT3(1, 1, 0), XMFLOAT3(1, 1, 1), quat); WHEN("Their world matrices are calculated") { auto axis_mat = make_worldmatrix(at); auto quat_mat = make_worldmatrix(qt); THEN("They should be roughly equivalent") { // if each respective member float is roughly equivalent, then we're good for (auto i = 0; i < 4; ++i) for (auto j = 0; j < 4; ++j) { CHECK(quat_mat.m[i][j] == Approx(axis_mat.m[i][j])); } }
int32_t main(int32_t argc, char *argv[]) { printf("<<watchlist//>>\n"); if( init_sdl2() ) { return 1; } uint32_t width = 1280; uint32_t height = 720; SDL_Window* window; sdl2_window("test-canvas", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, &window); SDL_GLContext* context; sdl2_glcontext(3, 2, window, &context); if( init_shader() ) { return 1; } if( init_canvas() ) { return 1; } printf("sizeof(struct Canvas): %zu\n", sizeof(struct Canvas)); printf("MAX_OGL_PRIMITIVES: %d\n", MAX_OGL_PRIMITIVES); struct Arcball arcball = {0}; arcball_create(width, height, (Vec4f){1.0,2.0,8.0,1.0}, (Vec4f){0.0,0.0,0.0,1.0}, 0.01, 1000.0, &arcball); struct Character symbols[256] = {0}; default_font_create(symbols); struct Shader shader = {0}; shader_create(&shader); shader_attach(&shader, GL_VERTEX_SHADER, "prefix.vert", 1, "volumetric_lines.vert"); shader_attach(&shader, GL_FRAGMENT_SHADER, "prefix.frag", 1, "volumetric_lines.frag"); shader_make_program(&shader, SHADER_DEFAULT_NAMES, "lines_shader"); struct Font font = {0}; font_create_from_characters(L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,:;", 256, symbols, 9, 3, global_default_font_palette, &font); struct Canvas text_canvas = {0}; canvas_create("text_canvas", &text_canvas); canvas_add_attribute(&text_canvas, SHADER_ATTRIBUTE_VERTEX, 3, GL_FLOAT); canvas_add_attribute(&text_canvas, SHADER_ATTRIBUTE_VERTEX_COLOR, 4, GL_UNSIGNED_BYTE); canvas_add_attribute(&text_canvas, SHADER_ATTRIBUTE_VERTEX_TEXCOORD, 2, GL_FLOAT); log_assert( canvas_add_shader(&text_canvas, shader.name, &shader) < MAX_CANVAS_SHADER ); log_assert( canvas_add_font(&text_canvas, "other_font", &font) < MAX_CANVAS_FONTS ); struct GameTime time = {0}; gametime_create(1.0f / 60.0f, &time); SDL_SetEventFilter(event_filter, NULL); while (true) { SDL_Event event; while( sdl2_poll_event(&event) ) { if( sdl2_handle_quit(event) ) { goto done; } sdl2_handle_resize(event); arcball_handle_resize(&arcball, event); arcball_handle_mouse(&arcball, event); } gametime_advance(&time, sdl2_time_delta()); sdl2_debug( SDL_GL_SetSwapInterval(1) ); ogl_debug( glClearDepth(1.0f); glClearColor(.0f, .0f, .0f, 1.0f); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); ); draw_grid(&text_canvas, 0, (Mat)IDENTITY_MAT, (Color){20, 180, 240, 255}, 0.02f, 12.0f, 12.0f, 12); draw_basis(&text_canvas, 1, (Mat)IDENTITY_MAT, 0.02f, 1.0f); Mat text_matrix = {0}; Quat text_rotation = {0}; quat_from_axis_angle((Vec4f){1.0, 0.0, 0.0, 1.0}, PI/2, text_rotation); mat_rotate(NULL, text_rotation, text_matrix); mat_translate(text_matrix, (Vec4f){-3.5, -1.0, 6.25, 1.0}, text_matrix); Vec4f world_cursor = {0,0,0,1}; text_put_world(&text_canvas, 0, world_cursor, text_matrix, (Color){0, 255, 255, 255}, 0.5f, "other_font", L"Dies ist ein Test\n"); text_put_world(&text_canvas, 0, world_cursor, text_matrix, (Color){255, 255, 0, 255}, 0.5f, "other_font", L"fuer einen Text"); gametime_integrate(&time); Vec4f screen_cursor = {0,0,0,1}; double fps = text_show_fps(&global_dynamic_canvas, 0, screen_cursor, 0, 0, (Color){255, 255, 255, 255}, 20.0f, "default_font", time.frame); /* text_show_time(&text_canvas, screen_cursor, 0, "default_font", 20.0, (Color){255, 255, 255, 255}, 0, 0, time.t); */ /* text_put_screen(&text_canvas, screen_cursor, 0, "default_font", 20.0, (Color){255, 210, 255, 255}, 0, 0, L"LALA singt das Meerschweinchen\n"); */ /* text_put_screen(&text_canvas, screen_cursor, 0, "default_font", 20.0, (Color){0, 210, 255, 255}, 0, 0, L"FICKEN immer und ueberall\n"); */ /* text_put_screen(&text_canvas, screen_cursor, 0, "default_font", 20.0, (Color){20, 210, 110, 255}, 0, 0, L"FUMMELN den ganzen Tag lang\n"); */ /* text_printf(&text_canvas, screen_cursor, 0, "default_font", 20.0, (Color){255, 40, 60, 255}, 0, 0, L"PRINTF %d Luftballons\n", 99); */ canvas_render_layers(&text_canvas, 0, MAX_CANVAS_LAYERS, &arcball.camera, (Mat)IDENTITY_MAT); canvas_clear(&text_canvas); canvas_render_layers(&global_dynamic_canvas, 0, MAX_CANVAS_LAYERS, &arcball.camera, (Mat)IDENTITY_MAT); canvas_clear(&global_dynamic_canvas); sdl2_debug( SDL_GL_SwapWindow(window) ); }
int32_t main(int32_t argc, char *argv[]) { printf("<<watchlist//>>\n"); if( init_sdl2() ) { return 1; } int32_t width = 1280; int32_t height = 720; SDL_Window* window; sdl2_window("cute3d: " __FILE__, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, &window); SDL_GLContext* context; sdl2_glcontext(3, 2, window, &context); if( init_shader() ) { return 1; } if( init_canvas(width, height) ) { return 1; } canvas_create("global_dynamic_canvas", &global_dynamic_canvas); struct Arcball arcball = {0}; arcball_create(width, height, (Vec4f){0.0,6.0,10.0,1.0}, (Vec4f){0.0,0.0,0.0,1.0}, 0.001f, 100.0, &arcball); struct GameTime time = {0}; gametime_create(1.0f / 60.0f, &time); Vec4f a = {0.0f, 0.0f, 1.0f}; Vec4f b = {1.0f, 0.0f, 1.0f}; draw_vec(&global_static_canvas, 0, (Mat)IDENTITY_MAT, (Color){25, 255, 25, 255}, 0.01f, a, (Vec3f){0.0f, 0.0f, 0.0f}, 1.0f, 1.0f); draw_vec(&global_static_canvas, 0, (Mat)IDENTITY_MAT, (Color){255, 25, 25, 255}, 0.01f, b, (Vec3f){0.0f, 0.0f, 0.0f}, 1.0f, 1.0f); Quat axis_angle_rot = {0}; quat_from_axis_angle((Vec4f)Y_AXIS, PI/4, axis_angle_rot); draw_quaternion(&global_static_canvas, 0, (Mat)IDENTITY_MAT, (Color){255, 255, 255, 255}, (Color){255, 0, 255, 255}, 0.01f, axis_angle_rot, 2.0f); vec_print("axis_angle_rot: ", axis_angle_rot); Vec4f axis_angle_result = {0}; vec_rotate(a, axis_angle_rot, axis_angle_result); draw_vec(&global_static_canvas, 0, (Mat)IDENTITY_MAT, (Color){255, 255, 25, 255}, 0.01f, axis_angle_result, (Vec3f){0.0f, 0.0f, 0.0f}, 1.0f, 2.0f); Vec4f axis = {0}; float angle = 0.0f; quat_to_axis_angle(axis_angle_rot, axis, &angle); Quat axis_angle_rot2 = {0}; quat_from_axis_angle(axis, angle, axis_angle_rot2); vec_print("axis_angle_rot2: ", axis_angle_rot2); Quat euler_angles_rot = {0}; quat_from_euler_angles(0.0f, PI/4, 0.0f, euler_angles_rot); vec_print("euler_angles_rot: ", euler_angles_rot); Vec4f euler_angles_result = {0}; vec_rotate(a, euler_angles_rot, euler_angles_result); draw_vec(&global_static_canvas, 0, (Mat)IDENTITY_MAT, (Color){25, 255, 255, 255}, 0.01f, euler_angles_result, (Vec3f){0.0f, 0.0f, 0.0f}, 1.0f, 3.0f); Quat vec_pair_rot = {0}; quat_from_vec_pair(a, b, vec_pair_rot); vec_print("vec_pair_rot: ", vec_pair_rot); Vec4f vec_pair_result = {0}; vec_rotate(a, vec_pair_rot, vec_pair_result); draw_vec(&global_static_canvas, 0, (Mat)IDENTITY_MAT, (Color){255, 25, 255, 255}, 0.01f, vec_pair_result, (Vec3f){0.0f, 0.0f, 0.0f}, 1.0f, 4.0f); Mat xaxis_control = {0}; xaxis_control[0] = 1; xaxis_control[4] = 0; xaxis_control[8] = 0; xaxis_control[12] = 0; xaxis_control[1] = 0; xaxis_control[5] = cosf(PI/4); xaxis_control[9] = -sinf(PI/4); xaxis_control[13] = 0; xaxis_control[2] = 0; xaxis_control[6] = sinf(PI/4); xaxis_control[10] = cosf(PI/4); xaxis_control[14] = 0; xaxis_control[3] = 0; xaxis_control[7] = 0; xaxis_control[11] = 0; xaxis_control[15] = 1; mat_print("xaxis_control: ", xaxis_control); Quat xaxis_rot = {0}; quat_from_axis_angle((Vec4f)X_AXIS, PI/4, xaxis_rot); Mat xaxis_mat = {0}; quat_to_mat(xaxis_rot, xaxis_mat); mat_print("xaxis_mat: ", xaxis_mat); Mat yaxis_control = {0}; yaxis_control[0] = cosf(PI/4); yaxis_control[4] = 0; yaxis_control[8] = sinf(PI/4); yaxis_control[12] = 0; yaxis_control[1] = 0; yaxis_control[5] = 1; yaxis_control[9] = 0; yaxis_control[13] = 0; yaxis_control[2] = -sinf(PI/4); yaxis_control[6] = 0; yaxis_control[10] = cosf(PI/4); yaxis_control[14] = 0; yaxis_control[3] = 0; yaxis_control[7] = 0; yaxis_control[11] = 0; yaxis_control[15] = 1; mat_print("yaxis_control: ", yaxis_control); Quat yaxis_rot = {0}; quat_from_axis_angle((Vec4f)Y_AXIS, PI/4, yaxis_rot); Mat yaxis_mat = {0}; quat_to_mat(yaxis_rot, yaxis_mat); mat_print("yaxis_mat: ", yaxis_mat); Mat zaxis_control = {0}; zaxis_control[0] = cosf(PI/4); zaxis_control[4] = -sinf(PI/4); zaxis_control[8] = 0; zaxis_control[12] = 0; zaxis_control[1] = sinf(PI/4); zaxis_control[5] = cosf(PI/4); zaxis_control[9] = 0; zaxis_control[13] = 0; zaxis_control[2] = 0; zaxis_control[6] = 0; zaxis_control[10] = 1; zaxis_control[14] = 0; zaxis_control[3] = 0; zaxis_control[7] = 0; zaxis_control[11] = 0; zaxis_control[15] = 1; mat_print("zaxis_control: ", zaxis_control); Quat zaxis_rot = {0}; quat_from_axis_angle((Vec4f)Z_AXIS, PI/4, zaxis_rot); Mat zaxis_mat = {0}; quat_to_mat(zaxis_rot, zaxis_mat); mat_print("zaxis_mat: ", zaxis_mat); draw_grid(&global_static_canvas, 0, (Mat)IDENTITY_MAT, (Color){120, 120, 120, 255}, 0.01f, 12.0f, 12.0f, 12); while(true) { SDL_Event event; while( sdl2_poll_event(&event) ) { if( sdl2_handle_quit(event) ) { goto done; } sdl2_handle_resize(event); arcball_handle_resize(&arcball, event); arcball_handle_mouse(&arcball, event); } sdl2_gl_set_swap_interval(0); ogl_debug( glClearDepth(1.0f); glClearColor(.0f, .0f, .0f, 1.0f); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); ); gametime_advance(&time, sdl2_time_delta()); gametime_integrate(&time); canvas_render_layers(&global_static_canvas, 0, MAX_CANVAS_LAYERS, &arcball.camera, (Mat)IDENTITY_MAT); sdl2_gl_swap_window(window); }
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; }
QuatP* qfrom_axis_angle(Quat axis, const float angle) { quat_from_axis_angle(axis, angle, axis); return axis; }