/// <summary> /// Calculates if varpdirection is 'lax' in respect to varpsource, meaning if it's 90 degrees around varpsource direction /// for example, right top is lax in respect to right, as is right down. Equally, left is lax in respect to left down, as is down. /// </summary> /// <param name="varpdirection"> /// The direction we need to know the laxity of /// </param> /// <param name="varpsource"> /// The source direction, to which varpdirection will be cast /// </param> /// <returns> /// True if varpdirection is lax in respect to varpsource /// </returns> private static bool metlaxdirection(enumcardinaldirections varpdirection, enumcardinaldirections varpsource) { bool varreturnvalue = false; //ignore the cases where parameters are none if (varpdirection != enumcardinaldirections.dirnone && varpsource != enumcardinaldirections.dirnone) { //convert directions in integers int varlocaldirection = (int)varpdirection; int varplus = (int)varpsource; int varminus = (int)varpsource; varplus++; varminus--; //loop the values if they exceed the direction wheel limits if (varplus > 8) varplus = 1; if (varminus < 1) varminus = 8; //confront the values if (varlocaldirection == (int)varpsource || varlocaldirection == varplus || varlocaldirection == varminus) { varreturnvalue = true; } } return varreturnvalue; }
/// <summary> /// This function will return a normalized Vector2 expressing the cardinal distance of a cardinal direction /// will be used to calculate displacement between two bones /// </summary> /// <param name="varpdirection"> /// the source cardinal direction (left, right, top, etc.) /// </param> /// <returns> /// The displacement needed to move in varpdirection direction /// </returns> private static Vector2 metcardinaldistancefromdirection(enumcardinaldirections varpdirection) { Vector2 varreturnvalue = new Vector2(); switch (varpdirection) { case enumcardinaldirections.dirup: varreturnvalue.x = 0; varreturnvalue.y = 1; break; case enumcardinaldirections.dirupright: varreturnvalue.x = 1; varreturnvalue.y = 1; break; case enumcardinaldirections.dirright: varreturnvalue.x = 1; varreturnvalue.y = 0; break; case enumcardinaldirections.dirdownright: varreturnvalue.x = 1; varreturnvalue.y = -1; break; case enumcardinaldirections.dirdown: varreturnvalue.x = 0; varreturnvalue.y = -1; break; case enumcardinaldirections.dirdownleft: varreturnvalue.x = -1; varreturnvalue.y = -1; break; case enumcardinaldirections.dirleft: varreturnvalue.x = -1; varreturnvalue.y = 0; break; case enumcardinaldirections.dirupleft: varreturnvalue.x = -1; varreturnvalue.y = 1; break; case enumcardinaldirections.dirnone: varreturnvalue = Vector2.zero; break; default: varreturnvalue = Vector2.zero; break; } return varreturnvalue; }
/// <summary> /// This recursive function will return the first sub bone of varproot that is found along varpdirection, to determine if the sub bone /// pertains to a certain limb or not. The function will deal with cohincident bones and multi branched sub bones. /// </summary> /// <param name="varpdirection"> /// The cardinal direction of the search (top, left, right, etc.) /// </param> /// <param name="varproot"> /// The source bone which will be used as reference for the direction of search /// </param> /// <param name="varporigin"> /// Recursion helper. Will be equal to varproot in the method call. /// </param> /// <returns> /// The reference to the most suitable bone in respect to varproot, along varpdirection /// </returns> private static Transform metfindvalidcardinalcandidate(enumcardinaldirections varpdirection, Transform varproot, Transform varporigin) { Transform varreturnvalue = varproot; Transform varcurrentcandidate = null; Transform varcurrentcandidatehasvalidsubbones = null; Transform varlastcandidate = null; Transform varlastcandidatehasvalidsubbones = null; enumcardinaldirections vardirection; //iterate only if the starting bone is not null if (varproot != null) { foreach (Transform varsubbone in varproot) { //store the last valid candidate for successive testing varlastcandidate = varcurrentcandidate; varlastcandidatehasvalidsubbones = varcurrentcandidatehasvalidsubbones; //calculate the cardinal direction (up, left, upleft, right, etc.) between the origin and the current sub bone vardirection = metcardinaldirection(metcardinaldistance(varporigin,varsubbone)); //if the direction is not the desired one, we issue a new recursive call to be sure that there's no sub bone //which is actually in the required direction (rules out cohincident bone heads and same direction sub bones) if (vardirection != varpdirection) { varcurrentcandidate = metfindvalidcardinalcandidate(varpdirection, varsubbone, varporigin); } //this sub bone's direction is the one we're looking for so we store it as the current valid candidate if (vardirection == varpdirection) { varcurrentcandidate = varsubbone; varcurrentcandidatehasvalidsubbones = metfindvalidcardinalcandidate(varpdirection, varcurrentcandidate, varporigin); } //return value decision check if we have a current candidate if (varcurrentcandidate != null) { //we have a current candidate check if we already have a return value candidate if (varlastcandidate != null) { //we have both a return value candidate and a new value candidate. check if these candidates have //valid origin direction sub bones //the last candidate has no valid direction sub bones, but the new candidate does if (varlastcandidatehasvalidsubbones == null && varcurrentcandidatehasvalidsubbones != null) { //the return value candidate is overwritten with the current candidate varreturnvalue = varcurrentcandidate; } //both candidates have sub bones in the required direction. if (varlastcandidatehasvalidsubbones != null && varcurrentcandidatehasvalidsubbones != null) { //We rule out the fittest candidate by counting both candidates sub bones if (metcountsubbones(varlastcandidate) < metcountsubbones(varcurrentcandidate)) { varreturnvalue = varcurrentcandidate; } } //both candidates have no sub bones in the required direction if (varlastcandidatehasvalidsubbones == null && varcurrentcandidatehasvalidsubbones == null) { if (varpdirection == enumcardinaldirections.dirup) //specific head case related to single bone-head armatures if (varcurrentcandidate.childCount == 0) varreturnvalue = varcurrentcandidate; } } //we have a current candidate and no return value candidate else //the return vaulue candidate becomes the current candidate varreturnvalue = varcurrentcandidate; } } //we effectively found no valid candidates since the return value equals the starting bone, so we null the result if (varreturnvalue == varproot) { varreturnvalue = null; } } return varreturnvalue; }
/// <summary> /// This is a recursive function responsible for storing a cardinal direction in the bones matrix. It'll basically memorize a complete body segment /// </summary> /// <param name="varpdirection"> /// Cardinal direction of exploration (up, right, left, etc.) /// </param> /// <param name="varproottransform"> /// The starting point of the exploration. The spine always starts at x varmatrixsize/2, y varmatrixsize/2 /// </param> /// <param name="varporigin"> /// Auxilliary exploration variable necessary for recursion. In method calls, needs be always equal to varproottransform /// </param> /// <param name="varpskip"> /// Auxilliary recursion variable. It needs be false in all method calls, but will be set to true by the algorithm if the bone needs to be skipped rather than stored. /// </param> /// <param name="varpstoretopbottomspinelimits"> /// Important variable that needs be true for the spine and false for the other body segments. Will memorize the top and bottom y coordinate of the relative bones. /// </param> /// <param name="varpstrict"> /// If the value is true, the direction of the sub bones will need be exactly like varpdirection. If varpstric is false, a sub bone can be in a +45 -45 angle in respect to the root to still qualify as a good candidate for the body segment. /// </param> /// <returns> /// If there's an exploration error the return value will be 0, otherwise it'll be 1 in all other cases. /// </returns> private static int metstorecardinaldirectioninbonesmatrix(enumcardinaldirections varpdirection, Transform varproottransform, Transform varporigin, bool varpskip, bool varpstoretopbottomspinelimits, bool varpstrict) { int varreturnvalue = 1; int varcandidatebones = 0; enumcardinaldirections vardirection = enumcardinaldirections.dirnone; Transform varcandidate = null; Vector2 vardirectiontofollow = new Vector2(); bool varlaxdirection = false; vardirectiontofollow = metcardinaldistancefromdirection(varpdirection); if (!varpskip) { metprint("Storing " + varproottransform.name + " x " + varcoordx + " y " + varcoordy,4); //we can't store the current bone since we exceeded the bones matrix limit if (varcoordx>=varmatrixsize || varcoordy>=varmatrixsize || varcoordx < 0 || varcoordy < 0) return 0; varbonesmatrix[varcoordy][varcoordx] = varproottransform; if (varpstoretopbottomspinelimits) { if (vartopboney > varcoordy) vartopboney = varcoordy; if (varbottomboney < varcoordy) varbottomboney = varcoordy; } varcoordx-=(int)vardirectiontofollow.x; varcoordy-=(int)vardirectiontofollow.y; varreturnvalue++; } //explore the current bone if it's a strict exploration, or otherwise only if the child cound is inferior to the sub bones exploration count limit in the options if (varproottransform.childCount < varmaxchildcount || varpstrict) { foreach (Transform varsubbone in varproottransform) { //determine the current direction between the current sub bone and the origin vardirection = metcardinaldirection(metcardinaldistance(varporigin,varsubbone)); //if the direction is not the desired one, we proceed to further checks if (vardirection != varpdirection) { varlaxdirection = true; //check if the current direction is lax, and thus valid, in respect to the requested one varlaxdirection = metlaxdirection(vardirection, varpdirection); //if the current direction is acceptable, iterate a recursion with a request to store the current bone if (varpstrict || !varlaxdirection) { varreturnvalue = metstorecardinaldirectioninbonesmatrix(varpdirection, varsubbone, varporigin,true ,varpstoretopbottomspinelimits, varpstrict); } //the current direction is not acceptable. iterate a recursion skipping the current sub bone else { varreturnvalue = metstorecardinaldirectioninbonesmatrix(varpdirection, varsubbone, varporigin,false ,varpstoretopbottomspinelimits, varpstrict); } //there was an error storing the last bone. exit the function. if (varreturnvalue == 0) { return 0; } } //we store the valid candidate in case the current direction is the one we're looking for if (vardirection == varpdirection) { varcandidate = varsubbone; varcandidatebones++; } } } //if we have more than one candidate, we need to find the most suitable one to store in the next iteration if (varcandidatebones>1) { varcandidate = null; //find the first direction sub bone for these candidates metprint("Find the first valid " + varpdirection.ToString() + " candidate for " + varproottransform,4); varcandidate = metfindvalidcardinalcandidate(varpdirection,varproottransform, varproottransform); } //we now have one valid candidate if it actually exists if (varcandidate != null) { //if the exploration is strict, setup the candidate as the origin for the next iteration (assures a geometrical 'straight line' in the bones matrix) if (varpstrict) { metstorecardinaldirectioninbonesmatrix(varpdirection, varcandidate, varcandidate, false, varpstoretopbottomspinelimits, varpstrict); } //if the exploration is lax, setup the origin as the origin for the next iteration (allows a segmented line for the limb structure. the bones matrix stays always straight) else { metstorecardinaldirectioninbonesmatrix(varpdirection, varcandidate, varporigin, false, varpstoretopbottomspinelimits, varpstrict); } varcurrentexplorationsize++; } return varreturnvalue; }