summaryrefslogtreecommitdiff
path: root/Runtime/mecanim/animation/blendtree.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/mecanim/animation/blendtree.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/mecanim/animation/blendtree.cpp')
-rw-r--r--Runtime/mecanim/animation/blendtree.cpp967
1 files changed, 967 insertions, 0 deletions
diff --git a/Runtime/mecanim/animation/blendtree.cpp b/Runtime/mecanim/animation/blendtree.cpp
new file mode 100644
index 0000000..e326b77
--- /dev/null
+++ b/Runtime/mecanim/animation/blendtree.cpp
@@ -0,0 +1,967 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/mecanim/animation/blendtree.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ void GetWeightsFreeformDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute);
+ void GetWeightsFreeformCartesian (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute);
+
+ void PrecomputeFreeform (int type, Blend2dDataConstant& out, memory::Allocator& alloc)
+ {
+ const Vector2f* positionArray = out.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = out.m_ChildCount;
+ float* constantMagnitudes = out.m_ChildMagnitudeArray.Get();
+ Vector2f* constantChildPairVectors = out.m_ChildPairVectorArray.Get();
+ float* constantChildPairAvgMagInv = out.m_ChildPairAvgMagInvArray.Get();
+ MotionNeighborList* constantChildNeighborLists = out.m_ChildNeighborListArray.Get();
+
+ if (type == 2)
+ {
+ for (int i=0; i<count; i++)
+ constantMagnitudes[i] = Magnitude (positionArray[i]);
+
+ for (int i=0; i<count; i++)
+ {
+ for (int j=0; j<count; j++)
+ {
+ int pairIndex = i + j*count;
+
+ // Calc avg magnitude for pair
+ float magSum = constantMagnitudes[j] + constantMagnitudes[i];
+ if (magSum > 0)
+ constantChildPairAvgMagInv[pairIndex] = 2.0f / magSum;
+ else
+ constantChildPairAvgMagInv[pairIndex] = 2.0f / magSum;
+
+ // Calc mag of vector and divide by avg magnitude
+ float mag = (constantMagnitudes[j] - constantMagnitudes[i]) * constantChildPairAvgMagInv[pairIndex];
+
+ if (constantMagnitudes[j] == 0 || constantMagnitudes[i] == 0)
+ constantChildPairVectors[pairIndex] = Vector2f (0, mag);
+ else
+ {
+ float angle = Angle (positionArray[i], positionArray[j]);
+ if (positionArray[i].x * positionArray[j].y - positionArray[i].y * positionArray[j].x < 0)
+ angle = -angle;
+ constantChildPairVectors[pairIndex] = Vector2f (angle, mag);
+ }
+ }
+ }
+ }
+ else if (type == 3)
+ {
+ for (int i=0; i<count; i++)
+ {
+ for (int j=0; j<count; j++)
+ {
+ int pairIndex = i + j*count;
+ constantChildPairAvgMagInv[pairIndex] = 1 / SqrMagnitude (positionArray[j] - positionArray[i]);
+ constantChildPairVectors[pairIndex] = positionArray[j] - positionArray[i];
+ }
+ }
+ }
+
+ float* weightArray;
+ ALLOC_TEMP (weightArray, float, count);
+
+ int* cropArray;
+ ALLOC_TEMP (cropArray, int, count);
+
+ Vector2f* workspaceBlendVectors;
+ ALLOC_TEMP (workspaceBlendVectors, Vector2f, count);
+
+ bool* neighborArray;
+ ALLOC_TEMP (neighborArray, bool, count*count);
+ for (int c=0; c<count*count; c++)
+ neighborArray[c] = false;
+
+ float minX = 10000.0f;
+ float maxX = -10000.0f;
+ float minY = 10000.0f;
+ float maxY = -10000.0f;
+ for (int c=0; c<count; c++)
+ {
+ minX = min (minX, positionArray[c].x);
+ maxX = max (maxX, positionArray[c].x);
+ minY = min (minY, positionArray[c].y);
+ maxY = max (maxY, positionArray[c].y);
+ }
+ float xRange = (maxX - minX) * 0.5f;
+ float yRange = (maxY - minY) * 0.5f;
+ minX -= xRange;
+ maxX += xRange;
+ minY -= yRange;
+ maxY += yRange;
+
+ for (int i=0; i<=100; i++)
+ {
+ for (int j=0; j<=100; j++)
+ {
+ float x = i*0.01f;
+ float y = j*0.01f;
+ if (type == 2)
+ GetWeightsFreeformDirectional (out, weightArray, cropArray, workspaceBlendVectors, minX * (1-x) + maxX * x, minY * (1-y) + maxY * y, true);
+ else if (type == 3)
+ GetWeightsFreeformCartesian (out, weightArray, cropArray, workspaceBlendVectors, minX * (1-x) + maxX * x, minY * (1-y) + maxY * y, true);
+ for (int c=0; c<count; c++)
+ if (cropArray[c] >= 0)
+ neighborArray[c * count + cropArray[c]] = true;
+ }
+ }
+ for (int c=0; c<count; c++)
+ {
+ dynamic_array<int> nList;
+ for (int d=0; d<count; d++)
+ if (neighborArray[c * count + d])
+ nList.push_back (d);
+
+ constantChildNeighborLists[c].m_Count = nList.size ();
+ constantChildNeighborLists[c].m_NeighborArray = alloc.ConstructArray<mecanim::uint32_t>(nList.size ());
+
+ for (int d=0; d<nList.size (); d++)
+ constantChildNeighborLists[c].m_NeighborArray[d] = nList[d];
+ }
+ }
+
+ void GetAllBlendValue(uint32_t nodeIndex, OffsetPtr<BlendTreeNodeConstant>* const allTreeNodes, dynamic_array<int> &arBlendValueIds)
+ {
+ BlendTreeNodeConstant* const currentNode = allTreeNodes[nodeIndex].Get();
+
+ if(currentNode->m_BlendEventID != -1)
+ {
+ dynamic_array<int>::const_iterator it = std::find(arBlendValueIds.begin(), arBlendValueIds.end(), currentNode->m_BlendEventID);
+ if(it == arBlendValueIds.end())
+ arBlendValueIds.push_back(currentNode->m_BlendEventID);
+
+ if(currentNode->m_BlendType >= 1)
+ {
+ dynamic_array<int>::const_iterator itY = std::find(arBlendValueIds.begin(), arBlendValueIds.end(), currentNode->m_BlendEventYID);
+ if(itY == arBlendValueIds.end())
+ arBlendValueIds.push_back(currentNode->m_BlendEventYID);
+ }
+
+ for(mecanim::uint32_t i = 0 ; i < currentNode->m_ChildCount; i++)
+ {
+ GetAllBlendValue(currentNode->m_ChildIndices[i], allTreeNodes, arBlendValueIds);
+ }
+ }
+
+ }
+ void GetAllBlendValue(BlendTreeConstant* const constant, dynamic_array<int> &arBlendValueIds)
+ {
+ if(constant->m_NodeCount > 0)
+ GetAllBlendValue(0, constant->m_NodeArray.Get(), arBlendValueIds);
+ }
+
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t childCount, uint32_t* childIndices, float* blendTreeThresholdArray, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_BlendEventID = blendValueID;
+ blendTreeNodeConstant->m_ChildCount = childCount;
+
+ blendTreeNodeConstant->m_ChildIndices = alloc.ConstructArray<uint32_t>(childCount);
+ memcpy(&blendTreeNodeConstant->m_ChildIndices[0], &childIndices[0], sizeof(uint32_t)*childCount);
+
+ // Setup blend 1d data constant
+ blendTreeNodeConstant->m_BlendType = 0;
+ blendTreeNodeConstant->m_Blend1dData = alloc.Construct<Blend1dDataConstant>();
+ Blend1dDataConstant& data = *blendTreeNodeConstant->m_Blend1dData;
+
+ // Populate blend 1d data constant
+ data.m_ChildCount = childCount;
+ data.m_ChildThresholdArray = alloc.ConstructArray<float>(data.m_ChildCount);
+ memcpy(&data.m_ChildThresholdArray[0], &blendTreeThresholdArray[0], sizeof(float)*childCount);
+
+ return blendTreeNodeConstant ;
+ }
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t blendValueYID, int blendType, uint32_t childCount, uint32_t* childIndices, Vector2f* blendTreePositionArray, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_BlendEventID = blendValueID;
+ blendTreeNodeConstant->m_BlendEventYID = blendValueYID;
+ blendTreeNodeConstant->m_ChildCount = childCount;
+
+ blendTreeNodeConstant->m_ChildIndices = alloc.ConstructArray<uint32_t>(childCount);
+ memcpy(&blendTreeNodeConstant->m_ChildIndices[0], &childIndices[0], sizeof(uint32_t)*childCount);
+
+ // Setup blend 2d data constant
+ blendTreeNodeConstant->m_BlendType = blendType;
+ blendTreeNodeConstant->m_Blend2dData = alloc.Construct<Blend2dDataConstant>();
+ Blend2dDataConstant& data = *blendTreeNodeConstant->m_Blend2dData;
+
+ // Populate blend 2d data constant
+ data.m_ChildCount = childCount;
+ data.m_ChildPositionArray = alloc.ConstructArray<Vector2f>(data.m_ChildCount);
+ memcpy(&data.m_ChildPositionArray[0], &blendTreePositionArray[0], sizeof(Vector2f)*childCount);
+
+ if (blendType == 2 || blendType == 3)
+ {
+ // Populate blend 2d precomputed data for type 2 or 3
+ if (blendType == 2)
+ {
+ data.m_ChildMagnitudeCount = childCount;
+ data.m_ChildMagnitudeArray = alloc.ConstructArray<float>(data.m_ChildMagnitudeCount);
+ }
+ data.m_ChildPairAvgMagInvCount = childCount * childCount;
+ data.m_ChildPairVectorCount = childCount * childCount;
+ data.m_ChildNeighborListCount = childCount;
+ data.m_ChildPairAvgMagInvArray = alloc.ConstructArray<float>(data.m_ChildPairAvgMagInvCount);
+ data.m_ChildPairVectorArray = alloc.ConstructArray<Vector2f>(data.m_ChildPairVectorCount);
+ data.m_ChildNeighborListArray = alloc.ConstructArray<MotionNeighborList>(data.m_ChildNeighborListCount);
+ PrecomputeFreeform (blendType, data, alloc);
+ }
+
+ return blendTreeNodeConstant;
+ }
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t clipID, float duration, bool mirror, float cycle, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_ChildCount = 0;
+ blendTreeNodeConstant->m_ClipID = clipID;
+ blendTreeNodeConstant->m_Duration = duration;
+ blendTreeNodeConstant->m_Mirror = mirror;
+ blendTreeNodeConstant->m_CycleOffset = cycle;
+
+ return blendTreeNodeConstant ;
+ }
+
+ BlendTreeConstant* CreateBlendTreeConstant(BlendTreeNodeConstant** nodeArray, uint32_t nodeCount, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeConstant);
+
+ BlendTreeConstant *blendTreeConstant = alloc.Construct<BlendTreeConstant>();
+
+ blendTreeConstant->m_NodeCount = nodeCount;
+ blendTreeConstant->m_NodeArray = alloc.ConstructArray< OffsetPtr<BlendTreeNodeConstant> >(nodeCount);
+
+ uint32_t i;
+ for(i = 0; i < nodeCount; i++)
+ blendTreeConstant->m_NodeArray[i] = nodeArray[i];
+
+ dynamic_array<int> blendValueIds;
+ GetAllBlendValue(blendTreeConstant, blendValueIds);
+
+ blendTreeConstant->m_BlendEventArrayConstant = CreateValueArrayConstant(kFloatType, blendValueIds.size(), alloc);
+
+ for(i = 0; i < blendValueIds.size(); i++)
+ {
+ blendTreeConstant->m_BlendEventArrayConstant->m_ValueArray[i].m_ID = blendValueIds[i];
+ }
+
+ return blendTreeConstant ;
+ }
+
+ BlendTreeConstant* CreateBlendTreeConstant(uint32_t clipID, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeConstant);
+
+ BlendTreeConstant *blendTreeConstant = alloc.Construct<BlendTreeConstant>();
+ blendTreeConstant->m_NodeCount = 1;
+ blendTreeConstant->m_NodeArray = alloc.ConstructArray< OffsetPtr<BlendTreeNodeConstant> >(1);
+
+ blendTreeConstant->m_NodeArray[0] = CreateBlendTreeNodeConstant(clipID,1.0f,false,0,alloc);
+
+ return blendTreeConstant;
+
+ }
+
+ void DestroyBlendTreeNodeConstant(BlendTreeNodeConstant * constant, memory::Allocator& alloc)
+ {
+ alloc.Deallocate(constant->m_ChildIndices);
+
+ if (!constant->m_Blend1dData.IsNull ())
+ {
+ alloc.Deallocate(constant->m_Blend1dData->m_ChildThresholdArray);
+ }
+
+ if (!constant->m_Blend2dData.IsNull ())
+ {
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPositionArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildMagnitudeArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPairVectorArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPairAvgMagInvArray);
+
+ if (!constant->m_Blend2dData->m_ChildNeighborListArray.IsNull ())
+ {
+ for (int i=0; i<constant->m_Blend2dData->m_ChildNeighborListCount; i++)
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildNeighborListArray[i].m_NeighborArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildNeighborListArray);
+ }
+ }
+
+ alloc.Deallocate(constant);
+ }
+
+ void DestroyBlendTreeConstant(BlendTreeConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ if(!constant->m_BlendEventArrayConstant.IsNull())
+ {
+ DestroyValueArrayConstant(constant->m_BlendEventArrayConstant.Get(), alloc);
+ }
+
+ for(uint32_t i = 0; i < constant->m_NodeCount; i++)
+ {
+ DestroyBlendTreeNodeConstant(constant->m_NodeArray[i].Get(), alloc);
+ }
+
+ alloc.Deallocate(constant->m_NodeArray);
+ alloc.Deallocate(constant);
+ }
+
+ }
+
+ BlendTreeMemory* CreateBlendTreeMemory(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeMemory);
+ BlendTreeMemory *blendTreeMemory = alloc.Construct<BlendTreeMemory>();
+
+ blendTreeMemory->m_NodeCount = GetLeafCount(*constant);
+ blendTreeMemory->m_NodeDurationArray = alloc.ConstructArray<float>(blendTreeMemory->m_NodeCount);
+
+ return blendTreeMemory ;
+ }
+
+ void DestroyBlendTreeMemory(BlendTreeMemory *memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ alloc.Deallocate(memory->m_NodeDurationArray);
+ alloc.Deallocate(memory);
+ }
+ }
+
+ BlendTreeInput* CreateBlendTreeInput(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeInput);
+
+ BlendTreeInput *blendTreeInput = alloc.Construct<BlendTreeInput>();
+
+ if(!constant->m_BlendEventArrayConstant.IsNull())
+ blendTreeInput->m_BlendValueArray = CreateValueArray(constant->m_BlendEventArrayConstant.Get(), alloc);
+
+ return blendTreeInput ;
+ }
+
+ void DestroyBlendTreeInput(BlendTreeInput * input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ if(input->m_BlendValueArray)
+ {
+ DestroyValueArray(input->m_BlendValueArray, alloc);
+ }
+
+ alloc.Deallocate(input);
+ }
+ }
+
+ BlendTreeOutput* CreateBlendTreeOutput(BlendTreeConstant const* constant, uint32_t maxBlendedClip, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeOutput);
+ BlendTreeOutput *blendTreeOutput = alloc.Construct<BlendTreeOutput>();
+
+ blendTreeOutput->m_MaxBlendedClip = maxBlendedClip;
+ blendTreeOutput->m_OutputBlendArray = alloc.ConstructArray< BlendTreeNodeOutput >(maxBlendedClip);
+
+ return blendTreeOutput ;
+ }
+
+ void DestroyBlendTreeOutput(BlendTreeOutput *output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ alloc.Deallocate(output->m_OutputBlendArray);
+ alloc.Deallocate(output);
+ }
+ }
+
+ BlendTreeWorkspace* CreateBlendTreeWorkspace(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeWorkspace);
+
+ BlendTreeWorkspace *blendTreeWorkspace = alloc.Construct<BlendTreeWorkspace>();
+ blendTreeWorkspace->m_BlendArray = alloc.ConstructArray<float>(constant->m_NodeCount);
+ // Optimize later to only have room for worst case number of immediate children instead of m_NodeCount:
+ blendTreeWorkspace->m_TempWeightArray = alloc.ConstructArray<float>(constant->m_NodeCount);
+ blendTreeWorkspace->m_TempCropArray = alloc.ConstructArray<int>(constant->m_NodeCount);
+ blendTreeWorkspace->m_ChildInputVectorArray = alloc.ConstructArray<Vector2f>(constant->m_NodeCount);
+
+ return blendTreeWorkspace ;
+ }
+
+ void DestroyBlendTreeWorkspace(BlendTreeWorkspace * workspace, memory::Allocator& alloc)
+ {
+ if(workspace != 0)
+ {
+ alloc.Deallocate(workspace->m_BlendArray);
+ alloc.Deallocate(workspace->m_TempWeightArray);
+ alloc.Deallocate(workspace->m_TempCropArray);
+ alloc.Deallocate(workspace->m_ChildInputVectorArray);
+ alloc.Deallocate(workspace);
+ }
+ }
+
+ float WeightForIndex( const float* thresholdArray, mecanim::uint32_t count, mecanim::uint32_t index, float blend)
+ {
+ if( blend >= thresholdArray[index])
+ {
+ if(index+1 == count)
+ {
+ return 1.0f;
+ }
+ else if(thresholdArray[index+1] < blend)
+ {
+ return 0.0f;
+ }
+ else
+ {
+ if(thresholdArray[index]-thresholdArray[index+1] != 0)
+ {
+ return (blend - thresholdArray[index+1]) / (thresholdArray[index]-thresholdArray[index+1]);
+ }
+ else
+ {
+ return thresholdArray[index];
+ }
+
+ }
+ }
+ else
+ {
+ if(index == 0)
+ {
+ return 1.0f;
+ }
+ else if(thresholdArray[index-1] > blend)
+ {
+ return 0.0f;
+ }
+ else
+ {
+ if(( thresholdArray[index]-thresholdArray[index-1]) != 0)
+ {
+ return (blend - thresholdArray[index-1]) / (thresholdArray[index]-thresholdArray[index-1]);
+ }
+ else
+ {
+ return thresholdArray[index];
+ }
+ }
+ }
+
+ }
+
+ void GetWeightsSimpleDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+
+ if (weightArray == NULL || positionArray == NULL)
+ return;
+
+ // Initialize all weights to 0
+ for (int i=0; i<count; i++)
+ weightArray[i] = 0;
+
+ // Handle fallback
+ if (count < 2)
+ {
+ if (count == 1)
+ weightArray[0] = 1;
+ return;
+ }
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+
+ // Handle special case when sampled ecactly in the middle
+ if (blendPosition == Vector2f::zero)
+ {
+ // If we have a center motion, give that one all the weight
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ {
+ weightArray[i] = 1;
+ return;
+ }
+ }
+
+ // Otherwise divide weight evenly
+ float sharedWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] = sharedWeight;
+ return;
+ }
+
+ int indexA = -1;
+ int indexB = -1;
+ int indexCenter = -1;
+ float maxDotForNegCross = -100000.0f;
+ float maxDotForPosCross = -100000.0f;
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ {
+ if (indexCenter >= 0)
+ return;
+ indexCenter = i;
+ continue;
+ }
+ Vector2f posNormalized = Normalize (positionArray[i]);
+ float dot = Dot (posNormalized, blendPosition);
+ float cross = posNormalized.x * blendPosition.y - posNormalized.y * blendPosition.x;
+ if (cross > 0)
+ {
+ if (dot > maxDotForPosCross)
+ {
+ maxDotForPosCross = dot;
+ indexA = i;
+ }
+ }
+ else
+ {
+ if (dot > maxDotForNegCross)
+ {
+ maxDotForNegCross = dot;
+ indexB = i;
+ }
+ }
+ }
+
+ float centerWeight = 0;
+
+ if (indexA < 0 || indexB < 0)
+ {
+ // Fallback if sampling point is not inside a triangle
+ centerWeight = 1;
+ }
+ else
+ {
+ Vector2f a = positionArray[indexA];
+ Vector2f b = positionArray[indexB];
+
+ // Calculate weights using barycentric coordinates
+ // (formulas from http://en.wikipedia.org/wiki/Barycentric_coordinate_system_%28mathematics%29 )
+ float det = b.y*a.x - b.x*a.y; // Simplified from: (b.y-0)*(a.x-0) + (0-b.x)*(a.y-0);
+ float wA = (b.y*blendValueX - b.x*blendValueY) / det; // Simplified from: ((b.y-0)*(l.x-0) + (0-b.x)*(l.y-0)) / det;
+ float wB = (a.x*blendValueY - a.y*blendValueX) / det; // Simplified from: ((0-a.y)*(l.x-0) + (a.x-0)*(l.y-0)) / det;
+ centerWeight = 1 - wA - wB;
+
+ // Clamp to be inside triangle
+ if (centerWeight < 0)
+ {
+ centerWeight = 0;
+ float sum = wA + wB;
+ wA /= sum;
+ wB /= sum;
+ }
+ else if (centerWeight > 1)
+ {
+ centerWeight = 1;
+ wA = 0;
+ wB = 0;
+ }
+
+ // Give weight to the two vertices on the periphery that are closest
+ weightArray[indexA] = wA;
+ weightArray[indexB] = wB;
+ }
+
+ if (indexCenter >= 0)
+ {
+ weightArray[indexCenter] = centerWeight;
+ }
+ else
+ {
+ // Give weight to all children when input is in the center
+ float sharedWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] += sharedWeight * centerWeight;
+ }
+ }
+
+ const float kInversePI = 1 / kPI;
+ float GetWeightFreeformDirectional (const Blend2dDataConstant& blendConstant, Vector2f* workspaceBlendVectors, int i, int j, Vector2f blendPosition)
+ {
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ Vector2f vecIO = workspaceBlendVectors[i];
+ vecIO.y *= blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+
+ if (blendConstant.m_ChildPositionArray[i] == Vector2f::zero)
+ vecIJ.x = workspaceBlendVectors[j].x;
+ else if (blendConstant.m_ChildPositionArray[j] == Vector2f::zero)
+ vecIJ.x = workspaceBlendVectors[i].x;
+ else if (vecIJ.x == 0 || blendPosition == Vector2f::zero)
+ vecIO.x = vecIJ.x;
+
+ return 1 - Dot (vecIJ, vecIO) / SqrMagnitude (vecIJ);
+ }
+
+ void GetWeightsFreeformDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+ const float* constantMagnitudes = blendConstant.m_ChildMagnitudeArray.Get();
+ const MotionNeighborList* constantChildNeighborLists = blendConstant.m_ChildNeighborListArray.Get();
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+ float magO = Magnitude (blendPosition);
+
+ if (blendPosition == Vector2f::zero)
+ {
+ for (int i=0; i<count; i++)
+ workspaceBlendVectors[i] = Vector2f (0, magO - constantMagnitudes[i]);
+ }
+ else
+ {
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ workspaceBlendVectors[i] = Vector2f (0, magO - constantMagnitudes[i]);
+ else
+ {
+ float angle = Angle (positionArray[i], blendPosition);
+ if (positionArray[i].x * blendPosition.y - positionArray[i].y * blendPosition.x < 0)
+ angle = -angle;
+ workspaceBlendVectors[i] = Vector2f (angle, magO - constantMagnitudes[i]);
+ }
+ }
+ }
+
+ if (preCompute)
+ {
+ for (int i=0; i<count; i++)
+ {
+ // Fade out over 180 degrees away from example
+ float value = 1 - abs (workspaceBlendVectors[i].x) * kInversePI;
+ cropArray[i] = -1;
+ for (int j=0; j<count; j++)
+ {
+ if (i==j)
+ continue;
+
+ float newValue = GetWeightFreeformDirectional (blendConstant, workspaceBlendVectors, i, j, blendPosition);
+
+ if (newValue <= 0)
+ {
+ value = 0;
+ cropArray[i] = -1;
+ break;
+ }
+ // Used for determining neighbors
+ if (newValue < value)
+ cropArray[i] = j;
+ value = min (value, newValue);
+ }
+ }
+ return;
+ }
+
+ for (int i=0; i<count; i++)
+ {
+ // Fade out over 180 degrees away from example
+ float value = 1 - abs (workspaceBlendVectors[i].x) * kInversePI;
+ for (int jIndex=0; jIndex<constantChildNeighborLists[i].m_Count; jIndex++)
+ {
+ int j = constantChildNeighborLists[i].m_NeighborArray[jIndex];
+ float newValue = GetWeightFreeformDirectional (blendConstant, workspaceBlendVectors, i, j, blendPosition);
+ if (newValue <= 0)
+ {
+ value = 0;
+ break;
+ }
+ value = min (value, newValue);
+ }
+ weightArray[i] = value;
+ }
+
+ // Normalize weights
+ float summedWeight = 0;
+ for (int i=0; i<count; i++)
+ summedWeight += weightArray[i];
+
+ if (summedWeight > 0)
+ {
+ summedWeight = 1.0f / summedWeight; // Do division once instead of for every sample
+ for (int i=0; i<count; i++)
+ weightArray[i] *= summedWeight;
+ }
+ else
+ {
+ // Give weight to all children as fallback when no children have any weight.
+ // This happens when sampling in the center if no center motion is provided.
+ float evenWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] = evenWeight;
+ }
+ }
+
+ void GetWeightsFreeformCartesian (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+ const MotionNeighborList* constantChildNeighborLists = blendConstant.m_ChildNeighborListArray.Get();
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+ for (int i=0; i<count; i++)
+ workspaceBlendVectors[i] = blendPosition - positionArray[i];
+
+ if (preCompute)
+ {
+ for (int i=0; i<count; i++)
+ {
+ cropArray[i] = -1;
+ Vector2f vecIO = workspaceBlendVectors[i];
+ float value = 1;
+ for (int j=0; j<count; j++)
+ {
+ if (i==j)
+ continue;
+
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ float newValue = 1 - Dot (vecIJ, vecIO) * blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+ if (newValue <= 0)
+ {
+ value = 0;
+ cropArray[i] = -1;
+ break;
+ }
+ // Used for determining neighbors
+ if (newValue < value)
+ cropArray[i] = j;
+ value = min (value, newValue);
+ }
+ }
+ return;
+ }
+
+ for (int i=0; i<count; i++)
+ {
+ Vector2f vecIO = workspaceBlendVectors[i];
+ float value = 1;
+ for (int jIndex=0; jIndex<constantChildNeighborLists[i].m_Count; jIndex++)
+ {
+ int j = constantChildNeighborLists[i].m_NeighborArray[jIndex];
+ if (i==j)
+ continue;
+
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ float newValue = 1 - Dot (vecIJ, vecIO) * blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+ if (newValue < 0)
+ {
+ value = 0;
+ break;
+ }
+ value = min (value, newValue);
+ }
+ weightArray[i] = value;
+ }
+
+ // Normalize weights
+ float summedWeight = 0;
+ for (int i=0; i<count; i++)
+ summedWeight += weightArray[i];
+ summedWeight = 1.0f / summedWeight; // Do division once instead of for every sample
+ for (int i=0; i<count; i++)
+ weightArray[i] *= summedWeight;
+ }
+
+ void GetWeights1d (const Blend1dDataConstant& blendConstant, float* weightArray, float blendValue)
+ {
+ blendValue = math::clamp (blendValue, blendConstant.m_ChildThresholdArray[0], blendConstant.m_ChildThresholdArray[blendConstant.m_ChildCount-1]);
+ for (mecanim::uint32_t j = 0 ; j < blendConstant.m_ChildCount; j++)
+ weightArray[j] = WeightForIndex (blendConstant.m_ChildThresholdArray.Get (), blendConstant.m_ChildCount, j, blendValue);
+ }
+
+ void GetWeights (const BlendTreeNodeConstant& nodeConstant, BlendTreeWorkspace &workspace, float* weightArray, float blendValueX, float blendValueY)
+ {
+ if (nodeConstant.m_BlendType == 0)
+ GetWeights1d (*nodeConstant.m_Blend1dData.Get(), weightArray, blendValueX);
+ else if (nodeConstant.m_BlendType == 1)
+ GetWeightsSimpleDirectional (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ else if (nodeConstant.m_BlendType == 2)
+ GetWeightsFreeformDirectional (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ else if (nodeConstant.m_BlendType == 3)
+ GetWeightsFreeformCartesian (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ }
+
+ uint32_t ComputeBlends(const BlendTreeConstant& constant, const BlendTreeInput &input, const BlendTreeMemory &memory, BlendTreeOutput &output, BlendTreeWorkspace &workspace)
+ {
+ uint32_t leafIndex = 0;
+ uint32_t currentOutputIndex = 0 ;
+ workspace.m_BlendArray[0] = 1;
+
+ uint32_t i = 0;
+ for(i = 0 ; i < constant.m_NodeCount; i ++)
+ {
+ const BlendTreeNodeConstant* nodeConstant = constant.m_NodeArray[i].Get();
+
+ if(nodeConstant->m_ClipID != -1)
+ {
+ if(workspace.m_BlendArray[i] > 0)
+ {
+ float duration = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) ? memory.m_NodeDurationArray[leafIndex] * nodeConstant->m_Duration : nodeConstant->m_Duration;
+
+ output.m_OutputBlendArray[currentOutputIndex].m_ID = nodeConstant->m_ClipID;
+ output.m_OutputBlendArray[currentOutputIndex].m_BlendValue = workspace.m_BlendArray[i];
+ output.m_OutputBlendArray[currentOutputIndex].m_Reverse = duration < 0;
+ output.m_OutputBlendArray[currentOutputIndex].m_CycleOffset = nodeConstant->m_CycleOffset;
+ output.m_OutputBlendArray[currentOutputIndex].m_Mirror = nodeConstant->m_Mirror;
+
+ output.m_Duration += math::abs(duration) * workspace.m_BlendArray[i];
+ currentOutputIndex++;
+ }
+
+ leafIndex++;
+ }
+ else if(nodeConstant->m_ChildCount> 0)
+ {
+ if (nodeConstant->m_BlendType == 0)
+ {
+ // 1D blending
+ int32_t index = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventID);
+ float blendValue;
+ input.m_BlendValueArray->ReadData(blendValue, constant.m_BlendEventArrayConstant->m_ValueArray[index].m_Index);
+
+ GetWeights (*nodeConstant, workspace, workspace.m_TempWeightArray, blendValue, 0);
+ }
+ else if (nodeConstant->m_BlendType >= 1)
+ {
+ // 2D blending
+ int32_t indexX = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventID);
+ int32_t indexY = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventYID);
+ float blendValueX, blendValueY;
+ input.m_BlendValueArray->ReadData(blendValueX, constant.m_BlendEventArrayConstant->m_ValueArray[indexX].m_Index);
+ input.m_BlendValueArray->ReadData(blendValueY, constant.m_BlendEventArrayConstant->m_ValueArray[indexY].m_Index);
+
+ GetWeights (*nodeConstant, workspace, workspace.m_TempWeightArray, blendValueX, blendValueY);
+ }
+
+ for(mecanim::uint32_t j = 0 ; j < nodeConstant->m_ChildCount; j++)
+ {
+ float w = workspace.m_TempWeightArray[j];
+ workspace.m_BlendArray[nodeConstant->m_ChildIndices[j]] = w * workspace.m_BlendArray[i];
+ }
+ }
+ }
+
+ return currentOutputIndex;
+ }
+
+ void EvaluateBlendTree(const BlendTreeConstant& constant, const BlendTreeInput &input, const BlendTreeMemory &memory, BlendTreeOutput &output, BlendTreeWorkspace &workspace)
+ {
+ for(uint32_t i = 0 ; i < output.m_MaxBlendedClip ; i++) output.m_OutputBlendArray[i].m_ID = -1;
+
+ output.m_Duration = 0;
+ uint32_t currentOutputIndex = 0;
+
+ if(constant.m_NodeCount >0)
+ currentOutputIndex = ComputeBlends(constant, input, memory, output, workspace);
+
+ if(currentOutputIndex == 0)
+ output.m_Duration = 1;
+ }
+
+
+ mecanim::uint32_t GetLeafCount(const BlendTreeConstant& constant)
+ {
+ mecanim::uint32_t leafCount = 0 ;
+
+ for(int i = 0 ; i < constant.m_NodeCount ; i++)
+ {
+ if(constant.m_NodeArray[i]->m_ClipID != -1)
+ leafCount++;
+ }
+
+ return leafCount;
+ }
+
+ void FillLeafIDArray(const BlendTreeConstant& constant, uint32_t* leafIDArray)
+ {
+ uint32_t baseIndex = 0 ;
+ uint32_t i;
+ for(i = 0 ; i < constant.m_NodeCount ; i++)
+ {
+ if(constant.m_NodeArray[i]->m_ClipID != -1)
+ {
+ leafIDArray[baseIndex] = constant.m_NodeArray[i]->m_ClipID;
+ baseIndex++;
+ }
+ }
+ }
+
+ mecanim::uint32_t GetMaxBlendCount(const BlendTreeConstant& constant, const BlendTreeNodeConstant& node)
+ {
+ uint32_t maxBlendCount = node.m_ClipID != -1 ? 1 : 0;
+
+ // Blending occur between closest sibbling only
+ uint32_t current = 0;
+ uint32_t previous = 0;
+
+ if(node.m_BlendType == 0 )
+ {
+ for(int i = 0 ; i < node.m_ChildCount ; i++)
+ {
+ current = GetMaxBlendCount(constant, *constant.m_NodeArray[node.m_ChildIndices[i]]);
+ maxBlendCount = math::maximum( maxBlendCount, previous + current);
+ previous = current;
+ }
+ }
+ else
+ {
+ for(int i = 0 ; i < node.m_ChildCount ; i++)
+ {
+ maxBlendCount += GetMaxBlendCount(constant, *constant.m_NodeArray[node.m_ChildIndices[i]]);
+ }
+ }
+
+ return maxBlendCount;
+ }
+
+ mecanim::uint32_t GetMaxBlendCount(const BlendTreeConstant& constant)
+ {
+ uint32_t maxBlendCount = 0;
+ if(constant.m_NodeCount)
+ maxBlendCount = GetMaxBlendCount(constant, *constant.m_NodeArray[0]);
+ return maxBlendCount;
+ }
+
+
+}// namespace animation
+
+}//namespace mecanim