public void DrawTexturedAndDeformedSkin(Mat2D transform, VertexBuffer deformBuffer, VertexBuffer vertexBuffer, IndexBuffer indexBuffer, int offset, int indexCount, float[] boneMatrices, float opacity, Color4 color, Texture texture) { m_Transform[0, 0] = transform[0]; m_Transform[1, 0] = transform[1]; m_Transform[0, 1] = transform[2]; m_Transform[1, 1] = transform[3]; m_Transform[0, 3] = transform[4]; m_Transform[1, 3] = transform[5]; Bind(m_DeformedTexturedSkinShader, deformBuffer, vertexBuffer); int[] u = m_DeformedTexturedSkinShader.Uniforms; GL.UniformMatrix4(u[0], true, ref m_Projection); GL.UniformMatrix4(u[1], true, ref m_ViewTransform); GL.UniformMatrix4(u[2], true, ref m_Transform); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, texture.Id); GL.Uniform1(u[3], 0); GL.Uniform1(u[4], opacity); GL.Uniform4(u[5], color); GL.Uniform3(u[6], boneMatrices.Length, boneMatrices); GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer.Id); GL.DrawElements(BeginMode.Triangles, indexCount, DrawElementsType.UnsignedShort, offset * 2); }

private void UpdateTransform() { Mat2D.FromRotation(m_Transform, m_Rotation); m_Transform[4] = m_Translation[0]; m_Transform[5] = m_Translation[1]; Mat2D.Scale(m_Transform, m_Transform, m_Scale); }

public void Copy(ActorImage node, Actor resetActor) { base.Copy(node, resetActor); m_DrawOrder = node.m_DrawOrder; m_BlendMode = node.m_BlendMode; m_TextureIndex = node.m_TextureIndex; m_VertexCount = node.m_VertexCount; m_TriangleCount = node.m_TriangleCount; m_Vertices = node.m_Vertices; m_Triangles = node.m_Triangles; if (node.m_AnimationDeformedVertices != null) { m_AnimationDeformedVertices = (float[])node.m_AnimationDeformedVertices.Clone(); } if (node.m_BoneConnections != null) { m_BoneConnections = new BoneConnection[node.m_BoneConnections.Length]; for (int i = 0; i < node.m_BoneConnections.Length; i++) { BoneConnection bc = new BoneConnection(); bc.m_BoneIdx = node.m_BoneConnections[i].m_BoneIdx; Mat2D.Copy(bc.m_Bind, node.m_BoneConnections[i].m_Bind); Mat2D.Copy(bc.m_InverseBind, node.m_BoneConnections[i].m_InverseBind); m_BoneConnections[i] = bc; } } }

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 void TransToMachinePoint(float x, float y, out float tX, out float tY) { double transferX, transferY; transferX = Mat2D.AffineTransPoint2d(x, y, out transferY); tX = (float)transferX; tY = (float)transferY; }

public void SetView(Mat2D viewTransform) { m_ViewTransform[0, 0] = viewTransform[0]; m_ViewTransform[1, 0] = viewTransform[1]; m_ViewTransform[0, 1] = viewTransform[2]; m_ViewTransform[1, 1] = viewTransform[3]; m_ViewTransform[0, 3] = viewTransform[4]; m_ViewTransform[1, 3] = viewTransform[5]; }

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

public void Copy(ActorNode node, Actor resetActor) { base.Copy(node, resetActor); m_Transform = new Mat2D(node.m_Transform); m_WorldTransform = new Mat2D(node.m_WorldTransform); m_Translation = new Vec2D(node.m_Translation); m_Scale = new Vec2D(node.m_Scale); m_Rotation = node.m_Rotation; m_Opacity = node.m_Opacity; m_RenderOpacity = node.m_RenderOpacity; m_OverrideWorldTransform = node.m_OverrideWorldTransform; }

