diff --git a/src/l1_5/core/quaternion.h b/src/l1_5/core/quaternion.h index 9140d73..320dae9 100644 --- a/src/l1_5/core/quaternion.h +++ b/src/l1_5/core/quaternion.h @@ -5,7 +5,8 @@ typedef vec4 quaternion_t; -vec4 quaternion_fl_mul_vec4(vec4 A, vec4 B){ +/* quaternion multiplication (quaternions are stored in vec4) */ +vec4 quaternion_fl_mul(vec4 A, vec4 B){ return (vec4){ A.x * B.x - A.y * B.y - A.z * B.z - A.w * B.w, A.x * B.y + A.y * B.x + A.z * B.w - A.w * B.z, @@ -25,7 +26,8 @@ vec4 quaternion_fl_inverse(vec4 a){ return (vec4){a.x * m, -a.y * m, -a.z * m, -a.w * m}; } -mat3 quaternion_fl_to_rot3d_mat3(vec4 q){ +/* It is assumed that q unit, i.e. abs(q) ~= 1 */ +mat3 unit_quaternion_fl_to_rot3d_mat3(vec4 q){ float a = q.x; float Rx = q.y; float Ry = q.z; @@ -33,27 +35,28 @@ mat3 quaternion_fl_to_rot3d_mat3(vec4 q){ return mat3_add_mat3(mat3_add_mat3( mat3_mul_scal(mat3_E, 2*a*a - 1), - mat3_mul_scal(mat3_for_cross_product((vec3){Rx, Ry, Rz}), 2 * a)), mat3_E); + mat3_mul_scal(mat3_for_cross_product((vec3){Rx, Ry, Rz}), -2 * a)), + mat3_mul_scal(mat3_new_for_proj((vec3){Rx, Ry, Rz}), 2)); } /* This thing requires some MATH */ -vec4 unit_quaternion_fl_power(vec4 q, float t){ - float qva = vec3_length((vec3){q.y, q.z, q.w}); - float fi = atan2f(qva, q.x); - const float rub = 0.001f; - float e_x = cosf(t * fi); - float e_vc; - // float t_to_3 = t*t*t; - // float t_to_5 = t_to_3*t*t; - // float fi_to_2 = fi * fi; - // float fi_to_4 = fi_to_2 * fi_to_2; - // Remember, qva = sin(fi) - if (qva < rub) { - /* I have a feeling this is an overkill... Well, only time __and 100 hours of testing__ will show ;) */ - // e_vc = t + (-t_to_3+t) / 6 * fi_to_2 + (3 * t_to_5 - 10 * t_to_3 + 7 * t) / 360 * fi_to_4; - e_vc = t + (-t*t*t+t) / 6 * fi * fi ; - } else { - e_vc = sinf(t *fi) / qva; - } - return (vec4){e_x, e_vc * q.y, e_vc * q.z, e_vc * q.w}; +// vec4 unit_quaternion_fl_power(vec4 q, float t){ +// float qva = vec3_length((vec3){q.y, q.z, q.w}); +// float fi = atan2f(qva, q.x); +// const float rub = 0.001f; +// float e_x = cosf(t * fi); +// float e_vc; +// // float t_to_3 = t*t*t; +// // float t_to_5 = t_to_3*t*t; +// // float fi_to_2 = fi * fi; +// // float fi_to_4 = fi_to_2 * fi_to_2; +// // Remember, qva = sin(fi) +// if (qva < rub) { +// /* I have a feeling this is an overkill... Well, only time __and 100 hours of testing__ will show ;) */ +// // e_vc = t + (-t_to_3+t) / 6 * fi_to_2 + (3 * t_to_5 - 10 * t_to_3 + 7 * t) / 360 * fi_to_4; +// e_vc = t + (-t*t*t+t) / 6 * fi * fi ; +// } else { +// e_vc = sinf(t *fi) / qva; +// } +// return (vec4){e_x, e_vc * q.y, e_vc * q.z, e_vc * q.w}; } diff --git a/src/l2/alice/engine.h b/src/l2/alice/engine.h index c3f6725..78a468f 100644 --- a/src/l2/alice/engine.h +++ b/src/l2/alice/engine.h @@ -667,6 +667,7 @@ typedef struct { VkQueue presentation_queue; } UsedVulkanQueues; +void alice_default_callback_on_wl_pointer_button(void* d, uint32_t button, uint32_t btn_action){} void alice_default_callback_on_wl_keyboard_key(void* d, U32 keysym, U32 key_action){} @@ -675,14 +676,17 @@ void alice_default_callback_on_another_frame(void* d, float fl){} typedef struct Alice Alice; typedef struct{ + /* guest data, button, button action (wl_pointer_button_state) */ + void (*on_wl_pointer_button)(void*, U32, U32); /* guest data, keysym, key action (wl_keyboard_key_state) */ void (*on_wl_keyboard_key)(void*, U32, U32); void (*on_another_frame)(void*, float); } AliceCallbacks; void AliceCallbacks_set_default(AliceCallbacks* self){ - self->on_another_frame = alice_default_callback_on_another_frame; + self->on_wl_pointer_button = alice_default_callback_on_wl_pointer_button; self->on_wl_keyboard_key = alice_default_callback_on_wl_keyboard_key; + self->on_another_frame = alice_default_callback_on_another_frame; } typedef struct { @@ -826,14 +830,6 @@ struct Alice { ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, const GenericMeshTopology* topology, AliceGenericMeshTexturePaths t_paths){ - // GenericMeshTopology topology; - // if (SpanU8_is_postfix(cstr(".AliceGenericMesh"), VecU8_to_span(&paths.topology_path) )) { - // topology = alice_expect_read_generic_mesh_from_file(paths.topology_path); - // } else if (SpanU8_is_postfix(cstr(".obj"), VecU8_to_span(&paths.topology_path) )) { - // topology = alice_expect_read_generic_mesh_from_obj_file(paths.topology_path); - // } else { - // abortf("Иди своей дорогой\n"); - // } ListNodeAliceGenericMeshHand* mm_node = safe_calloc(1, sizeof(ListNodeAliceGenericMeshHand)); AliceGenericMeshHand* mm = &mm_node->el; @@ -1724,6 +1720,7 @@ static void alice_mainloop_h_wl_pointer_button( void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t btn_action ) { Alice* alice = data; + alice->callbacks.on_wl_pointer_button(alice->guest, button, btn_action); } static void alice_mainloop_h_wl_pointer_axis( @@ -2031,6 +2028,7 @@ void Alice_set_point_light(Alice* alice, int index, Pipeline0PointLight data){ /* This function actually consumes alice handler. Alice must not be used after */ void Alice_mainloop(Alice* alice, const AliceCallbacks* callbacks) { + alice->callbacks.on_wl_pointer_button = callbacks->on_wl_pointer_button, alice->callbacks.on_wl_keyboard_key = callbacks->on_wl_keyboard_key; alice->callbacks.on_another_frame = callbacks->on_another_frame; printf("ENTERING WAYLAND MAINLOOP\n"); diff --git a/src/l3/r4/r4.c b/src/l3/r4/r4.c index d7666c4..b0e47eb 100644 --- a/src/l3/r4/r4.c +++ b/src/l3/r4/r4.c @@ -1,10 +1,11 @@ #include "../../l2/core/glb_file.h" #include "../../l2/alice/engine.h" -#include "../../l1_5/core/quaternion.h" #include "../../l1_5/marie/prim_shape_geom.h" #include "../../../gen/l1/VecAndSpan_vec3.h" #include "../../l1_5/core/color.h" +#include "linux/input-event-codes.h" + typedef struct{ vec3 pos; vec3 color; @@ -14,7 +15,8 @@ typedef struct{ #include "../../../gen/l1/eve/r4/VecLightSourceState.h" typedef struct { - float mass; + float mass; /* In kg, Not zero */ + float moment_of_inertia; /* In kg*m^2 */ /* Center of mass relative to "center of mesh" */ vec3 mass_center; } RigidBodyConfig; @@ -22,25 +24,47 @@ typedef struct { typedef struct { RigidBodyConfig p; vec3 pos; - vec3 speed; - quaternion_t rot; - quaternion_t rot_speed; + vec3 speed; // linear speed + mat3 rot; + vec3 angular_speed; } RigidBodyState; -/* point `M` is relative to the center of mass */ -vec3 RigidBodyState_translate_point(const RigidBodyState* self, vec3 M){ - return vec3_add_vec3(self->pos, M); -} - -vec3 RigidBodyState_translate_point_of_mesh(const RigidBodyState* self, vec3 M){ - return RigidBodyState_translate_point(self, vec3_minus_vec3(M, self->p.mass_center)); -} - /* Transforms points relative to center of mass. To get model_t for mesh, multiply transition matrix by this matrix */ mat4 RigidBodyState_get_tran_mat(const RigidBodyState* self){ - return marie_translation_mat4(self->pos); + return mat4_mul_mat4( + marie_translation_mat4(self->pos), + marie_mat3_to_mat4(self->rot)); } +mat4 RigidBodyState_get_tran_mat_of_mesh(const RigidBodyState* self) { + return mat4_mul_mat4(RigidBodyState_get_tran_mat(self), + marie_translation_mat4(vec3_minus(self->p.mass_center))); +} + +/* point `M` is relative to the center of mass */ +vec3 RigidBodyState_translate_point(const RigidBodyState* self, vec3 M){ + return vec3_add_vec3(self->pos, mat3_mul_vec3(self->rot, M)); +} + +/* point Mf is relative to visual mesh coordinate system origin */ +vec3 RigidBodyState_translate_point_of_mesh(const RigidBodyState* self, vec3 Mf){ + return RigidBodyState_translate_point(self, vec3_minus_vec3(Mf, self->p.mass_center)); +} + +/* point T is in global coordinate system. It is returned to coordinate system relative to center of mass */ +vec3 RigidBodyState_back_translate_point(const RigidBodyState* self, vec3 T) { + return mat3_mul_vec3(mat3_transpose(self->rot), vec3_minus_vec3(T, self->pos)); +} + +vec3 RigidBodyState_back_translate_point_of_mesh(const RigidBodyState* self, vec3 T) { + return vec3_add_vec3(RigidBodyState_back_translate_point(self, T), self->p.mass_center); +} + +typedef struct { + float mass; /* In kg, Not zero */ + float velocity; /* Not zero */ +} BulletConfig; + vec3 project_dir_onto_plane_xz(vec3 v){ vec2 xz = vec2_normalize((vec2){v.x, v.z}); return (vec3){xz.x, 0, xz.y}; @@ -59,6 +83,7 @@ typedef struct{ ListNodeAliceShinyMeshHand* LS_mesh; VecLightSourceState LS_state; + BulletConfig bullet_config; U64 misses_count; U64 hits_count; Vecvec3 bullets_stuck_on_ROA; @@ -67,11 +92,48 @@ typedef struct{ /* We are surrounded by a giant cubic mesh of light sources */ const int lighting_system_dim = 4; -void main_h_on_wayland_keyboard_key(void* data, U32 keysym, U32 act){ + +void physics_update(R4BetaState* st, float t){ + RigidBodyState *roa_st = &st->ROA_state; + roa_st->pos = vec3_add_vec3(roa_st->pos, vec3_mul_scal(roa_st->speed, t)); + mat3 rot_gain; + float rot_speed_amplitude = vec3_length(roa_st->angular_speed); + if (rot_speed_amplitude < 0.00001f){ + rot_gain = mat3_E; + } else { + vec3 r = vec3_div_by_scal(roa_st->angular_speed, rot_speed_amplitude); + rot_gain = marie_3d_rot_mat3(r, rot_speed_amplitude); + } + roa_st->rot = mat3_mul_mat3(rot_gain, roa_st->rot); +} + +/* Changs linear and rotational speed of RigidBody based on the impact with bullet. + * It does not update mass, moment of intertia, center of mass of rigid body. It is as if the bullet was + * very light compared to rigid body. + * imp is impact position (relative to center of mass) + * m2 is the mass of bullet. `v` is the speed of bullet */ +void RigidBodyState_when_shot(RigidBodyState* self, vec3 imp, float m2, vec3 v){ + vec3 IO = vec3_minus(imp); + float IO_norm_sq = vec3_dot(IO, IO); + + vec3 linear_speed_gain; + if (IO_norm_sq < 0.00001f) { + linear_speed_gain = v; + } else { + vec3 v_projected = vec3_mul_scal(IO, vec3_dot(IO, v) / IO_norm_sq); + linear_speed_gain = vec3_mul_scal(v_projected, m2 / self->p.mass); + } + self->speed = vec3_add_vec3(self->speed, linear_speed_gain); + + + vec3 angular_speed_gain = vec3_mul_scal(vec3_cross(v, IO), m2 / self->p.moment_of_inertia); + self->angular_speed = vec3_add_vec3(self->angular_speed, angular_speed_gain); +} + +void main_h_on_wl_pointer_button(void* data, U32 button, U32 act){ R4BetaState *st = data; Alice* alice = st->alice; - if (act == WL_KEYBOARD_KEY_STATE_RELEASED && keysym == XKB_KEY_Return) { - printf("Shot a bullet!\n"); + if (button == BTN_LEFT && act == WL_POINTER_BUTTON_STATE_PRESSED) { vec3 P = alice->cam_info.cam.pos; vec3 dir = vec3_minus(alice->cam_info.cam.cam_basis.z); vec3 Q = vec3_add_vec3(P, dir); @@ -80,7 +142,7 @@ void main_h_on_wayland_keyboard_key(void* data, U32 keysym, U32 act){ VecU32* Is = &st->ROA_topology.indexes; VecGenericMeshVertexInc* Vs = &st->ROA_topology.vertices; assert(Is->len % 3 == 0); - // todo: when I make model-t more complex (add rotation), this code will need to be updated + for (size_t i = 0; i < Is->len; i += 3) { vec3 A = Vs->buf[Is->buf[i + 0]].pos; vec3 B = Vs->buf[Is->buf[i + 1]].pos; @@ -97,14 +159,57 @@ void main_h_on_wayland_keyboard_key(void* data, U32 keysym, U32 act){ /* See, for some dumb reason I decided that bullets share a mesh with light sources. They are all cubes */ // U64 n = st->bullets_stuck_on_ROA.len + st->LS_state.len; assert(st->bullets_stuck_on_ROA.len + st->LS_state.len == st->LS_mesh->el.instance_attr.count); - vec3 impact_from_center = vec3_minus_vec3(impact, st->ROA_state.pos); - Vecvec3_append(&st->bullets_stuck_on_ROA, impact_from_center); + vec3 OI = vec3_minus_vec3(impact, st->ROA_state.pos); + /* OI is only a partial "back translation" */ + vec3 impact_in_MC_coords = mat3_mul_vec3(mat3_transpose(st->ROA_state.rot), OI); + Vecvec3_append(&st->bullets_stuck_on_ROA, impact_in_MC_coords); + + /* Adjusting linear speed and rotational speed of ROA */ + RigidBodyState_when_shot(&st->ROA_state, OI, st->bullet_config.mass, + vec3_mul_scal(dir, st->bullet_config.velocity)); } else { st->misses_count++; } } } +void main_h_on_wl_keyboard_key(void* data, U32 keysym, U32 act){ + R4BetaState *st = data; + // Alice* alice = st->alice; + if (act == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (keysym == XKB_KEY_9) { + if (st->bullet_config.velocity > 0.1) { + st->bullet_config.velocity *= 0.5f; + } + } else if (keysym == XKB_KEY_0) { + if (st->bullet_config.velocity < 1000000) { + st->bullet_config.velocity *= 2; + } + } else if (keysym == XKB_KEY_7) { + if (st->bullet_config.mass > 0.0005) { + st->bullet_config.mass *= 0.5f; + } + } else if (keysym == XKB_KEY_8) { + if (st->bullet_config.mass < 100) { + st->bullet_config.mass *= 2; + } + } else if (keysym == XKB_KEY_semicolon) { + st->ROA_state.speed = (vec3){0}; + } else if (keysym == XKB_KEY_colon){ + if (vec3_length(st->ROA_state.speed) < 0.2) { + st->ROA_state.speed = (vec3){0}; + } else { + st->ROA_state.speed = vec3_mul_scal(st->ROA_state.speed, 0.4f); + } + if (vec3_length(st->ROA_state.angular_speed) < 0.005) { + st->ROA_state.angular_speed = (vec3){0}; + } else { + st->ROA_state.angular_speed = vec3_mul_scal(st->ROA_state.angular_speed, 0.5f); + } + } + } +} + void main_h_on_another_frame(void* data, float fl){ R4BetaState *st = data; Alice* alice = st->alice; @@ -131,9 +236,10 @@ void main_h_on_another_frame(void* data, float fl){ } alice->cam_info.cam.pos = st->hero_pos; + physics_update(st, fl); + AliceGenericMeshHand_set_inst(&st->ROA_mesh->el, 0, (GenericMeshInstanceInc){ - .model_t = mat4_mul_mat4(RigidBodyState_get_tran_mat(&st->ROA_state), - marie_translation_mat4(vec3_minus(st->ROA_state.p.mass_center))), + .model_t = RigidBodyState_get_tran_mat_of_mesh(&st->ROA_state), }); Pipeline0UBO* ubo = (Pipeline0UBO*)MargaretSubbuf_get_mapped(&st->alice->pipeline0_ubo.staging); @@ -165,8 +271,10 @@ void main_h_on_another_frame(void* data, float fl){ LucyRenderer_clear(&alice->lucy_renderer); VecU8 text = VecU8_new(); + VecU8_append_fmt(&text, "Bullet mass = %v ; bullet velocity = %v\n", + VecU8_format("%.4f", st->bullet_config.mass), VecU8_format("%.4f", st->bullet_config.velocity)); if (st->misses_count == 0 && st->hits_count == 0) { - VecU8_append_cstr(&text, "Press ENTER to shoot\n"); + VecU8_append_cstr(&text, "Press BTN_LEFT to shoot\n"); } if (st->misses_count > 0) { VecU8_append_fmt(&text, "You missed %u times\n", st->misses_count); @@ -186,10 +294,8 @@ void run_app(){ st.alice = alice; st.alice->guest = &st; - // st.font_face = LucyFace_new(st.alice->ft_library, &st.alice->lucy_cache, - // VecU8_fmt("%s/src/l3/fonts/DMSerifText-Regular.ttf", cstr("."))); st.font_face = LucyFace_new(st.alice->ft_library, &st.alice->lucy_cache, - VecU8_fmt("%s/src/l3/fonts/GreatVibes-Regular.ttf", cstr("."))); + vcstr("./src/l3/fonts/DMSerifText-Regular.ttf")); st.font_face_of_size_40 = LucyFace_of_size(st.font_face, 40); VecLucyGlyphCachingRequest lucy_requests = VecLucyGlyphCachingRequest_new(); VecU32Segment ranges_needed = VecU32Segment_new(); @@ -220,9 +326,9 @@ void run_app(){ }); AliceGenericMeshHand_resize_instance_arr(st.alice, &st.ROA_mesh->el, 1); st.ROA_state = (RigidBodyState){ - .p.mass = 100.f, .p.mass_center = (vec3){5, 0.77f, -0.77f}, + .p.mass = 50.f, .p.moment_of_inertia = 5000.f, .p.mass_center = (vec3){5, 0.77f, -0.77f}, .pos = (vec3){11.f, 3, 4}, .speed = {0}, - .rot = (vec4){1, 0, 0, 0}, .rot_speed = (vec4){1, 0, 0, 0}}; + .rot = mat3_E, .angular_speed = (vec3){0}}; ShinyMeshTopology LS_topology = alice_expect_read_shiny_mesh_from_file(vcstr("./gen/l2/models/cube.AliceShinyMesh")); @@ -265,12 +371,14 @@ void run_app(){ ubo->point_light_count = (int)st.LS_state.len; ubo->spotlight_count = 0; + st.bullet_config = (BulletConfig){.mass = 0.01f, .velocity = 1000}; st.misses_count = 0; st.hits_count = 0; st.bullets_stuck_on_ROA = Vecvec3_new(); Alice_mainloop(st.alice, &(AliceCallbacks){ - .on_wl_keyboard_key = main_h_on_wayland_keyboard_key, + .on_wl_pointer_button = main_h_on_wl_pointer_button, + .on_wl_keyboard_key = main_h_on_wl_keyboard_key, .on_another_frame = main_h_on_another_frame, }); }