Advertisement
Guest User

Inverse Kinematics

a guest
Jul 15th, 2018
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 4.94 KB | None | 0 0
  1. local chain = {};
  2. local plane = {};
  3.  
  4. -- table sum function
  5. function sum(t)
  6.     local s = 0;
  7.     for _, value in ipairs(t) do
  8.         s = s + value;
  9.     end;
  10.     return s;
  11. end;
  12.  
  13. local part = Instance.new("Part");
  14. part.Material = Enum.Material.Plastic;
  15. part.Anchored = true;
  16. part.CanCollide = false;
  17. part.BrickColor = BrickColor.Blue();
  18.  
  19. local parts = {};
  20. local model = Instance.new("Model", game.Workspace);
  21. function drawline(key, a, v)
  22.     if not parts[key] then parts[key] = part:Clone(); parts[key].Parent = model; end;
  23.     parts[key].Size = Vector3.new(.2, .2, v.magnitude);
  24.     parts[key].CFrame = CFrame.new(a + v/2, a + v);
  25.     return parts[key];
  26. end;
  27.  
  28. function chain.new(joints, target)
  29.     local self = setmetatable({}, {__index = chain});
  30.  
  31.     local lengths = {};
  32.     for i = 1, #joints - 1 do
  33.         lengths[i] = (joints[i] - joints[i+1]).magnitude;
  34.     end;   
  35.    
  36.     self.n = #joints;
  37.     self.tolerance = 0.1;
  38.     self.target = target;
  39.     self.joints = joints;
  40.     self.lengths = lengths;
  41.     self.origin = CFrame.new(joints[1]);
  42.     self.totallength = sum(lengths);
  43.    
  44.     -- rotation constraints
  45.     self.constrained = false;
  46.     self.left = math.rad(89);
  47.     self.right = math.rad(89);
  48.     self.up = math.rad(89);
  49.     self.down = math.rad(89);
  50.    
  51.     return self;
  52. end;
  53.  
  54. -- this is the hardest part of the code so I super commented it!
  55. function chain:constrain(calc, line, cf)
  56.     local scalar = calc:Dot(line) / line.magnitude;
  57.     local proj = scalar * line.unit;
  58.    
  59.     -- get axis that are closest
  60.     local ups = {cf:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Top)), cf:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Bottom))};
  61.     local rights = {cf:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Right)),  cf:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Left))};
  62.     table.sort(ups, function(a, b) return (a - calc).magnitude < (b - calc).magnitude end);
  63.     table.sort(rights, function(a, b) return (a - calc).magnitude < (b - calc).magnitude end);
  64.    
  65.     local upvec = ups[1];
  66.     local rightvec = rights[1];
  67.  
  68.     -- get the vector from the projection to the calculated vector
  69.     local adjust = calc - proj;
  70.     if scalar < 0 then
  71.         -- if we're below the cone flip the projection vector
  72.         proj = -proj;
  73.     end;
  74.    
  75.     -- get the 2D components
  76.     local xaspect = adjust:Dot(rightvec);
  77.     local yaspect = adjust:Dot(upvec);
  78.    
  79.     -- get the cross section of the cone
  80.     local left = -(proj.magnitude * math.tan(self.left));
  81.     local right = proj.magnitude * math.tan(self.right);
  82.     local up = proj.magnitude * math.tan(self.up);
  83.     local down = -(proj.magnitude * math.tan(self.down));
  84.    
  85.     -- find the quadrant
  86.     local xbound = xaspect >= 0 and right or left;
  87.     local ybound = yaspect >= 0 and up or down;
  88.    
  89.     local f = calc;
  90.     -- check if in 2D point lies in the ellipse
  91.     local ellipse = xaspect^2/xbound^2 + yaspect^2/ybound^2;
  92.     local inbounds = ellipse <= 1 and scalar >= 0;
  93.    
  94.     if not inbounds then
  95.         -- get the angle of our out of ellipse point
  96.         local a = math.atan2(yaspect, xaspect);
  97.         -- find nearest point
  98.         local x = xbound * math.cos(a);
  99.         local y = ybound * math.sin(a);
  100.         -- convert back to 3D
  101.         f = (proj + rightvec * x + upvec * y).unit * calc.magnitude;
  102.     end;
  103.    
  104.     -- return our final vector
  105.     return f;
  106. end;
  107.  
  108. function chain:backward()
  109.     -- backward reaching; set end effector as target
  110.     self.joints[self.n] = self.target;
  111.     for i = self.n - 1, 1, -1 do
  112.         local r = (self.joints[i+1] - self.joints[i]);
  113.         local l = self.lengths[i] / r.magnitude;
  114.         -- find new joint position
  115.         local pos = (1 - l) * self.joints[i+1] + l * self.joints[i];
  116.         self.joints[i] = pos;
  117.     end;
  118. end;
  119.  
  120. function chain:forward()
  121.     -- forward reaching; set root at initial position
  122.     self.joints[1] = self.origin.p;
  123.     local coneVec = (self.joints[2] - self.joints[1]).unit;
  124.     for i = 1, self.n - 1 do
  125.         local r = (self.joints[i+1] - self.joints[i]);
  126.         local l = self.lengths[i] / r.magnitude;
  127.         -- setup matrix
  128.         local cf = CFrame.new(self.joints[i], self.joints[i] + coneVec);
  129.         -- find new joint position
  130.         local pos = (1 - l) * self.joints[i] + l * self.joints[i+1];
  131.         local t = self:constrain(pos - self.joints[i], coneVec, cf);
  132.         self.joints[i+1] = self.constrained and self.joints[i] + t or pos;
  133.         coneVec = self.joints[i+1] - self.joints[i];
  134.     end;
  135. end;
  136.  
  137. function chain:solve()
  138.     local distance = (self.joints[1] - self.target).magnitude;
  139.     if distance > self.totallength then
  140.         -- target is out of reach
  141.         for i = 1, self.n - 1 do
  142.             local r = (self.target - self.joints[i]).magnitude;
  143.             local l = self.lengths[i] / r;
  144.             -- find new joint position
  145.             self.joints[i+1] = (1 - l) * self.joints[i] + l * self.target;
  146.         end;
  147.     else
  148.         -- target is in reach
  149.         local bcount = 0;
  150.         local dif = (self.joints[self.n] - self.target).magnitude;
  151.         while dif > self.tolerance do
  152.             self:backward();
  153.             self:forward();
  154.             dif = (self.joints[self.n] - self.target).magnitude;
  155.             -- break if it's taking too long so the game doesn't freeze
  156.             bcount = bcount + 1;
  157.             if bcount > 5 then break; end;
  158.         end;
  159.     end;
  160. end;
  161.  
  162. return chain;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement