12void Skeleton3D::ensureSorted() {
13 if (sk_.bones().empty()) {
14 sk_.topologicalSort();
18void Skeleton3D::ensureStateSize() {
20 const unsigned n =
static_cast<unsigned>(sk_.bones().size());
21 if (state_.size() <
n) {
23 state_.position_.resize(
n);
24 state_.orientation_.resize(
n);
25 state_.scale_.resize(
n);
26 state_.rotation_.resize(
n);
30::ik::bone3d *Skeleton3D::boneAt(
int boneId)
const {
31 if (boneId < 0)
return nullptr;
32 const auto &
bones = sk_.bones();
33 if (
static_cast<size_t>(boneId) >=
bones.size())
return nullptr;
34 return bones[
static_cast<size_t>(boneId)];
37void Skeleton3D::requireBone(
int boneId)
const {
38 if (!boneAt(boneId)) {
39 throw Exception(
"Skeleton3D: invalid bone id %d", boneId);
47 throw Exception(
"Skeleton3D.createBone: length must be > 0");
51 sk_.topologicalSort();
53 return static_cast<int>(child->
id);
57 const_cast<Skeleton3D *
>(
this)->ensureSorted();
58 return static_cast<int>(sk_.bones().size());
63 ::ik::bone3d *
b = boneAt(boneId);
64 if (!
b->parent)
return -1;
65 return static_cast<int>(
b->parent->id);
70 return static_cast<int>(boneAt(boneId)->children.size());
75 ::ik::bone3d *
b = boneAt(boneId);
76 if (childIndex < 0 ||
static_cast<size_t>(childIndex) >=
b->children.size()) {
77 throw Exception(
"Skeleton3D.getChild: childIndex out of range");
79 return static_cast<int>(
b->children[
static_cast<size_t>(childIndex)]->
id);
84 return boneAt(boneId)->length;
90 throw Exception(
"Skeleton3D.setLength: root length is always 0");
93 throw Exception(
"Skeleton3D.setLength: length must be > 0");
95 boneAt(boneId)->setLength(
length);
101 boneAt(boneId)->position(state_) = {
x,
y,
z};
106 const_cast<Skeleton3D *
>(
this)->ensureStateSize();
107 return boneAt(boneId)->position(state_)[0];
112 const_cast<Skeleton3D *
>(
this)->ensureStateSize();
113 return boneAt(boneId)->position(state_)[1];
118 const_cast<Skeleton3D *
>(
this)->ensureStateSize();
119 return boneAt(boneId)->position(state_)[2];
125 boneAt(boneId)->orientation(state_) = {
x,
y,
z};
130 const_cast<Skeleton3D *
>(
this)->ensureStateSize();
131 return boneAt(boneId)->orientation(state_)[0];
136 const_cast<Skeleton3D *
>(
this)->ensureStateSize();
137 return boneAt(boneId)->orientation(state_)[1];
142 const_cast<Skeleton3D *
>(
this)->ensureStateSize();
143 return boneAt(boneId)->orientation(state_)[2];
149 boneAt(boneId)->rotation(state_) = {
yaw,
pitch};
154 const_cast<Skeleton3D *
>(
this)->ensureStateSize();
155 return boneAt(boneId)->rotation(state_)[0];
160 const_cast<Skeleton3D *
>(
this)->ensureStateSize();
161 return boneAt(boneId)->rotation(state_)[1];
167 if (minYaw > maxYaw || minPitch > maxPitch) {
168 throw Exception(
"Skeleton3D.setConstraints: min must be <= max per axis");
170 boneAt(boneId)->setConstraints(::ik::vec2{minYaw, minPitch},
171 ::ik::vec2{maxYaw, maxPitch});
176 boneAt(boneId)->clearConstraints();
181 return boneAt(boneId)->constrained;
187 sk_.init_straight_pose(state_, ::ik::vec3{rootX, rootY, rootZ});
199 sk_.forward_kinematics(state_);
205 sk_.update_rotations(state_);
210 return sk_.total_length_to(boneAt(boneId));
std::vector< Bone > bones
EVENGINE_API_FOUNDATION public API.
Script-facing 3D skeleton + pose state (ik::skeleton3d + ik::ecs3d). Local angles are yaw/pitch in th...
void setPosition(int boneId, float x, float y, float z)
Sets the world-space bone position.
void setConstraints(int boneId, float minYaw, float minPitch, float maxYaw, float maxPitch)
Sets joint angle limits for the bone.
float getLength(int boneId) const
Rest length of the bone.
void setLength(int boneId, float length)
Sets the rest length of the bone.
void forwardKinematics()
Updates world positions from local rotations.
int getChildCount(int boneId) const
Number of direct children of boneId.
Skeleton3D()
Creates an empty 3D skeleton with a root bone.
void bind()
Binds the current pose as the rest pose.
int createBone(int parentId, float length=1.f)
Create a child bone under parentId; returns new bone id.
float getRotationPitch(int boneId) const
Local pitch in radians.
void setRotation(int boneId, float yaw, float pitch)
Sets local hinge/yaw-pitch rotation (radians).
void setOrientation(int boneId, float x, float y, float z)
Sets the bone forward orientation vector.
void initStraightPose(float rootX=0.f, float rootY=0.f, float rootZ=0.f)
Initializes a straight chain pose from the root.
float totalLengthTo(int boneId) const
Sum of bone lengths from root to boneId inclusive.
float getOrientationZ(int boneId) const
Orientation vector Z.
void clearConstraints(int boneId)
Removes joint limits from the bone.
float getZ(int boneId) const
World-space Z of the bone (or component).
float getY(int boneId) const
World-space Y of the bone (or component).
float getOrientationY(int boneId) const
Orientation vector Y.
bool hasConstraints(int boneId) const
True if the bone has joint limits.
void updateRotations()
Derives local rotations from world positions.
float getX(int boneId) const
World-space X of the bone (or component).
int getChild(int boneId, int childIndex) const
Child bone id at childIndex under boneId.
float getOrientationX(int boneId) const
Orientation vector X.
float getRotationYaw(int boneId) const
Local yaw in radians.
int getParent(int boneId) const
Parent bone id, or -1 for the root.
int getBoneCount() const
Number of bones currently in the skeleton.