OMG OMG IT WORKS. Достигнута цель для КТ1: симуляция одного тёрдого тела в невесомости. This entire quaternion thing turned out to be irrelevant

This commit is contained in:
Андреев Григорий 2026-02-02 22:34:47 +03:00
parent 53dd36ab7c
commit f0634d08a1
3 changed files with 170 additions and 61 deletions

View File

@ -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};
}

View File

@ -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");

View File

@ -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,
});
}