コード例 #1
0
        public override void Constrain(ActorNode node)
        {
            ActorNode target = m_Target as ActorNode;

            if (target == null)
            {
                return;
            }

            Mat2D transformA = m_Parent.WorldTransform;
            Mat2D transformB = new Mat2D(target.WorldTransform);

            if (m_SourceSpace == TransformSpace.Local)
            {
                ActorNode grandParent = target.Parent;
                if (grandParent != null)
                {
                    Mat2D inverse = new Mat2D();
                    if (!Mat2D.Invert(inverse, grandParent.WorldTransform))
                    {
                        return;
                    }
                    Mat2D.Multiply(transformB, inverse, transformB);
                }
            }
            if (m_DestSpace == TransformSpace.Local)
            {
                ActorNode grandParent = m_Parent.Parent;
                if (grandParent != null)
                {
                    Mat2D.Multiply(transformB, grandParent.WorldTransform, transformB);
                }
            }
            Mat2D.Decompose(transformA, m_ComponentsA);
            Mat2D.Decompose(transformB, m_ComponentsB);

            float angleA = m_ComponentsA.Rotation % PI2;
            float angleB = m_ComponentsB.Rotation % PI2;
            float diff   = angleB - angleA;

            if (diff > Math.PI)
            {
                diff -= PI2;
            }
            else if (diff < -Math.PI)
            {
                diff += PI2;
            }

            float ti = 1.0f - m_Strength;

            m_ComponentsB.Rotation = angleA + diff * m_Strength;
            m_ComponentsB.X        = m_ComponentsA.X * ti + m_ComponentsB.X * m_Strength;
            m_ComponentsB.Y        = m_ComponentsA.Y * ti + m_ComponentsB.Y * m_Strength;
            m_ComponentsB.ScaleX   = m_ComponentsA.ScaleX * ti + m_ComponentsB.ScaleX * m_Strength;
            m_ComponentsB.ScaleY   = m_ComponentsA.ScaleY * ti + m_ComponentsB.ScaleY * m_Strength;
            m_ComponentsB.Skew     = m_ComponentsA.Skew * ti + m_ComponentsB.Skew * m_Strength;

            Mat2D.Compose(m_Parent.WorldTransform, m_ComponentsB);
        }
コード例 #2
0
        public Vec2D GetTipWorldTranslation(Vec2D vec)
        {
            Mat2D transform = new Mat2D();

            transform[4] = Length;
            Mat2D.Multiply(transform, WorldTransform, transform);
            vec[0] = transform[4];
            vec[1] = transform[5];
            return(vec);
        }
コード例 #3
0
 public void TransformBind(Mat2D xform)
 {
     if (m_BoneConnections != null)
     {
         foreach (BoneConnection bc in m_BoneConnections)
         {
             Mat2D.Multiply(bc.m_Bind, xform, bc.m_Bind);
             Mat2D.Invert(bc.m_InverseBind, bc.m_Bind);
         }
     }
 }
コード例 #4
0
        private void UpdateWorldTransform()
        {
            m_RenderOpacity = m_Opacity;

            if (m_Parent != null)
            {
                m_RenderCollapsed = m_IsCollapsedVisibility || m_Parent.m_RenderCollapsed;
                m_RenderOpacity  *= m_Parent.m_RenderOpacity;
                if (!m_OverrideWorldTransform)
                {
                    Mat2D.Multiply(m_WorldTransform, m_Parent.m_WorldTransform, m_Transform);
                }
            }
            else
            {
                Mat2D.Copy(m_WorldTransform, m_Transform);
            }
        }
コード例 #5
0
        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);
        }
