void OnCollisionStay(Collision collisionInfo) { //if (collisionInfo.rigidbody != part.Rigidbody || !moduleAnimateGeneric) if (!moduleAnimateGeneric || !animationStopper) { return; } if (!moduleAnimateGeneric.IsMoving()) { return; } foreach (ContactPoint contact in collisionInfo.contacts) { ColliderInfo thisColliderInfo = GetColliderInfo(contact.thisCollider); // Check if this is an internal collider moving in respect to the part, // or if it is a child part moving due to being animated by AnimatedAttachment if (thisColliderInfo.part == part) { PosRot currentPosRot = PosRot.GetPosRot(thisColliderInfo.collider.transform, part); Vector3 movement = currentPosRot.position - thisColliderInfo.posRot.position; double angle = Quaternion.Angle(currentPosRot.rotation, thisColliderInfo.posRot.rotation); thisColliderInfo.posRot = currentPosRot; // Set a minimum level if (movement.IsSmallerThan(0.1f) && angle < 0.1) { continue; } printf("Animation stopped by a collision between %s and %s (movement %s, angle %s)", thisColliderInfo.collider.name, collisionInfo.collider.name, movement, angle); } else { printf("Animation stopped by a collision between %s.%s and %s", thisColliderInfo.part.name, thisColliderInfo.collider.name, collisionInfo.collider.name); } float time = moduleAnimateGeneric.animTime; if (moduleAnimateGeneric.revClampPercent) { time = 1 - time; } moduleAnimateGeneric.deployPercent = 100.0f * time; moduleAnimateGeneric.allowDeployLimit = true; break; } }
private void AddColliders(List <ColliderInfo> colliderInfos, Part part) { printf("Adding part %s to collider list", part.name); foreach (Collider collider in part.GetPartColliders()) { ColliderInfo colliderInfo = new ColliderInfo(); colliderInfo.collider = collider; colliderInfo.part = part; colliderInfo.posRot = PosRot.GetPosRot(collider.transform, part); colliderInfos.Add(colliderInfo); } foreach (Part child in part.children) { AddColliders(colliderInfos, child); } }
private void UpdateAttachments(List <AttachedPartInfo> attachedPartInfos, bool debugPeriodic) { // Bail out if init failed if (attachedPartInfos == null) { if (debugPeriodic) { print("Empty attach node info list!"); } return; } if (debugPeriodic) { printf("Updating %s/%s parts", attachedPartInfos.Count, part.children.Count); } foreach (AttachNode attachNode in part.attachNodes) { // We can't move attach nodes that are positioned in the cfg file if (attachNode.nodeTransform == null) { if (debugPeriodic) { printf("Skipping %s since it lacks a node transform", attachNode.id); } continue; } // Get the position and rotation of the node transform relative to the part. // The nodeTransform itself will only contain its positions and rotation // relative to the immediate parent in the model PosRot attachNodePosRot = PosRot.GetPosRot(attachNode.nodeTransform, part); // We can't animate decoupling shrouds if (attachNodePosRot == null) { if (debugPeriodic) { printf("Skipping %s since it is a decoupler shroud", attachNode.id); } continue; } // Update the attachNode attachNode.position = attachNodePosRot.position; attachNode.orientation = attachNodePosRot.orientation; } for (int i = 0; i < part.children.Count; i++) { Part child = part.children[i]; // Create new entries in the list if needed if (i >= attachedPartInfos.Count) { if (debug) { printf("Adding child %s [%s]", child.name, i); } attachedPartInfos.Add(new AttachedPartInfo(this, child)); } AttachedPartInfo attachedPartInfo = attachedPartInfos[i]; // Assign parts to info structs newly loaded from a save file if (attachedPartInfo.loaded) { attachedPartInfo.attachedPart = child; attachedPartInfo.loaded = false; if (debug) { printf("Assigning child %s [%s]", attachedPartInfo.attachedPart, i); } } else // Remove stale entries if (attachedPartInfo.attachedPart != child) { if (debug) { printf("Deleting child %s/%s", attachedPartInfo.attachedPart, child.name); } attachedPartInfos.Remove(attachedPartInfo); i--; continue; } if (debugPeriodic) { printf("Updating child %s [%s]", attachedPartInfo, i); } attachedPartInfo.UpdateAttachments(flightState, debug, debugPeriodic); } // Continue deleting surplus entries while (attachedPartInfos.Count > part.children.Count) { AttachedPartInfo attachedPartInfo = attachedPartInfos[part.children.Count]; if (debug) { printf("Deleting child %s [%s]", attachedPartInfo.attachedPart, part.children.Count); } attachedPartInfos.Remove(attachedPartInfo); } }
public void UpdateAttachments(State flightState, bool debug, bool debugPeriodic) { // If the is no actual part attached to the attach node, then we can bail out. // Part attachedPart = GetAttachedPart(); if (debugPeriodic) { printf("UA: %s %s %s %s", attachedPart != null ? attachedPart.name : null, nodeType, stackAttachNode != null ? stackAttachNode.id : "null", stackAttachNode != null ? stackAttachNode.attachedPartId.ToString() : "null"); } // We don't want to mess with the joint attaching this part to its parent. // Also, take of the special case where they are both null, otherwise we // will incorrectly get a match between them, resulting in loss of function // if the animated part is the root part. if ((attachedPart == animatedAttachment.part.parent) && (attachedPart != null)) { if (debugPeriodic) { printf("Skipping parent"); } return; } switch (nodeType) { case AttachNode.NodeType.Surface: if (collider == null) { SetCollider(); if (debug) { printf("Setting collider to %s", collider.name); } } break; case AttachNode.NodeType.Stack: if (stackAttachNode == null) { stackAttachNode = animatedAttachment.part.FindAttachNodeByPart(attachedPart); // Sometimes life throws lemons at you, like when the user actvated attachments in flight if (stackAttachNode == null) { // Try again as surface attached nodeType = AttachNode.NodeType.Surface; return; } if (debug) { printf("Setting attach node to %s", stackAttachNode.id); } } break; } Transform referenceTransform = GetReferenceTransform(); // If this attach node is defined in the cfg, then bail out now, it will not be movable if (referenceTransform == null) { if (debugPeriodic) { printf("Skipping cfg based node: %s", stackAttachNode.id); } return; } // Get the position and rotation of the node transform relative to the part. // The nodeTransform itself will only contain its positions and rotation // relative to the immediate parent in the model PosRot referencePosRot = PosRot.GetPosRot(referenceTransform, animatedAttachment.part); // We can't animate decoupling shrouds if (referencePosRot == null) { if (debugPeriodic) { printf("Skipping decoupler shroud"); } return; } bool active = animatedAttachment.activated; if (EditorLogic.fetch && (EditorLogic.fetch.EditorConstructionMode != ConstructionMode.Place)) { active = false; } // Take note of newly attached parts, including at initial ship load if (attachedPart == null || !active) { if (debugPeriodic) { if (attachedPart == null) { printf("No part attached"); } } attachedPartOffset = null; return; } if (attachedPartOffset == null || attachedPartOriginal == null) { // Get attached part position relative to this part PosRot localPosRot = new PosRot(); Transform parent = attachedPart.transform.parent; // Let the engine calculate the local position instead of doing the calculation ourselves.. attachedPart.transform.parent = animatedAttachment.part.transform; localPosRot.position = attachedPart.transform.localPosition; localPosRot.rotation = attachedPart.transform.localRotation; attachedPart.transform.parent = parent; // We could do parenting trick for this too, but seems we loose the scaling if (attachedPartOffset == null) { printf("Recording attachedPartOffset"); attachedPartOffset = new PosRot(); attachedPartOffset.rotation = referencePosRot.rotation.Inverse() * localPosRot.rotation; attachedPartOffset.position = referencePosRot.rotation.Inverse() * (localPosRot.position - referencePosRot.position); } if (attachedPartOriginal == null) { printf("Recording attachedPartOriginal"); attachedPartOriginal = new PosRot(); attachedPartOriginal.rotation = localPosRot.rotation; } } // Calculate the attached parts position in the frame of reference of this part PosRot attachedPartPosRot = new PosRot { rotation = referencePosRot.rotation * attachedPartOffset.rotation, position = referencePosRot.position + referencePosRot.rotation * attachedPartOffset.position }; /* A sub part can either be connected directly by their transform having a parent transform, * or be connected through a joint. In the first case, the sub part will directly move with * their parent as their position is in in the reference frame of the parent local space. * In the latter case, the sub part lacks a parent transform, and the position is in the vessel * space instead, and parts are held together by forces working through the joints. * The first case occurs in two situations. In the VAB editor, all parts are connected by * parent transforms. And, during flight, a physicsless part will also be connected to the parent * this way - for example some science parts. * Joints are used for normal physics based parts during flight. */ if (attachedPart.transform.parent != null) { // If a parent was found, we will just update the position of the part directly since no physics is involved attachedPart.transform.localRotation = attachedPartPosRot.rotation; attachedPart.transform.localPosition = attachedPartPosRot.position; if (debugPeriodic) { printf("Updated pos without physics"); } // There is nothing more to do, so bail out return; } // In the editor, while changing action groups, the parent will be null for some reason. // We can catch that here by making sure there exists a joint if (attachedPart.attachJoint == null) { if (debugPeriodic) { printf("No attach joint found"); } return; } // Things get tricker if the parts are connected by joints. We need to setup the joint // to apply forces to the sub part. ConfigurableJoint joint = attachedPart.attachJoint.Joint; // It is not possible to change values of a JointDrive after creation, so we must create a // new one and apply it to the joint. Seems we can't only create it at startup either. switch (joint.name) { case "AnimatedAttachment": if (joint.xMotion != ConfigurableJointMotion.Free && flightState == State.STARTED) { animatedAttachment.initJointDrive = true; } break; case "MechanicsToolkit": break; default: animatedAttachment.initJointDrive = true; break; } if (animatedAttachment.initJointDrive) { animatedAttachment.initJointDrive = false; joint.name = "AnimatedAttachment"; jointDrive = new JointDrive(); printf("Creating a new drive mode. Previous: %s, %s, %s, %s, %s", joint.name, joint.xDrive.positionSpring, animatedAttachment.part.name, animatedAttachment.part.started, joint.angularXMotion); /* * printf(string.Format("maximumForce: {0}", animatedAttachment.maximumForce)); * printf(string.Format("positionDamper: {0}", animatedAttachment.positionDamper)); * printf(string.Format("positionSpring: {0}", animatedAttachment.positionSpring)); */ // The joint will not respond to changes to targetRotation/Position in locked mode, // so change it to free in all directions joint.xMotion = ConfigurableJointMotion.Free; joint.yMotion = ConfigurableJointMotion.Free; joint.zMotion = ConfigurableJointMotion.Free; joint.angularXMotion = ConfigurableJointMotion.Free; joint.angularYMotion = ConfigurableJointMotion.Free; joint.angularZMotion = ConfigurableJointMotion.Free; // Create a new joint with settings from the cfg file or user selection jointDrive.maximumForce = animatedAttachment.maximumForce; jointDrive.positionDamper = animatedAttachment.positionDamper; jointDrive.positionSpring = animatedAttachment.positionSpring; // Same drive in all directions.. is there benefits of separating them? joint.angularXDrive = jointDrive; joint.angularYZDrive = jointDrive; joint.xDrive = jointDrive; joint.yDrive = jointDrive; joint.zDrive = jointDrive; //} if (debug) { printf("%s", joint); } } if (debug) { printf("%s", attachedPartPosRot); } if (debug) { printf("%s", attachedPartOriginal); } // Update the joint.targetRotation using this convenience function, since the joint // reference frame has weird axes. Arguments are current and original rotation. joint.SetTargetRotationLocal( attachedPartPosRot.rotation, attachedPartOriginal.rotation); /* Move the attached part by updating the connectedAnchor instead of the joint.targetPosition. * This is easier since the anchor is in the reference frame of this part, and we already have the * position in that reference frame. It also makes sense from the view that since it really is the * attachment point of the attached part that is moving. There might be benefits of using the targetPosition * though, and should be possible to calculate it fairly easily if needed. */ joint.connectedAnchor = referencePosRot.position; // Make sure the target position is zero joint.targetPosition = Vector3.zero; // This scaling and rotation is to convert to joint space... maybe? // Determined by random tinkering and is magical as far as I am concerned joint.anchor = attachedPartOffset.rotation.Inverse() * Vector3.Scale( new Vector3(-1, -1, -1), attachedPartOffset.position); if (debugPeriodic) { printf("%s; %s; %s -> %s; %s -> %s; %s", referencePosRot, attachedPartPosRot, attachedPartOffset, attachedPartOriginal.rotation.eulerAngles, joint.targetRotation.eulerAngles, joint.anchor, joint.connectedAnchor ); } // Debug info if (debug) { // Show debug vectors for the child part if (axisJoint == null) { axisJoint = new AxisInfo(joint.transform); } if (lineAnchor == null) { lineAnchor = new LineInfo(animatedAttachment.part.transform, Color.cyan); } lineAnchor.Update(Vector3.zero, joint.connectedAnchor); if (lineNodeToPart == null) { lineNodeToPart = new LineInfo(animatedAttachment.part.transform, Color.magenta); } lineNodeToPart.Update( referencePosRot.position, attachedPartPosRot.position); } else { if (axisJoint != null) { axisJoint = null; } } // Debug info if (debug) { // Show debug vectors for the attachNodes if (orientationAttachNode == null) { orientationAttachNode = new OrientationInfo(animatedAttachment.part.transform, referencePosRot.position, referencePosRot.position + attachedPartOffset.orientation); } if (stackAttachNode != null) { orientationAttachNode.Update(referencePosRot.position, referencePosRot.position + stackAttachNode.orientation); } } else { if (orientationAttachNode != null) { orientationAttachNode = null; } } }