///<summary> /// Constructs a new CompoundCollidable. ///</summary> ///<param name="compoundShape">Compound shape to use for the collidable.</param> public CompoundCollidable(CompoundShape compoundShape) : base(compoundShape) { Events = new CompoundEventManager(); for (int i = 0; i < compoundShape.shapes.Count; i++) { CompoundChild child = GetChild(compoundShape.shapes.Elements[i], i); children.Add(child); } hierarchy = new CompoundHierarchy(this); }
///<summary> /// Constructs a new compound hierarchy. ///</summary> ///<param name="owner">Owner of the hierarchy.</param> public CompoundHierarchy(CompoundCollidable owner) { Owner = owner; CompoundChild[] children = new CompoundChild[owner.children.Count]; Array.Copy(owner.children.Elements, children, owner.children.Count); //In order to initialize a good tree, the local space bounding boxes should first be computed. //Otherwise, the tree would try to create a hierarchy based on a bunch of zeroed out bounding boxes! for (int i = 0; i < children.Length; i++) { children[i].CollisionInformation.worldTransform = owner.Shape.shapes.Elements[i].LocalTransform; children[i].CollisionInformation.UpdateBoundingBoxInternal(0); } Tree = new BoundingBoxTree <CompoundChild>(children); }
/// <summary> /// Removes a child from a compound collidable. /// </summary> /// <param name="compound">Compound collidable to remove a child from.</param> /// <param name="removalPredicate">Callback which analyzes a child and determines if it should be removed from the compound.</param> /// <param name="childContributions">Distribution contributions from all shapes in the compound shape. This can include shapes which are not represented in the compound.</param> /// <param name="distributionInfo">Distribution information of the new compound.</param> /// <param name="weight">Total weight of the new compound.</param> /// <param name="removedWeight">Weight removed from the compound.</param> /// <param name="removedCenter">Center of the chunk removed from the compound.</param> /// <returns>Whether or not any removal took place.</returns> public static bool RemoveChildFromCompound(CompoundCollidable compound, Func <CompoundChild, bool> removalPredicate, IList <ShapeDistributionInformation> childContributions, out ShapeDistributionInformation distributionInfo, out float weight, out float removedWeight, out Vector3 removedCenter) { bool removalOccurred = false; removedWeight = 0; removedCenter = new Vector3(); for (int i = compound.children.Count - 1; i >= 0; i--) { //The shape doesn't change during this process. The entity could, though. //All of the other collidable information, like the Tag, CollisionRules, Events, etc. all stay the same. CompoundChild child = compound.children.Elements[i]; if (removalPredicate(child)) { removalOccurred = true; CompoundShapeEntry entry = child.Entry; removedWeight += entry.Weight; Vector3 toAdd; Vector3.Multiply(ref entry.LocalTransform.Position, entry.Weight, out toAdd); Vector3.Add(ref removedCenter, ref toAdd, out removedCenter); //The child event handler must be unhooked from the compound. child.CollisionInformation.events.Parent = null; compound.children.FastRemoveAt(i); } } if (!removalOccurred) { //No removal occurred, so we cannot proceed. distributionInfo = new ShapeDistributionInformation(); weight = 0; return(false); } if (removedWeight > 0) { Vector3.Divide(ref removedCenter, removedWeight, out removedCenter); } //Compute the contributions from the original shape to the new form of the original collidable. distributionInfo = new ShapeDistributionInformation(); weight = 0; for (int i = compound.children.Count - 1; i >= 0; i--) { CompoundChild child = compound.children.Elements[i]; CompoundShapeEntry entry = child.Entry; ShapeDistributionInformation contribution = childContributions[child.shapeIndex]; Vector3.Add(ref contribution.Center, ref entry.LocalTransform.Position, out contribution.Center); Vector3.Multiply(ref contribution.Center, child.Entry.Weight, out contribution.Center); Vector3.Add(ref contribution.Center, ref distributionInfo.Center, out distributionInfo.Center); distributionInfo.Volume += contribution.Volume; weight += entry.Weight; } //Average the center out. Vector3.Divide(ref distributionInfo.Center, weight, out distributionInfo.Center); //Note that the 'entry' is from the Shape, and so the translations are local to the shape's center. //That is not technically the center of the new collidable- distributionInfo.Center is. //Offset the child collidables by -distributionInfo.Center using their local offset. Vector3 offset; Vector3.Negate(ref distributionInfo.Center, out offset); //Compute the unscaled inertia tensor. for (int i = compound.children.Count - 1; i >= 0; i--) { CompoundChild child = compound.children.Elements[i]; CompoundShapeEntry entry = child.Entry; Vector3 transformedOffset; Quaternion conjugate; Quaternion.Conjugate(ref entry.LocalTransform.Orientation, out conjugate); Quaternion.Transform(ref offset, ref conjugate, out transformedOffset); child.CollisionInformation.localPosition = transformedOffset; ShapeDistributionInformation contribution = childContributions[child.shapeIndex]; CompoundShape.TransformContribution(ref entry.LocalTransform, ref distributionInfo.Center, ref contribution.VolumeDistribution, entry.Weight, out contribution.VolumeDistribution); //Vector3.Add(ref entry.LocalTransform.Position, ref offsetA, out entry.LocalTransform.Position); Matrix3x3.Add(ref contribution.VolumeDistribution, ref distributionInfo.VolumeDistribution, out distributionInfo.VolumeDistribution); } //Normalize the volume distribution. Matrix3x3.Multiply(ref distributionInfo.VolumeDistribution, 1 / weight, out distributionInfo.VolumeDistribution); //Update the hierarchies of the compounds. //TODO: Create a new method that does this quickly without garbage. Requires a new Reconstruct method which takes a pool which stores the appropriate node types. compound.hierarchy.Tree.Reconstruct(compound.children); return(true); }
/// <summary> /// Splits a single compound collidable into two separate compound collidables and computes information needed by the simulation. /// </summary> /// <param name="splitPredicate">Delegate which determines if a child in the original compound should be moved to the new compound.</param> /// <param name="a">Original compound to be split. Children in this compound will be removed and added to the other compound.</param> /// <param name="b">Compound to receive children removed from the original compound.</param> /// <param name="distributionInfoA">Volume, volume distribution, and center information about the new form of the original compound collidable.</param> /// <param name="distributionInfoB">Volume, volume distribution, and center information about the new compound collidable.</param> /// <param name="weightA">Total weight associated with the new form of the original compound collidable.</param> /// <param name="weightB">Total weight associated with the new compound collidable.</param> /// <returns>Whether or not the predicate returned true for any element in the original compound and split the compound.</returns> public static bool SplitCompound(Func <CompoundChild, bool> splitPredicate, CompoundCollidable a, CompoundCollidable b, out ShapeDistributionInformation distributionInfoA, out ShapeDistributionInformation distributionInfoB, out float weightA, out float weightB) { bool splitOccurred = false; for (int i = a.children.Count - 1; i >= 0; i--) { //The shape doesn't change during this process. The entity could, though. //All of the other collidable information, like the Tag, CollisionRules, Events, etc. all stay the same. CompoundChild child = a.children.Elements[i]; if (splitPredicate(child)) { splitOccurred = true; a.children.FastRemoveAt(i); b.children.Add(child); //The child event handler must be unhooked from the old compound and given to the new one. child.CollisionInformation.events.Parent = b.Events; } } if (!splitOccurred) { //No split occurred, so we cannot proceed. distributionInfoA = new ShapeDistributionInformation(); distributionInfoB = new ShapeDistributionInformation(); weightA = 0; weightB = 0; return(false); } //Compute the contributions from the original shape to the new form of the original collidable. distributionInfoA = new ShapeDistributionInformation(); weightA = 0; distributionInfoB = new ShapeDistributionInformation(); weightB = 0; for (int i = a.children.Count - 1; i >= 0; i--) { CompoundChild child = a.children.Elements[i]; CompoundShapeEntry entry = child.Entry; Vector3 weightedCenter; Vector3.Multiply(ref entry.LocalTransform.Position, entry.Weight, out weightedCenter); Vector3.Add(ref weightedCenter, ref distributionInfoA.Center, out distributionInfoA.Center); distributionInfoA.Volume += entry.Shape.Volume; weightA += entry.Weight; } for (int i = b.children.Count - 1; i >= 0; i--) { CompoundChild child = b.children.Elements[i]; CompoundShapeEntry entry = child.Entry; Vector3 weightedCenter; Vector3.Multiply(ref entry.LocalTransform.Position, entry.Weight, out weightedCenter); Vector3.Add(ref weightedCenter, ref distributionInfoB.Center, out distributionInfoB.Center); distributionInfoB.Volume += entry.Shape.Volume; weightB += entry.Weight; } //Average the center out. if (weightA > 0) { Vector3.Divide(ref distributionInfoA.Center, weightA, out distributionInfoA.Center); } if (weightB > 0) { Vector3.Divide(ref distributionInfoB.Center, weightB, out distributionInfoB.Center); } //Note that the 'entry' is from the Shape, and so the translations are local to the shape's center. //That is not technically the center of the new collidable- distributionInfoA.Center is. //Offset the child collidables by -distributionInfoA.Center using their local offset. Vector3 offsetA; Vector3.Negate(ref distributionInfoA.Center, out offsetA); Vector3 offsetB; Vector3.Negate(ref distributionInfoB.Center, out offsetB); //Compute the unscaled inertia tensor. for (int i = a.children.Count - 1; i >= 0; i--) { CompoundChild child = a.children.Elements[i]; CompoundShapeEntry entry = child.Entry; Vector3 transformedOffset; Quaternion conjugate; Quaternion.Conjugate(ref entry.LocalTransform.Orientation, out conjugate); Quaternion.Transform(ref offsetA, ref conjugate, out transformedOffset); child.CollisionInformation.localPosition = transformedOffset; Matrix3x3 contribution; CompoundShape.TransformContribution(ref entry.LocalTransform, ref distributionInfoA.Center, ref entry.Shape.volumeDistribution, entry.Weight, out contribution); Matrix3x3.Add(ref contribution, ref distributionInfoA.VolumeDistribution, out distributionInfoA.VolumeDistribution); } for (int i = b.children.Count - 1; i >= 0; i--) { CompoundChild child = b.children.Elements[i]; CompoundShapeEntry entry = child.Entry; Vector3 transformedOffset; Quaternion conjugate; Quaternion.Conjugate(ref entry.LocalTransform.Orientation, out conjugate); Quaternion.Transform(ref offsetB, ref conjugate, out transformedOffset); child.CollisionInformation.localPosition = transformedOffset; Matrix3x3 contribution; CompoundShape.TransformContribution(ref entry.LocalTransform, ref distributionInfoB.Center, ref entry.Shape.volumeDistribution, entry.Weight, out contribution); Matrix3x3.Add(ref contribution, ref distributionInfoB.VolumeDistribution, out distributionInfoB.VolumeDistribution); } //Normalize the volume distribution. Matrix3x3.Multiply(ref distributionInfoA.VolumeDistribution, 1 / weightA, out distributionInfoA.VolumeDistribution); Matrix3x3.Multiply(ref distributionInfoB.VolumeDistribution, 1 / weightB, out distributionInfoB.VolumeDistribution); //Update the hierarchies of the compounds. //TODO: Create a new method that does this quickly without garbage. Requires a new Reconstruct method which takes a pool which stores the appropriate node types. a.hierarchy.Tree.Reconstruct(a.children); b.hierarchy.Tree.Reconstruct(b.children); return(true); }