コード例 #6
0
        public override void Constrain(ActorNode node)
        {
            ActorNode target      = m_Target as ActorNode;
            ActorNode grandParent = m_Parent.Parent;

            Mat2D transformA = m_Parent.WorldTransform;
            Mat2D transformB = new Mat2D();

            Mat2D.Decompose(transformA, m_ComponentsA);
            if (target == null)
            {
                Mat2D.Copy(transformB, transformA);
                m_ComponentsB[0] = m_ComponentsA[0];
                m_ComponentsB[1] = m_ComponentsA[1];
                m_ComponentsB[2] = m_ComponentsA[2];
                m_ComponentsB[3] = m_ComponentsA[3];
                m_ComponentsB[4] = m_ComponentsA[4];
                m_ComponentsB[5] = m_ComponentsA[5];
            }
            else
            {
                Mat2D.Copy(transformB, target.WorldTransform);
                if (m_SourceSpace == TransformSpace.Local)
                {
                    ActorNode sourceGrandParent = target.Parent;
                    if (sourceGrandParent != null)
                    {
                        Mat2D inverse = new Mat2D();
                        if (!Mat2D.Invert(inverse, sourceGrandParent.WorldTransform))
                        {
                            return;
                        }
                        Mat2D.Multiply(transformB, inverse, transformB);
                    }
                }
                Mat2D.Decompose(transformB, m_ComponentsB);

                if (!m_Copy)
                {
                    m_ComponentsB.Rotation = m_DestSpace == TransformSpace.Local ? 1.0f : m_ComponentsA.Rotation;
                }
                else
                {
                    m_ComponentsB.Rotation *= m_Scale;
                    if (m_Offset)
                    {
                        m_ComponentsB.Rotation += m_Parent.Rotation;
                    }
                }

                if (m_DestSpace == TransformSpace.Local)
                {
                    // Destination space is in parent transform coordinates.
                    // Recompose the parent local transform and get it in world, then decompose the world for interpolation.
                    if (grandParent != null)
                    {
                        Mat2D.Compose(transformB, m_ComponentsB);
                        Mat2D.Multiply(transformB, grandParent.WorldTransform, transformB);
                        Mat2D.Decompose(transformB, m_ComponentsB);
                    }
                }
            }

            bool clampLocal = m_MinMaxSpace == TransformSpace.Local && grandParent != null;

            if (clampLocal)
            {
                // Apply min max in local space, so transform to local coordinates first.
                Mat2D.Compose(transformB, m_ComponentsB);
                Mat2D inverse = new Mat2D();
                if (!Mat2D.Invert(inverse, grandParent.WorldTransform))
                {
                    return;
                }
                Mat2D.Multiply(transformB, inverse, transformB);
                Mat2D.Decompose(transformB, m_ComponentsB);
            }
            if (m_EnableMax && m_ComponentsB.Rotation > m_Max)
            {
                m_ComponentsB.Rotation = m_Max;
            }
            if (m_EnableMin && m_ComponentsB.Rotation < m_Min)
            {
                m_ComponentsB.Rotation = m_Min;
            }
            if (clampLocal)
            {
                // Transform back to world.
                Mat2D.Compose(transformB, m_ComponentsB);
                Mat2D.Multiply(transformB, grandParent.WorldTransform, transformB);
                Mat2D.Decompose(transformB, m_ComponentsB);
            }

            float angleA = m_ComponentsA.Rotation % PI2;
            float angleB = m_ComponentsB.Rotation % PI2;
            float diff   = angleB - angleA;

            if (diff > Math.PI)
            {
                diff -= PI2;
            }
            else if (diff < -Math.PI)
            {
                diff += PI2;
            }
            float ti = 1.0f - m_Strength;

            m_ComponentsB.Rotation = m_ComponentsA.Rotation + diff * m_Strength;
            m_ComponentsB.X        = m_ComponentsA.X;
            m_ComponentsB.Y        = m_ComponentsA.Y;
            m_ComponentsB.ScaleX   = m_ComponentsA.ScaleX;
            m_ComponentsB.ScaleY   = m_ComponentsA.ScaleY;
            m_ComponentsB.Skew     = m_ComponentsA.Skew;

            Mat2D.Compose(m_Parent.WorldTransform, m_ComponentsB);
        }
