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); }
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); }
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); } } }
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); } }
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); }
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); }
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); } } } } } }
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); } } }
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; }