Files
ReSkate/Extension/Skater/Test/carry_tests.cpp
T
AyeZeeBB 118c489fdb Plugins: what the Filmer needs as a plugin: carry, who sees a pose, a step in the camera cycle
PR 343's Filmer becomes a Luau plugin mod (kept in contrib/plugins/filmer) on general APIs instead of
its own C++:

- reskate.skater.carry: something held out in one or both hands and aimed, the body crouched (feet
  kept) and hunched over it, turned on the spot after it (Extension/Skater/carry.cpp, from the PR's
  filmer_hold.cpp, with tests)
- shown = "everyone" | "others" | "me" on skater.pose, reach and carry: the pose sent to the other
  players and the one drawn here are split when asked
- reskate.camera.cycle and the camera_cycle event: a plugin's camera as the last step of the camera
  height button's cycle, after first person
- reskate.session.nametags(false) holds the names over the other players off
- board.hide(bone, {others = true}) hides it in the board the others are sent too (a sent-pose filter)
- outfit.set takes hand wear slots; input.bind takes an action with no key of its own
2026-10-22 18:46:49 -05:00

179 lines
7.6 KiB
C++

// Something carried apart from the game: held out in front of the chest when it points level, higher
// when it points up, turned the way it is aimed when worn on the hand, the other hand under it, the
// hips lowered with the feet kept where they were, the body turned, and a broken pose left alone.
#include "Extension/Skater/carry.h"
#include <algorithm>
#include <cmath>
#include <iostream>
#include <numbers>
#include <stdexcept>
#include <vector>
using namespace dingosdk;
using namespace dingosdk::carry;
namespace {
void check(bool value, const char* message) { if (!value) throw std::runtime_error(message); }
float distance(const Vec3& a, const Vec3& b) {
return std::sqrt((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) + (a[2] - b[2]) * (a[2] - b[2]));
}
bool same_turn(const Quat& a, const Quat& b) { return std::abs(a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]) > 0.999f; }
// A skater standing, facing +Z (its right at -X), arms hanging out to the sides: upper arms 0.3 m,
// forearms 0.28 m, thighs and shins 0.45 m, the knees a little forward.
constexpr float upper_length = 0.3f, fore_length = 0.28f;
std::vector<Joint> standing() {
std::vector<Joint> pose(395);
pose[0].position = {4, 0, -2};
pose[1].position = {0, 0.95f, 0};
for (const std::size_t j : {42u, 43u, 44u, 45u}) pose[j].position = {0, 0.1f, 0};
pose[101].position = {0, 0.1f, 0};
pose[102].position = {0, 0.05f, 0};
pose[103].position = {0, 0.1f, 0};
for (const auto hand : {Hand::right, Hand::left}) {
const auto joints = arm_reach::chain(hand);
const float x = hand == Hand::right ? -1.0f : 1.0f;
pose[joints[7]].position = {0.15f * x, 0, 0};
pose[joints[8]].position = {0.05f * x, 0, 0};
pose[joints[9]].position = {0, -upper_length, 0};
pose[joints[10]].position = {0, -fore_length, 0};
}
for (const auto& leg : {right_leg, left_leg}) {
const float x = leg[3] == 8 ? -1.0f : 1.0f;
pose[leg[3]].position = {0.1f * x, 0, 0};
pose[leg[4]].position = {0, -0.45f, 0.03f};
pose[leg[5]].position = {0, -0.45f, -0.03f};
}
return pose;
}
std::array<Placed, 11> arm(const std::vector<Joint>& pose, Hand hand) {
std::array<Placed, 11> w{};
check(arm_reach::compose(pose, arm_reach::chain(hand), w), "The arm composes");
return w;
}
Placed placed(const std::vector<Joint>& pose, std::span<const std::uint16_t> path) {
std::array<Placed, 11> w{};
check(arm_reach::compose(pose, path, w), "The joints compose");
return w[path.size() - 1];
}
// Pointing at +Z, tipped up by `up` radians.
Quat aimed(float up) { return anim_studio::multiply(anim_studio::axis_angle({0, 1, 0}, std::numbers::pi_v<float>), anim_studio::axis_angle({1, 0, 0}, up)); }
void held_level_in_front_of_the_chest() {
auto pose = standing();
const Vec3 shoulder = arm(pose, Hand::right)[8].position;
Goal goal;
goal.front = {shoulder[0], shoulder[1], shoulder[2] + 2.0f}; // asked for far off: held where a person holds it
goal.aim = aimed(0);
goal.front_from_grip = {0, -0.143f, -0.248f};
goal.worn = Grip{};
const auto held = carry::carry(pose, goal);
check(held.valid, "Carried");
const auto w = arm(pose, Hand::right);
check(held.front[2] - w[8].position[2] > 0.25f, "out in front of the shoulder");
check(held.front[1] < w[8].position[1], "and below it");
check(std::abs(distance(w[8].position, w[9].position) - upper_length) < 1e-4f, "The upper arm keeps its length");
check(std::abs(distance(w[9].position, w[10].position) - fore_length) < 1e-4f, "and the forearm its length");
check(same_turn(held.item.rotation, goal.aim), "Worn, it points the way it is aimed");
}
void aimed_up_it_comes_up() {
auto level = standing(), up = standing();
Goal goal;
goal.front = {4, 1.4f, 0};
goal.front_from_grip = {0, -0.143f, -0.248f};
goal.aim = aimed(0);
const auto low = carry::carry(level, goal);
goal.aim = aimed(1.2f);
const auto high = carry::carry(up, goal);
check(low.valid && high.valid, "Carried both ways");
check(high.front[1] > low.front[1] + 0.2f, "Aimed up, it is held higher");
}
void the_other_hand_holds_it_too() {
auto pose = standing();
const auto before = arm(pose, Hand::left)[10].position;
Goal goal;
goal.front = {4, 1.3f, 0.5f};
goal.aim = aimed(0);
goal.both = true;
const auto held = carry::carry(pose, goal);
check(held.valid, "Carried in both hands");
const auto left = arm(pose, Hand::left)[10].position;
check(distance(left, before) > 0.2f, "The other hand comes up to it");
check(distance(left, held.item.position) < 0.3f, "and holds it");
}
void the_feet_stay_as_the_hips_go_down() {
auto pose = standing();
const auto right_foot = placed(pose, right_leg).position, left_foot = placed(pose, left_leg).position;
const float hips = placed(pose, std::span<const std::uint16_t>(right_leg).first(3)).position[1];
Goal goal;
goal.front = {4, 1.2f, 0.5f};
goal.aim = aimed(-0.3f);
goal.crouch = 0.2f;
goal.hunch = 0.8f;
check(carry::carry(pose, goal).valid, "Carried, crouched");
check(std::abs(placed(pose, std::span<const std::uint16_t>(right_leg).first(3)).position[1] - (hips - 0.2f)) < 1e-3f, "The hips go down");
check(distance(placed(pose, right_leg).position, right_foot) < 2e-3f, "the right foot stays where it was");
check(distance(placed(pose, left_leg).position, left_foot) < 2e-3f, "and the left");
const Vec3 head = placed(pose, head_chain).position;
check(head[2] > -2.0f + 0.05f, "The back curls over toward it");
}
void the_body_turns() {
auto pose = standing();
Goal goal;
goal.front = {4, 1.3f, 0.5f};
goal.aim = aimed(0);
goal.turn = std::numbers::pi_v<float> / 2;
check(carry::carry(pose, goal).valid, "Carried, turned");
const auto facing = body_facing(pose);
check(std::abs(facing[0]) > 0.99f, "The body turns a quarter round");
}
void no_weight_and_a_broken_pose() {
auto pose = standing();
const auto before = pose;
Goal goal;
goal.front = {4, 1.3f, 0.5f};
goal.aim = aimed(0);
goal.crouch = 0.3f;
goal.weight = 0;
check(carry::carry(pose, goal).valid, "Carried with no weight");
for (std::size_t i = 0; i < pose.size(); ++i)
check(same_turn(pose[i].rotation, before[i].rotation) && distance(pose[i].position, before[i].position) < 1e-5f, "leaves the pose as it was");
auto again = standing();
goal.weight = 1;
goal.both = true;
goal.hunch = 0.6f;
goal.turn = 0.5f;
check(carry::carry(again, goal).valid, "Carried with everything");
for (std::size_t i = 0; i < again.size(); ++i)
if (std::find(written.begin(), written.end(), i) == written.end())
check(same_turn(again[i].rotation, before[i].rotation) && distance(again[i].position, before[i].position) < 1e-5f,
"A carry writes only the joints it says it writes");
auto broken = standing();
broken[48].rotation[0] = NAN;
check(!carry::carry(broken, goal).valid, "A broken arm carries nothing");
goal.front = {NAN, 0, 0};
auto fine = standing();
check(!carry::carry(fine, goal).valid, "A point that is not a number is refused");
}
} // namespace
int main() {
try {
held_level_in_front_of_the_chest();
aimed_up_it_comes_up();
the_other_hand_holds_it_too();
the_feet_stay_as_the_hips_go_down();
the_body_turns();
no_weight_and_a_broken_pose();
} catch (const std::exception& error) {
std::cerr << "FAILED: " << error.what() << '\n';
return 1;
}
std::cout << "carry: all passed\n";
return 0;
}