public static ActorImage Read(Actor actor, BinaryReader reader, ActorImage node = null) { if (node == null) { node = new ActorImage(); } ActorNode.Read(actor, reader, node); bool isVisible = reader.ReadByte() != 0; if (isVisible) { node.m_BlendMode = (BlendModes)reader.ReadByte(); node.m_DrawOrder = (int)reader.ReadUInt16(); node.m_TextureIndex = (int)reader.ReadByte(); int numConnectedBones = (int)reader.ReadByte(); if (numConnectedBones != 0) { node.m_BoneConnections = new BoneConnection[numConnectedBones]; for (int i = 0; i < numConnectedBones; i++) { BoneConnection bc = new BoneConnection(); bc.m_BoneIdx = reader.ReadUInt16(); Actor.ReadFloat32Array(reader, bc.m_Bind.Values); Mat2D.Invert(bc.m_InverseBind, bc.m_Bind); node.m_BoneConnections[i] = bc; } Mat2D worldOverride = new Mat2D(); Actor.ReadFloat32Array(reader, worldOverride.Values); node.WorldTransformOverride = worldOverride; } uint numVertices = reader.ReadUInt32(); int vertexStride = numConnectedBones > 0 ? 12 : 4; node.m_VertexCount = (int)numVertices; node.m_Vertices = new float[numVertices * vertexStride]; Actor.ReadFloat32Array(reader, node.m_Vertices); uint numTris = reader.ReadUInt32(); node.m_Triangles = new ushort[numTris * 3]; node.m_TriangleCount = (int)numTris; Actor.ReadUInt16Array(reader, node.m_Triangles); } return(node); }

