public override void CompleteResolve() { base.CompleteResolve(); ActorBone bone = m_Parent as ActorBone; bone.m_Jelly = this; // Get jellies. IList <ActorNode> children = bone.Children; if (children == null) { return; } m_Bones = new List <ActorJellyBone>(); foreach (ActorNode child in children) { if (child is ActorJellyBone) { m_Bones.Add(child as ActorJellyBone); } } }
public override ActorComponent MakeInstance(Actor resetActor) { ActorBone instanceNode = new ActorBone(); instanceNode.Copy(this, resetActor); return(instanceNode); }
public static ActorBone Read(Actor actor, BinaryReader reader, ActorBone node = null) { if (node == null) { node = new ActorBone(); } ActorBoneBase.Read(actor, reader, node); return(node); }
private void UpdateJellies() { if (m_Bones == null) { return; } ActorBone bone = m_Parent as ActorBone; // We are in local bone space. Vec2D tipPosition = new Vec2D(bone.Length, 0.0f); if (FuzzyEquals(m_CachedTip, tipPosition) && FuzzyEquals(m_CachedOut, m_OutPoint) && FuzzyEquals(m_CachedIn, m_InPoint) && m_CachedScaleIn == m_ScaleIn && m_CachedScaleOut == m_ScaleOut) { return; } Vec2D.Copy(m_CachedTip, tipPosition); Vec2D.Copy(m_CachedOut, m_OutPoint); Vec2D.Copy(m_CachedIn, m_InPoint); m_CachedScaleIn = m_ScaleIn; m_CachedScaleOut = m_ScaleOut; Vec2D q0 = new Vec2D(); Vec2D q1 = m_InPoint; Vec2D q2 = m_OutPoint; Vec2D q3 = tipPosition; ForwardDiffBezier(q0[0], q1[0], q2[0], q3[0], m_JellyPoints, JellyMax, 0); ForwardDiffBezier(q0[1], q1[1], q2[1], q3[1], m_JellyPoints, JellyMax, 1); IList <Vec2D> normalizedPoints = NormalizeCurve(m_JellyPoints, m_Bones.Count); Vec2D lastPoint = m_JellyPoints[0]; float scale = m_ScaleIn; float scaleInc = (m_ScaleOut - m_ScaleIn) / (m_Bones.Count - 1); for (int i = 0; i < normalizedPoints.Count; i++) { ActorJellyBone jelly = m_Bones[i]; Vec2D p = normalizedPoints[i]; jelly.Translation = lastPoint; jelly.Length = Vec2D.Distance(p, lastPoint); jelly.ScaleY = scale; scale += scaleInc; Vec2D diff = Vec2D.Subtract(new Vec2D(), p, lastPoint); jelly.Rotation = (float)Math.Atan2(diff[1], diff[0]); lastPoint = p; } }
public override void CompleteResolve() { base.CompleteResolve(); if (m_Children == null) { return; } foreach (ActorNode node in m_Children) { if (node is ActorBone) { m_FirstBone = node as ActorBone; return; } } }
void ConstrainRotation(BoneChain fk, float rotation) { ActorBone bone = fk.m_Bone; Mat2D parentWorld = bone.Parent.WorldTransform; Mat2D transform = bone.Transform; TransformComponents c = fk.m_TransformComponents; if (rotation == 0.0f) { Mat2D.Identity(transform); } else { Mat2D.FromRotation(transform, rotation); } // Translate transform[4] = c.X; transform[5] = c.Y; // Scale float scaleX = c.ScaleX; float scaleY = c.ScaleY; transform[0] *= scaleX; transform[1] *= scaleX; transform[2] *= scaleY; transform[3] *= scaleY; // Skew float skew = c.Skew; if (skew != 0.0) { transform[2] = transform[0] * skew + transform[2]; transform[3] = transform[1] * skew + transform[3]; } Mat2D.Multiply(bone.WorldTransform, parentWorld, transform); }
private void ReadComponentsBlock(BlockReader block) { int componentCount = block.ReadUInt16(); m_Components = new ActorComponent[componentCount + 1]; m_Components[0] = m_Root; // Guaranteed from the exporter to be in index order. BlockReader nodeBlock = null; int componentIndex = 1; m_NodeCount = 1; while ((nodeBlock = block.ReadNextBlock()) != null) { ActorComponent component = null; if (Enum.IsDefined(typeof(BlockTypes), nodeBlock.BlockType)) { BlockTypes type = (BlockTypes)nodeBlock.BlockType; switch (type) { case BlockTypes.ActorNode: component = ActorNode.Read(this, nodeBlock); break; case BlockTypes.ActorBone: component = ActorBone.Read(this, nodeBlock); break; case BlockTypes.ActorRootBone: component = ActorRootBone.Read(this, nodeBlock); break; case BlockTypes.ActorImage: m_ImageNodeCount++; component = ActorImage.Read(this, nodeBlock, makeImageNode()); if ((component as ActorImage).TextureIndex > m_MaxTextureIndex) { m_MaxTextureIndex = (component as ActorImage).TextureIndex; } break; case BlockTypes.ActorIKTarget: component = ActorIKTarget.Read(this, nodeBlock); break; case BlockTypes.ActorEvent: component = ActorEvent.Read(this, nodeBlock); break; case BlockTypes.CustomIntProperty: component = CustomIntProperty.Read(this, nodeBlock); break; case BlockTypes.CustomFloatProperty: component = CustomFloatProperty.Read(this, nodeBlock); break; case BlockTypes.CustomStringProperty: component = CustomStringProperty.Read(this, nodeBlock); break; case BlockTypes.CustomBooleanProperty: component = CustomBooleanProperty.Read(this, nodeBlock); break; case BlockTypes.ActorColliderRectangle: component = ActorColliderRectangle.Read(this, nodeBlock); break; case BlockTypes.ActorColliderTriangle: component = ActorColliderTriangle.Read(this, nodeBlock); break; case BlockTypes.ActorColliderCircle: component = ActorColliderCircle.Read(this, nodeBlock); break; case BlockTypes.ActorColliderPolygon: component = ActorColliderPolygon.Read(this, nodeBlock); break; case BlockTypes.ActorColliderLine: component = ActorColliderLine.Read(this, nodeBlock); break; case BlockTypes.ActorNodeSolo: component = ActorNodeSolo.Read(this, nodeBlock); break; case BlockTypes.ActorJellyBone: component = ActorJellyBone.Read(this, nodeBlock); break; case BlockTypes.JellyComponent: component = JellyComponent.Read(this, nodeBlock); break; case BlockTypes.ActorIKConstraint: component = ActorIKConstraint.Read(this, nodeBlock); break; case BlockTypes.ActorDistanceConstraint: component = ActorDistanceConstraint.Read(this, nodeBlock); break; case BlockTypes.ActorTranslationConstraint: component = ActorTranslationConstraint.Read(this, nodeBlock); break; case BlockTypes.ActorScaleConstraint: component = ActorScaleConstraint.Read(this, nodeBlock); break; case BlockTypes.ActorRotationConstraint: component = ActorRotationConstraint.Read(this, nodeBlock); break; case BlockTypes.ActorTransformConstraint: component = ActorTransformConstraint.Read(this, nodeBlock); break; } } if (component is ActorNode) { m_NodeCount++; } m_Components[componentIndex] = component; if (component != null) { component.Idx = (ushort)(componentIndex); } componentIndex++; } m_ImageNodes = new ActorImage[m_ImageNodeCount]; m_Nodes = new ActorNode[m_NodeCount]; m_Nodes[0] = m_Root; // Resolve nodes. int imgIdx = 0; int anIdx = 0; ActorComponent[] components = m_Components; for (int i = 1; i <= componentCount; i++) { ActorComponent c = components[i]; // Nodes can be null if we read from a file version that contained nodes that we don't interpret in this runtime. if (c != null) { c.ResolveComponentIndices(components); } ActorImage ain = c as ActorImage; if (ain != null) { m_ImageNodes[imgIdx++] = ain; } ActorNode an = c as ActorNode; if (an != null) { m_Nodes[anIdx++] = an; } } for (int i = 1; i <= componentCount; i++) { ActorComponent c = components[i]; if (c != null) { c.CompleteResolve(); } } SortDependencies(); }
public override void Update(byte dirt) { ActorBone bone = m_Parent as ActorBone; ActorBone parentBone = bone.Parent as ActorBone; JellyComponent parentBoneJelly = parentBone == null ? null : parentBone.m_Jelly; Mat2D inverseWorld = new Mat2D(); if (!Mat2D.Invert(inverseWorld, bone.WorldTransform)) { return; } if (m_InTarget != null) { Vec2D translation = m_InTarget.GetWorldTranslation(new Vec2D()); Vec2D.TransformMat2D(m_InPoint, translation, inverseWorld); Vec2D.Normalize(m_InDirection, m_InPoint); } else if (parentBone != null) { if (parentBone.FirstBone == bone && parentBoneJelly != null && parentBoneJelly.m_OutTarget != null) { Vec2D translation = parentBoneJelly.m_OutTarget.GetWorldTranslation(new Vec2D()); Vec2D localParentOut = Vec2D.TransformMat2D(new Vec2D(), translation, inverseWorld); Vec2D.Normalize(localParentOut, localParentOut); Vec2D.Negate(m_InDirection, localParentOut); } else { Vec2D d1 = new Vec2D(1.0f, 0.0f); Vec2D d2 = new Vec2D(1.0f, 0.0f); Vec2D.TransformMat2(d1, d1, parentBone.WorldTransform); Vec2D.TransformMat2(d2, d2, bone.WorldTransform); Vec2D sum = Vec2D.Add(new Vec2D(), d1, d2); Vec2D.TransformMat2(m_InDirection, sum, inverseWorld); Vec2D.Normalize(m_InDirection, m_InDirection); } m_InPoint[0] = m_InDirection[0] * m_EaseIn * bone.Length * CurveConstant; m_InPoint[1] = m_InDirection[1] * m_EaseIn * bone.Length * CurveConstant; } else { m_InDirection[0] = 1.0f; m_InDirection[1] = 0.0f; m_InPoint[0] = m_InDirection[0] * m_EaseIn * bone.Length * CurveConstant; } if (m_OutTarget != null) { Vec2D translation = m_OutTarget.GetWorldTranslation(new Vec2D()); Vec2D.TransformMat2D(m_OutPoint, translation, inverseWorld); Vec2D tip = new Vec2D(bone.Length, 0.0f); Vec2D.Subtract(m_OutDirection, m_OutPoint, tip); Vec2D.Normalize(m_OutDirection, m_OutDirection); } else if (bone.FirstBone != null) { ActorBone firstBone = bone.FirstBone; JellyComponent firstBoneJelly = firstBone.m_Jelly; if (firstBoneJelly != null && firstBoneJelly.m_InTarget != null) { Vec2D translation = firstBoneJelly.m_InTarget.GetWorldTranslation(new Vec2D()); Vec2D worldChildInDir = Vec2D.Subtract(new Vec2D(), firstBone.GetWorldTranslation(new Vec2D()), translation); Vec2D.TransformMat2(m_OutDirection, worldChildInDir, inverseWorld); } else { Vec2D d1 = new Vec2D(1.0f, 0.0f); Vec2D d2 = new Vec2D(1.0f, 0.0f); Vec2D.TransformMat2(d1, d1, firstBone.WorldTransform); Vec2D.TransformMat2(d2, d2, bone.WorldTransform); Vec2D sum = Vec2D.Add(new Vec2D(), d1, d2); Vec2D.Negate(sum, sum); Vec2D.TransformMat2(m_OutDirection, sum, inverseWorld); Vec2D.Normalize(m_OutDirection, m_OutDirection); } Vec2D.Normalize(m_OutDirection, m_OutDirection); Vec2D scaledOut = Vec2D.Scale(new Vec2D(), m_OutDirection, m_EaseOut * bone.Length * CurveConstant); m_OutPoint[0] = bone.Length; m_OutPoint[1] = 0.0f; Vec2D.Add(m_OutPoint, m_OutPoint, scaledOut); } else { m_OutDirection[0] = -1.0f; m_OutDirection[1] = 0.0f; Vec2D scaledOut = Vec2D.Scale(new Vec2D(), m_OutDirection, m_EaseOut * bone.Length * CurveConstant); m_OutPoint[0] = bone.Length; m_OutPoint[1] = 0.0f; Vec2D.Add(m_OutPoint, m_OutPoint, scaledOut); } UpdateJellies(); }
public override void CompleteResolve() { base.CompleteResolve(); if (m_InfluencedBones == null || m_InfluencedBones.Length == 0) { return; } // Initialize solver. ActorBone start = m_InfluencedBones[0].m_Bone; ActorBone end = m_InfluencedBones[m_InfluencedBones.Length - 1].m_Bone; int count = 0; while (end != null && end != start.Parent) { count++; end = end.Parent as ActorBone; } bool allIn = count < 3; end = m_InfluencedBones[m_InfluencedBones.Length - 1].m_Bone; m_FKChain = new BoneChain[count]; int idx = count - 1; while (end != null && end != start.Parent) { BoneChain bc = new BoneChain(); bc.m_Bone = end; bc.m_Angle = 0.0f; bc.m_Included = allIn; bc.m_TransformComponents = new TransformComponents(); bc.m_ParentWorldInverse = new Mat2D(); bc.m_Index = idx; m_FKChain[idx--] = bc; end = end.Parent as ActorBone; } // Make sure bones are good. m_BoneData = new List <BoneChain>(); foreach (InfluencedBone bone in m_InfluencedBones) { BoneChain item = Array.Find(m_FKChain, chainItem => chainItem.m_Bone == bone.m_Bone); if (item == null) { Console.WriteLine("Bone not in chain: " + bone.m_Bone.Name); continue; } m_BoneData.Add(item); } if (!allIn) { // Influenced bones are in the IK chain. for (int i = 0; i < m_BoneData.Count - 1; i++) { BoneChain item = m_BoneData[i]; item.m_Included = true; m_FKChain[item.m_Index + 1].m_Included = true; } } // Finally mark dependencies. foreach (InfluencedBone bone in m_InfluencedBones) { // Don't mark dependency on parent as ActorComponent already does this. if (bone.m_Bone == m_Parent) { continue; } m_Actor.AddDependency(this, bone.m_Bone); } if (m_Target != null) { m_Actor.AddDependency(this, m_Target); } // All the first level children of the influenced bones should depend on the final bone. BoneChain tip = m_FKChain[m_FKChain.Length - 1]; foreach (BoneChain fk in m_FKChain) { if (fk == tip) { continue; } ActorBone bone = fk.m_Bone; foreach (ActorNode node in bone.Children) { BoneChain item = Array.Find(m_FKChain, chainItem => chainItem.m_Bone == node); if (item != null) { // node is in the FK chain. continue; } m_Actor.AddDependency(node, tip.m_Bone); } } }
void Solve2(BoneChain fk1, BoneChain fk2, Vec2D worldTargetTranslation) { ActorBone b1 = fk1.m_Bone; ActorBone b2 = fk2.m_Bone; BoneChain firstChild = m_FKChain[fk1.m_Index + 1]; Mat2D iworld = fk1.m_ParentWorldInverse; Vec2D pA = b1.GetWorldTranslation(new Vec2D()); Vec2D pC = firstChild.m_Bone.GetWorldTranslation(new Vec2D()); Vec2D pB = b2.GetTipWorldTranslation(new Vec2D());; Vec2D pBT = new Vec2D(worldTargetTranslation); pA = Vec2D.TransformMat2D(pA, pA, iworld); pC = Vec2D.TransformMat2D(pC, pC, iworld); pB = Vec2D.TransformMat2D(pB, pB, iworld); pBT = Vec2D.TransformMat2D(pBT, pBT, iworld); // http://mathworld.wolfram.com/LawofCosines.html Vec2D av = Vec2D.Subtract(new Vec2D(), pB, pC); float a = Vec2D.Length(av); Vec2D bv = Vec2D.Subtract(new Vec2D(), pC, pA); float b = Vec2D.Length(bv); Vec2D cv = Vec2D.Subtract(new Vec2D(), pBT, pA); float c = Vec2D.Length(cv); float A = (float)Math.Acos(Math.Max(-1, Math.Min(1, (-a * a + b * b + c * c) / (2 * b * c)))); float C = (float)Math.Acos(Math.Max(-1, Math.Min(1, (a * a + b * b - c * c) / (2 * a * b)))); float r1, r2; if (b2.Parent != b1) { BoneChain secondChild = m_FKChain[fk1.m_Index + 2]; Mat2D secondChildWorldInverse = secondChild.m_ParentWorldInverse; pC = firstChild.m_Bone.GetWorldTranslation(new Vec2D()); pB = b2.GetTipWorldTranslation(new Vec2D()); Vec2D avec = Vec2D.Subtract(new Vec2D(), pB, pC); Vec2D avLocal = Vec2D.TransformMat2(new Vec2D(), avec, secondChildWorldInverse); float angleCorrection = (float)-Math.Atan2(avLocal[1], avLocal[0]); if (m_InvertDirection) { r1 = (float)Math.Atan2(cv[1], cv[0]) - A; r2 = -C + PI + angleCorrection; } else { r1 = A + (float)Math.Atan2(cv[1], cv[0]); r2 = C - PI + angleCorrection; } } else if (m_InvertDirection) { r1 = (float)Math.Atan2(cv[1], cv[0]) - A; r2 = -C + PI; } else { r1 = A + (float)Math.Atan2(cv[1], cv[0]); r2 = C - PI; } ConstrainRotation(fk1, r1); ConstrainRotation(firstChild, r2); if (firstChild != fk2) { ActorBone bone = fk2.m_Bone; Mat2D.Multiply(bone.WorldTransform, bone.Parent.WorldTransform, bone.Transform); } // Simple storage, need this for interpolation. fk1.m_Angle = r1; firstChild.m_Angle = r2; }
public override void Constrain(ActorNode node) { ActorNode target = m_Target as ActorNode; if (target == null) { return; } Vec2D worldTargetTranslation = new Vec2D(); target.GetWorldTranslation(worldTargetTranslation); if (m_InfluencedBones.Length == 0) { return; } // Decompose the chain. foreach (BoneChain item in m_FKChain) { ActorBone bone = item.m_Bone; Mat2D parentWorld = bone.Parent.WorldTransform; Mat2D.Invert(item.m_ParentWorldInverse, parentWorld); Mat2D.Multiply(bone.Transform, item.m_ParentWorldInverse, bone.WorldTransform); Mat2D.Decompose(bone.Transform, item.m_TransformComponents); } int count = m_BoneData.Count; if (count == 1) { Solve1(m_BoneData[0], worldTargetTranslation); } else if (count == 2) { Solve2(m_BoneData[0], m_BoneData[1], worldTargetTranslation); } else { BoneChain tip = m_BoneData[count - 1]; for (int i = 0; i < count - 1; i++) { BoneChain item = m_BoneData[i]; Solve2(item, tip, worldTargetTranslation); for (int j = item.m_Index + 1; j < m_FKChain.Length - 1; j++) { BoneChain fk = m_FKChain[j]; Mat2D.Invert(fk.m_ParentWorldInverse, fk.m_Bone.Parent.WorldTransform); } } } // At the end, mix the FK angle with the IK angle by strength if (m_Strength != 1.0) { foreach (BoneChain fk in m_FKChain) { if (!fk.m_Included) { ActorBone bone = fk.m_Bone; Mat2D.Multiply(bone.WorldTransform, bone.Parent.WorldTransform, bone.Transform); continue; } float fromAngle = fk.m_TransformComponents.Rotation % PI2; float toAngle = fk.m_Angle % PI2; float diff = toAngle - fromAngle; if (diff > PI) { diff -= PI2; } else if (diff < -PI) { diff += PI2; } float angle = fromAngle + diff * m_Strength; ConstrainRotation(fk, angle); } } }