コード例 #7
0
        private void InitializeActor()
        {
            IEnumerable <ActorNode> nodes = m_Actor.Nodes;
            //m_Actor.Root.ScaleX = NimaToUnityScale;
            //m_Actor.Root.ScaleY = NimaToUnityScale;

            int imgNodeCount = 0;

            foreach (ActorNode node in nodes)
            {
                ActorImage ai = node as ActorImage;
                if (ai != null)
                {
                    imgNodeCount++;
                }
            }
            m_ImageNodes = new ActorImage[imgNodeCount];
            m_Meshes     = new Mesh[imgNodeCount];

            int imgIdx = 0;

            foreach (ActorNode node in nodes)
            {
                ActorImage ai = node as ActorImage;
                if (ai != null)
                {
                    m_ImageNodes[imgIdx] = ai;
                    Mesh mesh = new Mesh();
                    if (ai.DoesAnimationVertexDeform)
                    {
                        mesh.MarkDynamic();
                    }
                    m_Meshes[imgIdx] = mesh;
                    imgIdx++;

                    int     aiVertexCount    = ai.VertexCount;
                    int     aiVertexStride   = ai.VertexStride;
                    int     aiPositionOffset = ai.VertexPositionOffset;
                    int     aiUVOffset       = ai.VertexUVOffset;
                    float[] vertexBuffer     = ai.Vertices;

                    Vector3[] vertices = new Vector3[aiVertexCount];
                    Vector2[] uvs      = new Vector2[aiVertexCount];
                    Color32[] colors   = new Color32[aiVertexCount];

                    if (aiVertexStride == 12)
                    {
                        // We have bone weights.
                        int aiVertexBoneIndexOffset  = ai.VertexBoneIndexOffset;
                        int aiVertexBoneWeightOffset = ai.VertexBoneWeightOffset;
                        int idx = 0;

                        Mat2D newWorldOverride = new Mat2D();
                        // Change the override to scale by our UnityScale
                        Mat2D.Multiply(newWorldOverride, m_Actor.Root.Transform, ai.WorldTransformOverride);
                        ai.WorldTransformOverride = newWorldOverride;

                        // We don't use the bind transforms in the regular render path as we let Unity do the deform
                        // But in the Canvas render path we need to manually deform the vertices so we need to have our
                        // bind matrices in the correct world transform.
                        ai.TransformBind(m_Actor.Root.Transform);

                        if (ai.DoesAnimationVertexDeform)
                        {
                            // Update the vertex deforms too.
                            ai.TransformDeformVertices(ai.WorldTransform);
                        }

                        // Unity expects skinned mesh vertices to be in bone world space (character world).
                        // So we transform them to our world transform.
                        BoneWeight[] weights = new BoneWeight[aiVertexCount];

                        Mat2D wt = ai.WorldTransform;

                        for (int j = 0; j < aiVertexCount; j++)
                        {
                            float x = vertexBuffer[idx + aiPositionOffset];
                            float y = vertexBuffer[idx + aiPositionOffset + 1];
                            vertices[j] = new Vector3(wt[0] * x + wt[2] * y + wt[4], wt[1] * x + wt[3] * y + wt[5], 0.0f);

                            uvs[j]    = new Vector2(vertexBuffer[idx + aiUVOffset], 1.0f - vertexBuffer[idx + aiUVOffset + 1]);
                            colors[j] = new Color32(255, 255, 255, (byte)Math.Round(255 * ai.RenderOpacity));

                            BoneWeight weight = new BoneWeight();
                            weight.boneIndex0 = (int)vertexBuffer[idx + aiVertexBoneIndexOffset];
                            weight.boneIndex1 = (int)vertexBuffer[idx + aiVertexBoneIndexOffset + 1];
                            weight.boneIndex2 = (int)vertexBuffer[idx + aiVertexBoneIndexOffset + 2];
                            weight.boneIndex3 = (int)vertexBuffer[idx + aiVertexBoneIndexOffset + 3];
                            weight.weight0    = vertexBuffer[idx + aiVertexBoneWeightOffset];
                            weight.weight1    = vertexBuffer[idx + aiVertexBoneWeightOffset + 1];
                            weight.weight2    = vertexBuffer[idx + aiVertexBoneWeightOffset + 2];
                            weight.weight3    = vertexBuffer[idx + aiVertexBoneWeightOffset + 3];
                            weights[j]        = weight;

                            idx += aiVertexStride;
                        }
                        mesh.vertices    = vertices;
                        mesh.uv          = uvs;
                        mesh.boneWeights = weights;

                        // Set up bind poses.
                        int bindBoneCount = ai.ConnectedBoneCount + 1;                         // Always an extra bone for the root transform (identity).

                        Matrix4x4[] bindPoses = new Matrix4x4[bindBoneCount];
                        for (int i = 0; i < bindBoneCount; i++)
                        {
                            Matrix4x4 mat = new Matrix4x4();
                            mat          = Matrix4x4.identity;
                            bindPoses[i] = mat;
                        }

                        int bidx = 1;

                        foreach (ActorImage.BoneConnection bc in ai.BoneConnections)
                        {
                            Matrix4x4 mat   = bindPoses[bidx];
                            Mat2D     ibind = bc.InverseBind;

                            mat[0, 0]       = ibind[0];
                            mat[1, 0]       = ibind[1];
                            mat[0, 1]       = ibind[2];
                            mat[1, 1]       = ibind[3];
                            mat[0, 3]       = ibind[4];
                            mat[1, 3]       = ibind[5];
                            bindPoses[bidx] = mat;
                            bidx++;
                        }
                        mesh.bindposes = bindPoses;
                    }
                    else
                    {
                        int idx = 0;
                        for (int j = 0; j < aiVertexCount; j++)
                        {
                            vertices[j] = new Vector3(vertexBuffer[idx + aiPositionOffset], vertexBuffer[idx + aiPositionOffset + 1], 0);
                            uvs[j]      = new Vector2(vertexBuffer[idx + aiUVOffset], 1.0f - vertexBuffer[idx + aiUVOffset + 1]);
                            colors[j]   = new Color32(255, 255, 255, (byte)Math.Round(255 * ai.RenderOpacity));

                            idx += aiVertexStride;
                        }
                        mesh.vertices = vertices;
                        mesh.uv       = uvs;
                    }


                    int      triangleCount  = ai.TriangleCount;
                    ushort[] triangleBuffer = ai.Triangles;

                    int[] tris   = new int[triangleCount * 3];
                    int   triIdx = 0;
                    for (int j = 0; j < triangleCount; j++)
                    {
                        tris[triIdx]     = (int)triangleBuffer[triIdx + 2];
                        tris[triIdx + 1] = (int)triangleBuffer[triIdx + 1];
                        tris[triIdx + 2] = (int)triangleBuffer[triIdx];
                        triIdx          += 3;
                    }

                    mesh.triangles = tris;
                    mesh.colors32  = colors;
                    mesh.RecalculateBounds();
                    mesh.RecalculateNormals();

                    // We don't need to hold the geometry data in the node now that it's in our buffers.
                    // ai.DisposeGeometry(); // We now do need to hold onto it as we manually deform.
                }
            }

            // Find any vertex deform animation keyframes and update them to scale the vertices as is necessary for the skinned path.
            foreach (Nima.Animation.ActorAnimation animation in m_Actor.Animations)
            {
                if (animation == null || animation.AnimatedComponents == null)
                {
                    continue;
                }
                foreach (Nima.Animation.ComponentAnimation componentAnimation in animation.AnimatedComponents)
                {
                    ActorNode node = m_Actor[componentAnimation.ComponentIndex] as ActorNode;
                    if (node == null)
                    {
                        continue;
                    }
                    ActorImage actorImage = node as ActorImage;
                    if (actorImage != null && actorImage.ConnectedBoneCount == 0)
                    {
                        // This image is in the hierarchy, no need to transform the vertices.
                        continue;
                    }
                    foreach (Nima.Animation.PropertyAnimation propertyAnimation in componentAnimation.Properties)
                    {
                        if (propertyAnimation != null && propertyAnimation.PropertyType == Nima.Animation.PropertyTypes.VertexDeform)
                        {
                            foreach (Nima.Animation.KeyFrame keyFrame in propertyAnimation.KeyFrames)
                            {
                                (keyFrame as Nima.Animation.KeyFrameVertexDeform).TransformVertices(node.WorldTransform);
                            }
                        }
                    }
                }
            }
        }