/** * parentMat: null -> no change * same pointer -> parent transform changed * diff pointer -> parent changed */ public static void Draw(Group *self, float *parentMat, bool isParentTransformDirty) { var childLst = &self->childLst; int childCount = childLst->count; void **childArr = childLst->arr; // sort by depth if any child depth is changed for (int i = 0; i < childCount; i += 1) { if (Node.IsDepthDirty(childArr[i])) { Algo.MergeSort(childArr, childCount, Node.DepthCmp); break; } } float *mat = self->mat; float *concatMat = self->concatMat; bool isTransformDirty = self->isTransformDirty; if (isTransformDirty) { self->isTransformDirty = false; Mat2D.FromScalingRotationTranslation(mat, self->pos, self->scl, self->rot); } if (isParentTransformDirty) { self->parentMat = parentMat; } if (isTransformDirty || isParentTransformDirty) { isParentTransformDirty = true; if (parentMat == null) { Mat2D.Copy(concatMat, mat); } else { Mat2D.Mul(concatMat, parentMat, mat); } } for (int i = 0; i < childCount; i += 1) { Node.Draw(childArr[i], concatMat, isParentTransformDirty); } }

public static void Draw(TpSprite *self, float *parentMat, bool isParentTransformDirty) { #if FDB Should.NotNull("self", self); Should.TypeEqual("self", self->type, Type); Should.NotNull("self->spriteMeta", self->spriteMeta); #endif var bat = DrawCtx.GetBatch(self->spriteMeta->atlas->name); int vertIdx = bat.vertCount, triIdx = bat.triCount; bat.RequestQuota(4, 6); float *verts = self->verts; float *uvs = self->uvs; if (self->isTransformDirty) { float *mat = stackalloc float[6]; Mat2D.FromScalingRotationTranslation(mat, self->pos, self->scl, self->rot); TpSpriteMeta.FillQuad(self->spriteMeta, mat, self->verts); } var bVerts = bat.verts; var bUvs = bat.uvs; float z = self->pos[2]; bVerts[vertIdx].Set(verts[0], verts[1], z); bVerts[vertIdx + 1].Set(verts[2], verts[3], z); bVerts[vertIdx + 2].Set(verts[4], verts[5], z); bVerts[vertIdx + 3].Set(verts[6], verts[7], z); bUvs[vertIdx].Set(uvs[0], uvs[1]); bUvs[vertIdx + 1].Set(uvs[2], uvs[3]); bUvs[vertIdx + 2].Set(uvs[4], uvs[5]); bUvs[vertIdx + 3].Set(uvs[6], uvs[7]); float *color = self->color; var bColor = Vec4.Color(color, 0.5f); var bColors = bat.colors; bColors[vertIdx] = bColor; bColors[vertIdx + 1] = bColor; bColors[vertIdx + 2] = bColor; bColors[vertIdx + 3] = bColor; var bTris = bat.tris; bTris[triIdx] = vertIdx; bTris[triIdx + 1] = vertIdx + 1; bTris[triIdx + 2] = vertIdx + 2; bTris[triIdx + 3] = vertIdx; bTris[triIdx + 4] = vertIdx + 2; bTris[triIdx + 5] = vertIdx + 3; }

public virtual void CacheTransform() { transformDirty = false; bool useParentSize = parent != null && parent.useLayout; if (useParentSize) { cachedPos = Axes.Calc(relativePosAxes, pos, parent.cachedSize); cachedSize = Axes.Calc(relativeSizeAxes, size, parent.cachedSize); } else { cachedPos = Axes.Calc(relativePosAxes, pos); cachedSize = Axes.Calc(relativeSizeAxes, size); } if (useLayout) { cachedPivot = cachedSize * Align.Calc(pivotAlign, customPivotAlign); cachedPos.x += margin.l; cachedPos.y += margin.b; cachedSize.x -= margin.l + margin.r; cachedSize.y -= margin.b + margin.t; cachedMat.FromTranslation(-cachedPivot); cachedMat.ScaleRotateTranslate(scl, rot, cachedPos); } else { cachedMat.FromScalingRotationTranslation(scl, rot, cachedPos); } if (useParentSize) { cachedAnchor = parent.cachedSize * Align.Calc(anchorAlign, customAnchorAlign); cachedMat.Translate(cachedAnchor); } cachedMatConcat = parent == null ? cachedMat : parent.cachedMatConcat * cachedMat; if (needMatConcatInverse) { cachedMatConcatInverse.FromInverse(cachedMatConcat); } if (needScreenAabb) { cachedScreenAabb = (cachedMatConcat * new Quad(0, 0, cachedSize.x, cachedSize.y)).GetAabb(); } }

public void UpdateMount() { if (m_MountNode == null) { return; } //Matrix4x4 world = m_MountTargetObject.transform.localToWorldMatrix; Matrix4x4 localParent = Matrix4x4.identity; if (gameObject.transform.parent) { localParent = gameObject.transform.parent.worldToLocalMatrix; } Matrix4x4 localTransform = localParent * m_ActorGameObject.transform.localToWorldMatrix; // m_MountNode Mat2D world = m_MountNode.WorldTransform; Mat2D m2d = new Mat2D(); m2d[0] = localTransform[0, 0]; m2d[1] = localTransform[1, 0]; m2d[2] = localTransform[0, 1]; m2d[3] = localTransform[1, 1]; m2d[4] = localTransform[0, 3] + world[4] * ActorAsset.NimaToUnityScale * localTransform[0, 0]; m2d[5] = localTransform[1, 3] + world[5] * ActorAsset.NimaToUnityScale * localTransform[1, 1]; if (m_InheritRotation && m_InheritScale) { Vec2D scale = new Vec2D(); float angle = Mat2D.Decompose(world, scale); transform.localEulerAngles = new Vector3(0.0f, 0.0f, angle * Mathf.Rad2Deg); transform.localScale = new Vector3(scale[0] * m_ScaleModifier, scale[1] * m_ScaleModifier, 1.0f); } else if (m_InheritRotation) { float angle = (float)Math.Atan2(world[1], world[0]); transform.localEulerAngles = new Vector3(0.0f, 0.0f, angle * Mathf.Rad2Deg); } else if (m_InheritScale) { Vec2D scale = new Vec2D(); Mat2D.GetScale(world, scale); transform.localScale = new Vector3(scale[0] * m_ScaleModifier, scale[1] * m_ScaleModifier, 1.0f); } transform.localPosition = new Vector3(m2d[4], m2d[5], transform.localPosition.z); }

public static void Init(Group *self) { self->tag = Tag.Group; Vec3.Zero(self->pos); Vec2.One(self->scl); self->rot = 0; self->isTransformDirty = true; self->isDepthDirty = true; Mat2D.Identity(self->mat); Mat2D.Identity(self->concatMat); self->parentMat = null; PtrLst.Init(&self->childLst); }

void Solve1(BoneChain fk1, Vec2D worldTargetTranslation) { Mat2D iworld = fk1.m_ParentWorldInverse; var pA = new Vec2D(); fk1.m_Bone.GetWorldTranslation(pA); var pBT = new Vec2D(worldTargetTranslation); // To target in worldspace Vec2D toTarget = Vec2D.Subtract(new Vec2D(), pBT, pA); // Note this is directional, hence not transformMat2d Vec2D toTargetLocal = Vec2D.TransformMat2(new Vec2D(), toTarget, iworld); float r = (float)Math.Atan2(toTargetLocal[1], toTargetLocal[0]); ConstrainRotation(fk1, r); fk1.m_Angle = r; }

public override void Constrain(ActorNode node) { ActorNode target = m_Target as ActorNode; if (target == null) { return; } Vec2D targetTranslation = target.GetWorldTranslation(new Vec2D()); Vec2D ourTranslation = m_Parent.GetWorldTranslation(new Vec2D()); Vec2D toTarget = Vec2D.Subtract(new Vec2D(), ourTranslation, targetTranslation); float currentDistance = Vec2D.Length(toTarget); switch (m_Mode) { case Mode.Closer: if (currentDistance < m_Distance) { return; } break; case Mode.Further: if (currentDistance > m_Distance) { return; } break; } if (currentDistance < 0.001) { return; } Vec2D.Scale(toTarget, toTarget, 1.0f / currentDistance); Vec2D.Scale(toTarget, toTarget, m_Distance); Mat2D world = m_Parent.WorldTransform; Vec2D position = Vec2D.Lerp(new Vec2D(), ourTranslation, Vec2D.Add(new Vec2D(), targetTranslation, toTarget), m_Strength); world[4] = position[0]; world[5] = position[1]; }

public override void UpdateTransform() { if (m_ActorCollider == null) { return; } base.UpdateTransform(); m_Collider.enabled = m_ActorCollider.IsCollisionEnabled; Mat2D world = m_ActorNode.WorldTransform; Vec2D scale = new Vec2D(); float angle = Mat2D.Decompose(world, scale); transform.localEulerAngles = new Vector3(0.0f, 0.0f, angle * Mathf.Rad2Deg); transform.localScale = new Vector3(scale[0] * ActorAsset.NimaToUnityScale, scale[1] * ActorAsset.NimaToUnityScale, 1.0f); transform.localPosition = new Vector3(world[4] * ActorAsset.NimaToUnityScale, world[5] * ActorAsset.NimaToUnityScale, 0.0f); }

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

protected override void OnRenderFrame(FrameEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); Mat2D view = new Mat2D(); m_Renderer.SetView(view); if (m_GameActorInstance != null) { m_GameActorInstance.Render(m_Renderer); } // e.Time /*float[] transform = Mat2D.Create(); * m_Renderer.BlendMode = Renderer2D.BlendModes.Transparent; * Mat2D.Scale(transform, transform, Vec2D.Create(2048.0f, 256.0f)); * m_Renderer.DrawTextured(view, transform, m_VertexBuffer, m_IndexBuffer, 1.0f, Color.White, m_Texture);*/ this.SwapBuffers(); }

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 void UpdateVertexPositionBuffer(float[] buffer, bool isSkinnedDeformInWorld = true) { Mat2D worldTransform = this.WorldTransform; int readIdx = 0; int writeIdx = 0; float[] v = m_AnimationDeformedVertices != null ? m_AnimationDeformedVertices : m_Vertices; int stride = m_AnimationDeformedVertices != null ? 2 : VertexStride; if (IsSkinned) { float[] boneTransforms = this.BoneInfluenceMatrices; //Mat2D inverseWorldTransform = Mat2D.Invert(new Mat2D(), worldTransform); float[] influenceMatrix = new float[6] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; int boneIndexOffset = VertexBoneIndexOffset; int weightOffset = VertexBoneWeightOffset; for (int i = 0; i < m_VertexCount; i++) { float x = v[readIdx]; float y = v[readIdx + 1]; float px, py; if (m_AnimationDeformedVertices != null && isSkinnedDeformInWorld) { px = x; py = y; } else { px = worldTransform[0] * x + worldTransform[2] * y + worldTransform[4]; py = worldTransform[1] * x + worldTransform[3] * y + worldTransform[5]; } influenceMatrix[0] = influenceMatrix[1] = influenceMatrix[2] = influenceMatrix[3] = influenceMatrix[4] = influenceMatrix[5] = 0.0f; for (int wi = 0; wi < 4; wi++) { int boneIndex = (int)m_Vertices[boneIndexOffset + wi]; float weight = m_Vertices[weightOffset + wi]; int boneTransformIndex = boneIndex * 6; for (int j = 0; j < 6; j++) { influenceMatrix[j] += boneTransforms[boneTransformIndex + j] * weight; } } x = influenceMatrix[0] * px + influenceMatrix[2] * py + influenceMatrix[4]; y = influenceMatrix[1] * px + influenceMatrix[3] * py + influenceMatrix[5]; readIdx += stride; boneIndexOffset += VertexStride; weightOffset += VertexStride; buffer[writeIdx++] = x; buffer[writeIdx++] = y; } } else { Vec2D tempVec = new Vec2D(); for (int i = 0; i < m_VertexCount; i++) { tempVec[0] = v[readIdx]; tempVec[1] = v[readIdx + 1]; Vec2D.TransformMat2D(tempVec, tempVec, worldTransform); readIdx += stride; buffer[writeIdx++] = tempVec[0]; buffer[writeIdx++] = tempVec[1]; } } }

void Start() { m_Animator = GetComponent <Animator>(); m_Actor = gameObject.GetComponent <ActorBaseComponent>(); if (m_Actor != null) { if (m_Actor.ActorInstance != null) { m_Aim = m_Actor.ActorInstance.GetAnimation("Aim2"); Nima.Animation.ActorAnimation walk = m_Actor.ActorInstance.GetAnimation("Walk"); Nima.Animation.ActorAnimation walkToIdle = m_Actor.ActorInstance.GetAnimation("WalkToIdle"); // Calculate aim slices. if (m_Aim != null) { ActorNode muzzle = m_Actor.ActorInstance.GetNode("Muzzle"); if (muzzle != null) { for (int i = 0; i < AimSliceCount; i++) { float position = i / (float)(AimSliceCount - 1) * m_Aim.Duration; m_Aim.Apply(position, m_Actor.ActorInstance, 1.0f); Mat2D worldTransform = muzzle.WorldTransform; AimSlice slice = m_AimLookup[i]; // Extract forward vector and position. slice.dir = new Vec2D(); Vec2D.Normalize(slice.dir, new Vec2D(worldTransform[0], worldTransform[1])); slice.point = new Vec2D(worldTransform[4], worldTransform[5]); m_AimLookup[i] = slice; } } if (walk != null) { walk.Apply(0.0f, m_Actor.ActorInstance, 1.0f); for (int i = 0; i < AimSliceCount; i++) { float position = i / (float)(AimSliceCount - 1) * m_Aim.Duration; m_Aim.Apply(position, m_Actor.ActorInstance, 1.0f); Mat2D worldTransform = muzzle.WorldTransform; AimSlice slice = m_AimWalkingLookup[i]; // Extract forward vector and position. slice.dir = new Vec2D(); Vec2D.Normalize(slice.dir, new Vec2D(worldTransform[0], worldTransform[1])); slice.point = new Vec2D(worldTransform[4], worldTransform[5]); m_AimWalkingLookup[i] = slice; } } if (walkToIdle != null) { walkToIdle.Apply(walkToIdle.Duration, m_Actor.ActorInstance, 1.0f); } } } } }

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

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

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 void Start() { m_Actor = gameObject.GetComponent <ActorBaseComponent>(); if (m_Actor != null) { // Get a game object from the actor, use this to mount items or query for other components. // GameObject headColliderGameObject = m_Actor.GetActorGameObject("HeadCollider"); // if(headColliderGameObject != null) // { // Collider2D collider = headColliderGameObject.GetComponent<Collider2D>(); // if(collider != null) // { // // Set it to a trigger, or do something else with it... // // collider.isTrigger = true; // } // } if (m_Actor.ActorInstance != null) { m_Idle = m_Actor.ActorInstance.GetAnimation("Idle"); m_Aim = m_Actor.ActorInstance.GetAnimation("Aim2"); m_Walk = m_Actor.ActorInstance.GetAnimationInstance("Walk"); m_Run = m_Actor.ActorInstance.GetAnimation("Run"); m_WalkToIdle = m_Actor.ActorInstance.GetAnimation("WalkToIdle"); // We made walk an animation instance so it has it's own sense of time which lets it do things like track events. m_Walk.AnimationEvent += delegate(object animationInstance, Nima.Animation.AnimationEventArgs args) { // Event triggered from animation. }; ActorNode characterNode = m_Actor.ActorInstance.GetNode("Character"); if (characterNode != null) { m_GroundSpeedProperty = characterNode.GetCustomFloatProperty("GroundSpeed"); m_IsRunningProperty = characterNode.GetCustomBooleanProperty("IsRunning"); } // Calculate aim slices. if (m_Aim != null) { ActorNode muzzle = m_Actor.ActorInstance.GetNode("Muzzle"); if (muzzle != null) { for (int i = 0; i < AimSliceCount; i++) { float position = i / (float)(AimSliceCount - 1) * m_Aim.Duration; m_Aim.Apply(position, m_Actor.ActorInstance, 1.0f); m_Actor.ActorInstance.Advance(0.0f); Mat2D worldTransform = muzzle.WorldTransform; AimSlice slice = m_AimLookup[i]; // Extract forward vector and position. slice.dir = new Vec2D(); Vec2D.Normalize(slice.dir, new Vec2D(worldTransform[0], worldTransform[1])); slice.point = new Vec2D(worldTransform[4] * ActorAsset.NimaToUnityScale, worldTransform[5] * ActorAsset.NimaToUnityScale); m_AimLookup[i] = slice; } } if (m_Walk != null) { m_Walk.Time = 0.0f; m_Walk.Apply(1.0f); for (int i = 0; i < AimSliceCount; i++) { float position = i / (float)(AimSliceCount - 1) * m_Aim.Duration; m_Aim.Apply(position, m_Actor.ActorInstance, 1.0f); m_Actor.ActorInstance.Advance(0.0f); Mat2D worldTransform = muzzle.WorldTransform; AimSlice slice = m_AimWalkingLookup[i]; // Extract forward vector and position. slice.dir = new Vec2D(); Vec2D.Normalize(slice.dir, new Vec2D(worldTransform[0], worldTransform[1])); slice.point = new Vec2D(worldTransform[4] * ActorAsset.NimaToUnityScale, worldTransform[5] * ActorAsset.NimaToUnityScale); m_AimWalkingLookup[i] = slice; } } } } } m_IdleTime = 0.0f; }

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