public static void UpdateRACFemaleDNABones(UMAData umaData, UMASkeleton skeleton) { if (RACHumanFemaleDNAConverterBehaviour.customisation == null) RACHumanFemaleDNAConverterBehaviour.customisation = GameObject.FindObjectOfType(typeof(RACCustomization)) as RACCustomization; HumanFemaleDNAConverterBehaviour.UpdateUMAFemaleDNABones (umaData, skeleton); List<RACModifiableControl> controls = RACHumanFemaleDNAConverterBehaviour.customisation.ControlList; for (int iSliderIndex = 0; iSliderIndex < controls.Count; ++iSliderIndex) { RACModifiableControl curControl = controls[iSliderIndex]; for (int iBodyPartIndex = 0; iBodyPartIndex < curControl.modifiedBodyParts.Length; ++iBodyPartIndex) { int skeletonStringToHash = UMASkeleton.StringToHash(curControl.modifiedBodyParts[iBodyPartIndex]); //Get the starting information Vector3 startingInformation = Vector3.zero; if (curControl.sliderStyle == RACModifiableControl.SliderStyle.POSITION) { startingInformation = skeleton.GetPosition(skeletonStringToHash); Vector3 scale = skeleton.GetScale(skeletonStringToHash); //Modify it if (curControl.effectsX) startingInformation.x += curControl.sliderControl.actualValue*(1/scale.x); if (curControl.effectsY) startingInformation.y += curControl.sliderControl.actualValue*(1/scale.y); if (curControl.effectsZ) startingInformation.z += curControl.sliderControl.actualValue*(1/scale.z); skeleton.SetPosition(skeletonStringToHash, startingInformation); } else if (curControl.sliderStyle == RACModifiableControl.SliderStyle.SCALE) { startingInformation = skeleton.GetScale(skeletonStringToHash); //Modify it if (curControl.effectsX) startingInformation.x = curControl.sliderControl.actualValue; if (curControl.effectsY) startingInformation.y = curControl.sliderControl.actualValue; if (curControl.effectsZ) startingInformation.z = curControl.sliderControl.actualValue; skeleton.SetScale(skeletonStringToHash, startingInformation); } } } }
public void AdjustScale(UMASkeleton skeleton) { if (_adjustScale) { if (skeleton.HasBone(scaleBoneHash)) { var liveScaleResult = _liveScale != -1f ? _liveScale : _scale; float finalOverallScale = skeleton.GetScale(_scaleBoneHash).x *liveScaleResult; //hmm why does this work- its supposed to be + skeleton.SetScale(_scaleBoneHash, new Vector3(finalOverallScale, finalOverallScale, finalOverallScale)); } } }
/// <summary> /// Apply the modifiers using the given dna (determined by the typehash) /// </summary> /// <param name="umaData"></param> /// <param name="skeleton"></param> /// <param name="dnaTypeHash"></param> public override void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash) { var umaDna = umaData.GetDna(dnaTypeHash); var masterWeightCalc = masterWeight.GetWeight(umaDna); if (masterWeightCalc == 0f) { return; } for (int i = 0; i < _skeletonModifiers.Count; i++) { _skeletonModifiers[i].umaDNA = umaDna; var thisHash = (_skeletonModifiers[i].hash != 0) ? _skeletonModifiers[i].hash : UMAUtils.StringToHash(_skeletonModifiers[i].hashName); //check skeleton has the bone we want to change if (!skeleton.HasBone(thisHash)) { //Debug.LogWarning("You were trying to apply skeleton modifications to a bone that didn't exist (" + _skeletonModifiers[i].hashName + ") on " + umaData.gameObject.name); continue; } //With these ValueX.x is the calculated value and ValueX.y is min and ValueX.z is max var thisValueX = _skeletonModifiers[i].CalculateValueX(umaDna); var thisValueY = _skeletonModifiers[i].CalculateValueY(umaDna); var thisValueZ = _skeletonModifiers[i].CalculateValueZ(umaDna); if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Position) { skeleton.SetPositionRelative(thisHash, new Vector3( Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z), Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z), Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z)), masterWeightCalc); } else if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Rotation) { skeleton.SetRotationRelative(thisHash, Quaternion.Euler(new Vector3( Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z), Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z), Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z))), masterWeightCalc); } else if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Scale) { //If there are two sets of skeletonModifiers and both are at 50% it needs to apply them both but the result should be cumulative //so we need to work out the difference this one is making, weight that and add it to the current scale of the bone var scale = new Vector3( Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z), Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z), Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z)); //we cant use val.value here because the initial values always need to be applied var defaultVal = SkeletonModifier.skelAddDefaults[SkeletonModifier.SkeletonPropType.Scale].x; var scaleDiff = new Vector3(scale.x - defaultVal, scale.y - defaultVal, scale.z - defaultVal); var weightedScaleDiff = scaleDiff * masterWeightCalc; var fullScale = skeleton.GetScale(_skeletonModifiers[i].hash) + weightedScaleDiff; skeleton.SetScale(thisHash, fullScale); } } }
/// <summary> /// Updates the characterHeightMassRadius after all other changes have been made by the converters /// </summary> private void UpdateCharacterHeightMassRadius(UMAData umaData, UMASkeleton skeleton, Bounds newBounds) { float charHeight = newBounds.size.y; float charWidth = newBounds.size.x; if (umaData.umaRecipe.raceData.umaTarget == RaceData.UMATarget.Humanoid) { //adjusting is only about tweaking the result if (_adjustHeight || _adjustRadius) { float chinHeight = 0f; float headHeight = 0f; float headWidth = 0f; UpdateMechanimBoneDict(umaData, skeleton); //character needs to be moved into the root again with no rotation applied var umaTransform = umaData.transform; var originalParent = umaData.transform.parent; var originalRot = umaData.transform.localRotation; var originalPos = umaData.transform.localPosition; var umaCollider = umaData.gameObject.GetComponent <Collider>(); bool prevColliderEnabled = umaCollider != null ? umaCollider.enabled : false; //if there is a collider, disable it before we move anything if (umaCollider) { umaCollider.enabled = false; } umaTransform.SetParent(null, false); umaTransform.localRotation = Quaternion.identity; umaTransform.localPosition = Vector3.zero; if (_adjustHeight) { //Classically a human is apprx 7.5 heads tall, but we only know the height to the base of the head bone //usually head bone is actually usually in line with the lips, making the base of the chin, 1/3rd down the neck //so if we have the optional neck bone use that to estimate the base of the chin chinHeight = skeleton.GetRelativePosition(_mechanimBoneDict["Head"]).y; if (skeleton.BoneExists(_mechanimBoneDict["Neck"])) { chinHeight = skeleton.GetRelativePosition(_mechanimBoneDict["Neck"]).y + (((skeleton.GetRelativePosition(_mechanimBoneDict["Head"]).y - skeleton.GetRelativePosition(_mechanimBoneDict["Neck"]).y) / 3f) * 2f); } //apply the headRatio (by default this is 7.5) chinHeight = (chinHeight / 6.5f) * (_headRatio - 1); //so classically chinbase is 6.5 headHeights from the floor so headHeight is.. headHeight = (chinHeight / (_headRatio - 1)); //but bobble headed charcaters (toons), children or dwarves etc have bigger heads proportionally //so their overall height will greater than (chinHeight / 6.5) * 7.5 //If we have the eyes we can use those to calculate the size of the head better because classically the distance from the chin to the eyes will be half the head height if (skeleton.BoneExists(_mechanimBoneDict["LeftEye"]) || skeleton.BoneExists(_mechanimBoneDict["RightEye"])) { var eyeHeight = 0f; //if we have both eyes get the average if (skeleton.BoneExists(_mechanimBoneDict["LeftEye"]) && skeleton.BoneExists(_mechanimBoneDict["RightEye"])) { eyeHeight = (skeleton.GetRelativePosition(_mechanimBoneDict["LeftEye"]).y + skeleton.GetRelativePosition(_mechanimBoneDict["RightEye"]).y) / 2f; } else if (skeleton.BoneExists(_mechanimBoneDict["LeftEye"])) { eyeHeight = skeleton.GetRelativePosition(_mechanimBoneDict["LeftEye"]).y; } else if (skeleton.BoneExists(_mechanimBoneDict["RightEye"])) { eyeHeight = skeleton.GetRelativePosition(_mechanimBoneDict["RightEye"]).y; } headHeight = ((eyeHeight - chinHeight) * 2f); //because we do this the actual headRatio doesnt *feel* right //i.e. with toon the head ratio is more like 3, but the correct value for calcs is 6.5 //I'd prefer it if these calcs made 3 deliver the correct result } //So finally //Debug.Log("chinHeight was " + chinHeight+" headHeight was "+headHeight); //Debug.Log("Classical Height from Head = " + (headHeight * 7.5f)); //Debug.Log("Classical Height from Chin = " + ((chinHeight / 6.5f) * 7.5f)); charHeight = chinHeight + headHeight; } if (_adjustRadius) { float shouldersWidth = Mathf.Abs(skeleton.GetRelativePosition(_mechanimBoneDict["LeftUpperArm"]).x - skeleton.GetRelativePosition(_mechanimBoneDict["RightUpperArm"]).x); //Also female charcaters tend to have hips wider than their shoulders, so check that float hipsWidth = Mathf.Abs(skeleton.GetRelativePosition(_mechanimBoneDict["LeftUpperLeg"]).x - skeleton.GetRelativePosition(_mechanimBoneDict["RightUpperLeg"]).x); //the outerWidth of the hips is larger than this because the thigh muscles are so big so make this 1/3rd bigger hipsWidth = (hipsWidth / 2) * 3; //classically the width of the body is 3* headwidth for a strong character so headwidth will be headWidth = shouldersWidth / 2.75f; //but bobble headed charcaters (toons), children or dwarves etc have bigger heads proportionally and the head can be wider than the shoulders //so if we have eye bones use them to calculate head with if (skeleton.BoneExists(_mechanimBoneDict["LeftEye"]) && skeleton.BoneExists(_mechanimBoneDict["RightEye"])) { //clasically a face is 5* the width of the eyes where the distance between the pupils is 2 * eye width var eyeWidth = Mathf.Abs(skeleton.GetRelativePosition(_mechanimBoneDict["LeftEye"]).x - skeleton.GetRelativePosition(_mechanimBoneDict["RightEye"]).x) / 2; headWidth = eyeWidth * 5f; } charWidth = (shouldersWidth > headWidth || hipsWidth > headWidth) ? (shouldersWidth > hipsWidth ? shouldersWidth : hipsWidth) : headWidth; //we might also want to take into account the z depth between the hips and the head, //because if the character has been made into a horse or something it might be on all fours (and still be mechanim.Humanoid [if that even works!]) //capsule colliders break down in this scenario though (switch race to SkyCar to see what I mean), so would we want to change the orientation of the collider or the type? //does the collider orientation have any impact on physics? } //Set the UMA back umaTransform.SetParent(originalParent, false); umaTransform.localRotation = originalRot; umaTransform.localPosition = originalPos; //set any collider to its original setting if (umaCollider) { umaCollider.enabled = prevColliderEnabled; } } } else //if its a car or a castle or whatever use the bounds { charHeight = newBounds.size.y; charWidth = newBounds.size.x; } //get the scale of the overall scale bone if set float overallScale = 1f; if (skeleton.GetBoneTransform(_scaleBoneHash) != null) { overallScale = (skeleton.GetScale(_scaleBoneHash)).x; } if (_adjustHeight) { //characterHeight is what we calculated plus any radius y the user has added umaData.characterHeight = charHeight * (1 + (_radiusAdjustY * 2)); } else { umaData.characterHeight = charHeight; } if (_adjustRadius) { //characterRadius is the average of the width we calculated plus the z-depth from the bounds / 2 //we need to include the new _radiusAdjust.y (which is now an adjust for the z axis as radiusAdjustY is now its own field (used for heightAdjust) umaData.characterRadius = (charWidth + newBounds.size.z * (_radiusAdjust.x * 2)) / 2; //Debug.Log("BCM umaData.characterRadius[" + umaData.characterRadius + "] = (charWidth[" + charWidth + "] + newBounds.size.z[" + newBounds.size.z + "] * (radiusAdjust.x[" + _radiusAdjust.x + "] * 2)) / 2;"); } else { umaData.characterRadius = (charWidth + newBounds.size.z) / 2; } if (_adjustMass) { //characterMass is... what? still dont understand this quite var massZModified = (umaData.characterRadius * 2) * overallScale; var massYModified = ((1 + _radiusAdjustY) * 0.5f) * overallScale; umaData.characterMass = (_massAdjust.x * overallScale) + _massAdjust.y * massYModified + _massAdjust.z * massZModified; } else { //HUMANMALE umaData.characterMass[66.68236] = raceMass[50] * overallScale[0.907451] + 28f * umaDna.upperWeight[0.5019608] + 22f * umaDna.lowerWeight[0.3176471] umaData.characterMass = 66.68236f; //? Thats the result } //in the fixed UMA converters HumanMales mass is actually less than HumanFemales, with this thats solved //but the characters are lighter than stock by a about 5 when at they standard size and heaver than stock by about 7 when at max height //I dont know how much this matters? It can easily be fixed by tweaking the calcs, I just dont know what the numbers mean again... //Debug.Log(umaData.transform.gameObject.name + " umaData.characterMass was "+ umaData.characterMass+" characterHeight was " + umaData.characterHeight + " characterRadius was " + umaData.characterRadius); #if UNITY_EDITOR /*if (_heightDebugToolsEnabled) * AddDebugBoxes(umaData, chinHeight, headHeight, headWidth); * else * RemoveDebugBoxes(umaData);*/ #endif }