コード例 #8
0
        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;
        }
コード例 #9
0
        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);
                }
            }
        }
コード例 #10
0
        public override void Constrain(ActorNode node)
        {
            ActorNode target      = m_Target as ActorNode;
            ActorNode grandParent = m_Parent.Parent;

            Mat2D transformA   = m_Parent.WorldTransform;
            Vec2D translationA = new Vec2D(transformA[4], transformA[5]);
            Vec2D translationB = new Vec2D();

            if (target == null)
            {
                Vec2D.Copy(translationB, translationA);
            }
            else
            {
                Mat2D transformB = new Mat2D(target.WorldTransform);
                if (m_SourceSpace == TransformSpace.Local)
                {
                    ActorNode sourceGrandParent = target.Parent;
                    if (sourceGrandParent != null)
                    {
                        Mat2D inverse = new Mat2D();
                        if (!Mat2D.Invert(inverse, sourceGrandParent.WorldTransform))
                        {
                            return;
                        }
                        Mat2D.Multiply(transformB, inverse, transformB);
                    }
                }
                translationB[0] = transformB[4];
                translationB[1] = transformB[5];

                if (!m_CopyX)
                {
                    translationB[0] = m_DestSpace == TransformSpace.Local ? 0.0f : translationA[0];
                }
                else
                {
                    translationB[0] *= m_ScaleX;
                    if (m_Offset)
                    {
                        translationB[0] += m_Parent.X;
                    }
                }

                if (!m_CopyY)
                {
                    translationB[1] = m_DestSpace == TransformSpace.Local ? 0.0f : translationA[1];
                }
                else
                {
                    translationB[1] *= m_ScaleY;

                    if (m_Offset)
                    {
                        translationB[1] += m_Parent.Y;
                    }
                }

                if (m_DestSpace == TransformSpace.Local)
                {
                    // Destination space is in parent transform coordinates.
                    if (grandParent != null)
                    {
                        Vec2D.TransformMat2D(translationB, translationB, grandParent.WorldTransform);
                    }
                }
            }

            bool clampLocal = m_MinMaxSpace == TransformSpace.Local && grandParent != null;

            if (clampLocal)
            {
                // Apply min max in local space, so transform to local coordinates first.
                Mat2D invert = new Mat2D();
                if (!Mat2D.Invert(invert, grandParent.WorldTransform))
                {
                    return;
                }
                // Get our target world coordinates in parent local.
                Vec2D.TransformMat2D(translationB, translationB, invert);
            }
            if (m_EnableMaxX && translationB[0] > m_MaxX)
            {
                translationB[0] = m_MaxX;
            }
            if (m_EnableMinX && translationB[0] < m_MinX)
            {
                translationB[0] = m_MinX;
            }
            if (m_EnableMaxY && translationB[1] > m_MaxY)
            {
                translationB[1] = m_MaxY;
            }
            if (m_EnableMinY && translationB[1] < m_MinY)
            {
                translationB[1] = m_MinY;
            }
            if (clampLocal)
            {
                // Transform back to world.
                Vec2D.TransformMat2D(translationB, translationB, grandParent.WorldTransform);
            }

            float ti = 1.0f - m_Strength;

            // Just interpolate world translation
            transformA[4] = translationA[0] * ti + translationB[0] * m_Strength;
            transformA[5] = translationA[1] * ti + translationB[1] * m_Strength;
        }