mirror of
https://github.com/XProger/OpenLara.git
synced 2025-04-21 03:21:51 +02:00
add simple IK solver for legs and arms
This commit is contained in:
parent
b28c12d1d4
commit
97e8d1db91
@ -57,6 +57,7 @@ struct Camera : ICamera {
|
||||
int speed;
|
||||
bool smooth;
|
||||
|
||||
bool spectatorVR;
|
||||
bool spectator;
|
||||
vec3 specPos, specPosSmooth;
|
||||
vec3 specRot, specRotSmooth;
|
||||
@ -67,6 +68,7 @@ struct Camera : ICamera {
|
||||
this->owner = owner;
|
||||
reset();
|
||||
|
||||
spectatorVR = false;
|
||||
spectator = false;
|
||||
specTimer = 0.0f;
|
||||
}
|
||||
@ -559,6 +561,10 @@ struct Camera : ICamera {
|
||||
specTimer = 0.0f;
|
||||
}
|
||||
|
||||
if (!spectator && spectatorVR) {
|
||||
mViewInv = mat4(owner->mainLightPos, owner->pos, vec3(0, -1, 0));
|
||||
}
|
||||
|
||||
if (spectator) {
|
||||
vec2 L = specJoy.L;
|
||||
vec2 R = specJoy.R;
|
||||
|
@ -1432,8 +1432,11 @@ struct Controller {
|
||||
return;
|
||||
animation.getJoints(getMatrix(), -1, true, joints);
|
||||
jointsFrame = Core::stats.frame;
|
||||
updateIK();
|
||||
}
|
||||
|
||||
virtual void updateIK() {}
|
||||
|
||||
Basis& getJoint(int index) {
|
||||
updateJoints();
|
||||
|
||||
|
179
src/lara.h
179
src/lara.h
@ -44,6 +44,8 @@
|
||||
#define LARA_WADE_MAX_DEPTH 730.0f
|
||||
#define LARA_SWIM_MIN_DEPTH 512.0f
|
||||
|
||||
#define LARA_HEEL_HEIGHT 48.0f
|
||||
|
||||
#define LARA_MIN_SPECULAR 0.03f
|
||||
#define LARA_WET_SPECULAR 0.5f
|
||||
#define LARA_WET_TIMER (LARA_WET_SPECULAR / 16.0f) // 4 sec
|
||||
@ -79,7 +81,7 @@ struct Lara : Character {
|
||||
|
||||
ANIM_STAND = 11,
|
||||
|
||||
ANIM_LANDING = 24,
|
||||
ANIM_LANDING_HIGH = 24,
|
||||
|
||||
ANIM_CLIMB_JUMP = 26,
|
||||
|
||||
@ -107,6 +109,8 @@ struct Lara : Character {
|
||||
|
||||
ANIM_SLIDE_FORTH = 70,
|
||||
|
||||
ANIM_LANDING_LOW = 82,
|
||||
|
||||
ANIM_FALL_BACK = 93,
|
||||
|
||||
ANIM_HANG = 96,
|
||||
@ -326,6 +330,8 @@ struct Lara : Character {
|
||||
|
||||
bool dozy;
|
||||
bool canJump;
|
||||
bool useIK;
|
||||
bool useIKAim;
|
||||
|
||||
int32 networkInput;
|
||||
|
||||
@ -642,6 +648,9 @@ struct Lara : Character {
|
||||
} else
|
||||
animation.setAnim(ANIM_STAND);
|
||||
}
|
||||
|
||||
useIK = true;
|
||||
useIKAim = false;
|
||||
}
|
||||
|
||||
virtual ~Lara() {
|
||||
@ -947,6 +956,27 @@ struct Lara : Character {
|
||||
|| state == STATE_STEP_LEFT;
|
||||
}
|
||||
|
||||
bool canIKLegs() {
|
||||
return (animation.index != ANIM_RUN_ASCEND_LEFT
|
||||
&& animation.index != ANIM_RUN_ASCEND_RIGHT
|
||||
&& animation.index != ANIM_WALK_ASCEND_LEFT
|
||||
&& animation.index != ANIM_WALK_ASCEND_RIGHT
|
||||
&& animation.index != ANIM_WALK_DESCEND_RIGHT
|
||||
&& animation.index != ANIM_WALK_DESCEND_LEFT
|
||||
&& animation.index != ANIM_BACK_DESCEND_LEFT
|
||||
&& animation.index != ANIM_BACK_DESCEND_RIGHT)
|
||||
&& (state == STATE_WALK
|
||||
|| state == STATE_RUN
|
||||
|| state == STATE_STOP
|
||||
|| state == STATE_FAST_BACK
|
||||
|| state == STATE_TURN_RIGHT
|
||||
|| state == STATE_TURN_LEFT
|
||||
|| state == STATE_BACK
|
||||
|| state == STATE_FAST_TURN
|
||||
|| state == STATE_STEP_RIGHT
|
||||
|| state == STATE_STEP_LEFT);
|
||||
}
|
||||
|
||||
bool wpnReady() {
|
||||
return arms[0].anim != Weapon::Anim::PREPARE && arms[0].anim != Weapon::Anim::UNHOLSTER && arms[0].anim != Weapon::Anim::HOLSTER;
|
||||
}
|
||||
@ -1018,6 +1048,11 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
void wpnFire() {
|
||||
if (useIKAim) {
|
||||
doShot(Input::joy[0].down[jkA], Input::joy[1].down[jkA]);
|
||||
return;
|
||||
}
|
||||
|
||||
bool armShot[2] = { false, false };
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Arm &arm = arms[i];
|
||||
@ -1084,10 +1119,19 @@ struct Lara : Character {
|
||||
game->addMuzzleFlash(this, i ? LARA_LGUN_JOINT : LARA_RGUN_JOINT, i ? LARA_LGUN_OFFSET : LARA_RGUN_OFFSET, 1 + camera->cameraIndex);
|
||||
|
||||
// TODO: use new trace code
|
||||
int joint = wpnCurrent == TR::Entity::SHOTGUN ? 8 : (i ? 11 : 8);
|
||||
vec3 p = getJoint(joint).pos;
|
||||
vec3 d = arm->rotAbs * vec3(0, 0, 1);
|
||||
vec3 t = p + d * (24.0f * 1024.0f) + ((vec3(randf(), randf(), randf()) * 2.0f) - vec3(1.0f)) * 1024.0f;
|
||||
vec3 p, d, t;
|
||||
|
||||
if (useIKAim) {
|
||||
int joint = wpnCurrent == TR::Entity::SHOTGUN ? JOINT_ARM_R3 : (i ? JOINT_ARM_L3 : JOINT_ARM_R3);
|
||||
p = getJoint(joint).pos;
|
||||
d = getJoint(joint).rot * vec3(0, 1, 0);
|
||||
t = p + d * 15.0f * 1024.0f;
|
||||
} else {
|
||||
int joint = wpnCurrent == TR::Entity::SHOTGUN ? JOINT_ARM_R1 : (i ? JOINT_ARM_L1 : JOINT_ARM_R1);
|
||||
p = getJoint(joint).pos;
|
||||
d = arm->rotAbs * vec3(0, 0, 1);
|
||||
t = p + d * (15.0f * 1024.0f) + ((vec3(randf(), randf(), randf()) * 2.0f) - vec3(1.0f)) * 1024.0f;
|
||||
}
|
||||
|
||||
int room;
|
||||
vec3 hit = trace(getRoomIndex(), p, t, room, false);
|
||||
@ -1140,6 +1184,12 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
if (!emptyHands()) {
|
||||
|
||||
if (useIK) {
|
||||
//wpnFire();
|
||||
//return;
|
||||
}
|
||||
|
||||
bool isRifle = wpnCurrent == TR::Entity::SHOTGUN;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
@ -2397,7 +2447,7 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
if (state == STATE_FALL && health > 0.0f)
|
||||
animation.setAnim(ANIM_LANDING);
|
||||
animation.setAnim(ANIM_LANDING_HIGH);
|
||||
}
|
||||
}
|
||||
return STAND_GROUND;
|
||||
@ -3401,7 +3451,7 @@ struct Lara : Character {
|
||||
w *= TURN_FAST;
|
||||
else if (state == STATE_FAST_BACK)
|
||||
w *= TURN_FAST_BACK;
|
||||
else if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_WALK || (state == STATE_STOP && animation.index == ANIM_LANDING))
|
||||
else if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_WALK || (state == STATE_STOP && animation.index == ANIM_LANDING_HIGH))
|
||||
w *= TURN_NORMAL;
|
||||
else if (state == STATE_FORWARD_JUMP || state == STATE_BACK || state == STATE_WADE)
|
||||
w *= TURN_SLOW;
|
||||
@ -3514,7 +3564,7 @@ struct Lara : Character {
|
||||
vTilt *= 2.0f;
|
||||
vTilt *= rotFactor.y;
|
||||
bool VR = (Core::settings.detail.stereo == Core::Settings::STEREO_VR) && camera->firstPerson;
|
||||
updateTilt((input & WALK) == 0 && (state == STATE_RUN || (state == STATE_STOP && animation.index == ANIM_LANDING) || stand == STAND_UNDERWATER) && !VR, vTilt.x, vTilt.y);
|
||||
updateTilt((input & WALK) == 0 && (state == STATE_RUN || (state == STATE_STOP && animation.index == ANIM_LANDING_HIGH) || stand == STAND_UNDERWATER) && !VR, vTilt.x, vTilt.y);
|
||||
|
||||
collisionOffset = vec3(0.0f);
|
||||
|
||||
@ -3871,6 +3921,119 @@ struct Lara : Character {
|
||||
visibleMask ^= 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
void solveJointsArm(int j0, int j1, int j2) {
|
||||
int index = j0 == JOINT_ARM_R1 ? 0 : 1;
|
||||
|
||||
Basis &hJoint = getJoint(jointHead);
|
||||
vec3 hPos = hJoint.pos - hJoint.rot * vec3(0, 48, -24);
|
||||
|
||||
vec3 target = Input::hmd.controllers[index].getPos();
|
||||
target += hPos;
|
||||
|
||||
vec3 start = joints[j0].pos;
|
||||
vec3 middle = joints[j1].pos;
|
||||
vec3 end = target;
|
||||
|
||||
float length1 = (middle - start).length();
|
||||
float length2 = (joints[j2].pos - middle).length();
|
||||
|
||||
vec3 dir = end - start;
|
||||
float length = dir.length();
|
||||
if (length > length1 + length2) {
|
||||
end = start + dir * ((length1 + length2 - 0.1f) / length);
|
||||
}
|
||||
|
||||
vec3 down = vec3(0.0f, 1.0f, 0.0f);
|
||||
vec3 pole = start + (getDir().cross(down) * (index == 0 ? -1.0f : 1.0f) + down) * 1000.0f;
|
||||
|
||||
if (ikSolve3D(start, end, pole, length1, length2, middle))
|
||||
{
|
||||
joints[j0] = Basis(start, middle, joints[j0].rot * vec3(1.0f, 0.0f, 0.0f));
|
||||
joints[j1] = Basis(middle, end, joints[j1].rot * vec3(1.0f, 0.0f, 0.0f));
|
||||
joints[j2].pos = end;
|
||||
}
|
||||
|
||||
joints[j2].rot = Input::hmd.controllers[index].getRot();
|
||||
}
|
||||
|
||||
void solveJointsLeg(int j0, int j1, int j2, float footHeight) {
|
||||
vec3 start = joints[j0].pos;
|
||||
vec3 middle = joints[j1].pos;
|
||||
vec3 end = joints[j2].pos;
|
||||
|
||||
float length1 = (middle - start).length();
|
||||
float length2 = (end - middle).length();
|
||||
|
||||
if (end.y > footHeight) {
|
||||
end.y = footHeight;
|
||||
}
|
||||
|
||||
vec3 pole = middle + (middle - start + middle - end) * 1024.0f;
|
||||
|
||||
if (ikSolve3D(start, end, pole, length1, length2, middle)) {
|
||||
joints[j0] = Basis(start, middle, joints[j0].rot * vec3(1.0f, 0.0f, 0.0f));
|
||||
joints[j1] = Basis(middle, end, joints[j1].rot * vec3(1.0f, 0.0f, 0.0f));
|
||||
joints[j2].pos = end;
|
||||
}
|
||||
/*
|
||||
|
||||
vec3 pole = start + getDir() * 10000.0f;
|
||||
|
||||
if (ikSolve3D(start, end, pole, length1, length2, middle)) {
|
||||
float angle = middle.xy().angle();
|
||||
quat q(vec3(1, 0, 0), PI * 0.5f - angle);
|
||||
|
||||
joints[j0].rot = joints[j0].rot * q;
|
||||
|
||||
TR::Node *node = (TR::Node*)&level->nodesData[getModel()->node];
|
||||
TR::Node *t = node + j0;
|
||||
|
||||
joints[j1].rotate(q.conjugate());
|
||||
joints[j1].pos = joints[j0].pos + joints[j0].rot * vec3(float(t->x), float(t->y), float(t->z));
|
||||
|
||||
t = node + j1;
|
||||
joints[j2].pos = joints[j1].pos + joints[j1].rot * vec3(float(t->x), float(t->y), float(t->z));
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
float getFloorHeight(const vec3 &pos) {
|
||||
int16 roomIndex = getRoomIndex();
|
||||
TR::Room::Sector *sector = level->getSector(roomIndex, pos);
|
||||
|
||||
if (!sector) {
|
||||
return this->pos.y;
|
||||
}
|
||||
|
||||
return level->getFloor(sector, pos);// - LARA_HEEL_HEIGHT;
|
||||
}
|
||||
|
||||
virtual void updateIK() override {
|
||||
if (useIK && canIKLegs()) {
|
||||
float ikPivotOffset = -Input::hmd.head.getPos().y * ONE_METER;
|
||||
|
||||
float footHeightL = getFloorHeight(joints[JOINT_LEG_L3].pos);
|
||||
float footHeightR = getFloorHeight(joints[JOINT_LEG_R3].pos);
|
||||
|
||||
if (fabsf(footHeightL - footHeightR) < 256.0f) {
|
||||
ikPivotOffset += max(footHeightL, footHeightR) - pos.y;
|
||||
}
|
||||
|
||||
for (int i = 0; i < getModel()->mCount; i++) {
|
||||
joints[i].pos.y += ikPivotOffset;
|
||||
}
|
||||
|
||||
solveJointsLeg(JOINT_LEG_L1, JOINT_LEG_L2, JOINT_LEG_L3, footHeightL - LARA_HEEL_HEIGHT);
|
||||
solveJointsLeg(JOINT_LEG_R1, JOINT_LEG_R2, JOINT_LEG_R3, footHeightR - LARA_HEEL_HEIGHT);
|
||||
}
|
||||
|
||||
if (useIKAim) {
|
||||
solveJointsArm(JOINT_ARM_L1, JOINT_ARM_L2, JOINT_ARM_L3);
|
||||
solveJointsArm(JOINT_ARM_R1, JOINT_ARM_R2, JOINT_ARM_R3);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
26
src/level.h
26
src/level.h
@ -3252,8 +3252,34 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
if (Core::eye == 0.0f && Core::settings.detail.isStereo()) {
|
||||
Lara *lara = (Lara*)getLara(0);
|
||||
|
||||
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
|
||||
if (lara && lara->camera && !lara->camera->firstPerson) {
|
||||
lara->camera->changeView(true);
|
||||
}
|
||||
}
|
||||
|
||||
renderEye(-1, showUI, invBG);
|
||||
renderEye(+1, showUI, invBG);
|
||||
|
||||
#ifdef _OS_WIN
|
||||
uint8 stereo = Core::settings.detail.stereo;
|
||||
Core::settings.detail.stereo = Core::Settings::STEREO_OFF;
|
||||
|
||||
if (lara) {
|
||||
float dt = Core::deltaTime;
|
||||
Core::deltaTime = 1.0f;
|
||||
// lara->camera->spectatorVR = true;
|
||||
lara->camera->update();
|
||||
Core::deltaTime = dt;
|
||||
}
|
||||
renderEye(0, showUI, invBG);
|
||||
Core::settings.detail.stereo = stereo;
|
||||
if (lara) {
|
||||
lara->camera->spectatorVR = false;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
renderEye(int(Core::eye), showUI, invBG);
|
||||
}
|
||||
|
@ -1,18 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LocalDebuggerWorkingDirectory>..\..\..\bin\TR1_PSX</LocalDebuggerWorkingDirectory>
|
||||
<LocalDebuggerWorkingDirectory>C:\Projects\TR\TR1_PSX</LocalDebuggerWorkingDirectory>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
<LocalDebuggerCommandArguments>
|
||||
</LocalDebuggerCommandArguments>
|
||||
<LocalDebuggerCommandArguments>PSXDATA/LEVEL2.PSX</LocalDebuggerCommandArguments>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">
|
||||
<LocalDebuggerWorkingDirectory>..\..\..\bin\TR1_PSX</LocalDebuggerWorkingDirectory>
|
||||
<LocalDebuggerWorkingDirectory>C:\Projects\TR\TR1_PSX</LocalDebuggerWorkingDirectory>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LocalDebuggerWorkingDirectory>..\..\..\bin\TR1_PSX</LocalDebuggerWorkingDirectory>
|
||||
<LocalDebuggerWorkingDirectory>C:\Projects\TR\TR1_PSX</LocalDebuggerWorkingDirectory>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
<LocalDebuggerCommandArguments>PSXDATA/GYM.PSX</LocalDebuggerCommandArguments>
|
||||
<LocalDebuggerCommandArguments>PSXDATA/LEVEL2.PSX</LocalDebuggerCommandArguments>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -584,7 +584,7 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPara
|
||||
case WM_ACTIVATE :
|
||||
if (XInputEnable)
|
||||
XInputEnable(wParam != WA_INACTIVE);
|
||||
Input::reset();
|
||||
//Input::reset();
|
||||
break;
|
||||
case WM_SIZE:
|
||||
Core::width = LOWORD(lParam);
|
||||
@ -812,6 +812,8 @@ void vrUpdate() {
|
||||
|
||||
vr::VRCompositor()->WaitGetPoses(tPose, vr::k_unMaxTrackedDeviceCount, NULL, 0);
|
||||
|
||||
static bool forceUpdatePose = false;
|
||||
|
||||
for (int id = 0; id < vr::k_unMaxTrackedDeviceCount; id++) {
|
||||
vr::TrackedDevicePose_t &pose = tPose[id];
|
||||
|
||||
@ -825,8 +827,9 @@ void vrUpdate() {
|
||||
mat4 pR = convToMat4(hmd->GetProjectionMatrix(vr::Eye_Right, 8.0f, 45.0f * 1024.0f));
|
||||
|
||||
mat4 head = convToMat4(pose.mDeviceToAbsoluteTracking);
|
||||
if (Input::hmd.zero.x == INF) {
|
||||
if (Input::hmd.zero.x == INF || forceUpdatePose) {
|
||||
Input::hmd.zero = head.getPos();
|
||||
forceUpdatePose = false;
|
||||
}
|
||||
head.setPos(head.getPos() - Input::hmd.zero);
|
||||
|
||||
@ -850,32 +853,41 @@ void vrUpdate() {
|
||||
// continue;
|
||||
}
|
||||
|
||||
Input::setJoyDown(0, jkLeft, IS_DOWN(vr::k_EButton_DPad_Left));
|
||||
Input::setJoyDown(0, jkUp, IS_DOWN(vr::k_EButton_DPad_Up));
|
||||
Input::setJoyDown(0, jkRight, IS_DOWN(vr::k_EButton_DPad_Right));
|
||||
Input::setJoyDown(0, jkDown, IS_DOWN(vr::k_EButton_DPad_Down));
|
||||
|
||||
if (IS_DOWN(vr::k_EButton_Axis0)) {
|
||||
Input::setJoyPos(0, jkL, vec2(state.rAxis[0].x, -state.rAxis[0].y));
|
||||
if (IS_DOWN(vr::k_EButton_SteamVR_Touchpad)) {
|
||||
forceUpdatePose = true;
|
||||
}
|
||||
|
||||
Input::setJoyDown(0, jkA, IS_DOWN(vr::k_EButton_Axis1) ? (state.rAxis[1].x > 0.5) : false);
|
||||
Input::setJoyDown(0, jkY, IS_DOWN(vr::k_EButton_Grip));
|
||||
Input::setJoyDown(0, jkX, IS_DOWN(vr::k_EButton_ApplicationMenu));
|
||||
|
||||
// TODO
|
||||
int joyIndex;
|
||||
switch (hmd->GetControllerRoleForTrackedDeviceIndex(id)) {
|
||||
case vr::TrackedControllerRole_LeftHand :
|
||||
// TODO
|
||||
break;
|
||||
case vr::TrackedControllerRole_RightHand :
|
||||
// TODO
|
||||
break;
|
||||
default : ;
|
||||
case vr::TrackedControllerRole_RightHand : joyIndex = 0; break;
|
||||
case vr::TrackedControllerRole_LeftHand : joyIndex = 1; break;
|
||||
default : joyIndex = -1; break;
|
||||
}
|
||||
break;
|
||||
|
||||
if (joyIndex == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
Input::setJoyPos(joyIndex, jkL, vec2(state.rAxis[0].x, -state.rAxis[0].y));
|
||||
Input::setJoyDown(joyIndex, jkA, IS_DOWN(vr::k_EButton_Axis1) ? (state.rAxis[1].x > 0.25) : false);
|
||||
Input::setJoyDown(joyIndex, jkY, IS_DOWN(vr::k_EButton_Grip));
|
||||
Input::setJoyDown(joyIndex, jkX, IS_DOWN(vr::k_EButton_ApplicationMenu));
|
||||
|
||||
mat4 m = convToMat4(pose.mDeviceToAbsoluteTracking);
|
||||
m.setPos((m.getPos() - Input::hmd.zero) * ONE_METER);
|
||||
|
||||
mat4 scaleBasis(
|
||||
1, 0, 0, 0,
|
||||
0, -1, 0, 0,
|
||||
0, 0, -1, 0,
|
||||
0, 0, 0, 1);
|
||||
|
||||
m = scaleBasis * m * scaleBasis.inverse();
|
||||
Input::hmd.controllers[joyIndex] = m;
|
||||
|
||||
#undef IS_DOWN
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
103
src/utils.h
103
src/utils.h
@ -1057,6 +1057,22 @@ struct Basis {
|
||||
Basis(const quat &rot, const vec3 &pos) : rot(rot), pos(pos), w(1.0f) {}
|
||||
Basis(const mat4 &matrix) : rot(matrix.getRot()), pos(matrix.getPos()), w(1.0f) {}
|
||||
|
||||
Basis(const vec3 &start, const vec3 &end, const vec3 &right) {
|
||||
vec3 u = (end - start).normal();
|
||||
vec3 d = right.cross(u).normal();
|
||||
vec3 r = u.cross(d);
|
||||
|
||||
mat4 m;
|
||||
m.up() = vec4(u, 0.0f);
|
||||
m.dir() = vec4(d, 0.0f);
|
||||
m.right() = vec4(r, 0.0f);
|
||||
m.offset() = vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
identity();
|
||||
translate(start);
|
||||
rotate(m.getRot().normal());
|
||||
}
|
||||
|
||||
void identity() {
|
||||
rot = quat(0, 0, 0, 1);
|
||||
pos = vec3(0, 0, 0);
|
||||
@ -1094,6 +1110,93 @@ struct Basis {
|
||||
}
|
||||
};
|
||||
|
||||
int qSolve(float a, float b, float c, float &x1, float &x2) {
|
||||
if (fabsf(a) < EPS) {
|
||||
if (fabsf(b) < EPS) {
|
||||
return 0;
|
||||
}
|
||||
x1 = x2 = -c / b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
float d = b * b - 4.0f * a * c;
|
||||
if (d < 0.0f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float inv2a = 1.0f / (2.0f * a);
|
||||
|
||||
if (d < EPS) {
|
||||
x1 = x2 = -b * inv2a;
|
||||
return 1;
|
||||
}
|
||||
|
||||
d = sqrtf(d);
|
||||
x1 = (-b - d) * inv2a;
|
||||
x2 = (-b + d) * inv2a;
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool ikSolve2D(const vec2 &end, float length1, float length2, vec2 &middle) {
|
||||
float length = end.length();
|
||||
|
||||
if (length > length1 + length2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool flipXY = end.x < end.y;
|
||||
|
||||
float a, b;
|
||||
|
||||
if (flipXY) {
|
||||
a = end.y;
|
||||
b = end.x;
|
||||
} else {
|
||||
a = end.x;
|
||||
b = end.y;
|
||||
}
|
||||
ASSERT(fabsf(a) > EPS);
|
||||
|
||||
float m = (SQR(length1) - SQR(length2) + SQR(a) + SQR(b)) / (2.0f * a);
|
||||
float n = b / a;
|
||||
|
||||
float y1, y2;
|
||||
|
||||
if (qSolve(1.0f + SQR(n), -2.0f * m * n, SQR(m) - SQR(length1), y1, y2)) {
|
||||
|
||||
if (flipXY) {
|
||||
middle.x = y2;
|
||||
middle.y = m - n * y2;
|
||||
} else {
|
||||
middle.y = y2;
|
||||
middle.x = m - n * y2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
middle = end * (length1 / length);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ikSolve3D(const vec3 &start, const vec3 &end, const vec3 &pole, float length1, float length2, vec3 &middle) {
|
||||
vec3 d = (end - start).normal();
|
||||
vec3 n = d.cross(pole - start).normal();
|
||||
|
||||
mat4 m;
|
||||
m.right() = vec4(n.cross(d), 0.0f);
|
||||
m.up() = vec4(d, 0.0f);
|
||||
m.dir() = vec4(n, 0.0f);
|
||||
m.offset() = vec4(start, 1.0f);
|
||||
|
||||
bool res = ikSolve2D((m.inverse() * end).xy(), length1, length2, middle.xy());
|
||||
|
||||
middle.z = 0.0f;
|
||||
middle = m * middle;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct ubyte2 {
|
||||
uint8 x, y;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user