/// <summary> /// Creates and displays a mesh that represents a connecting pipe between the source and the /// target parts. /// </summary> protected virtual void CreateLinkPipe() { var nrmTexture = pipeNormalsTexturePath != "" ? GetTexture(pipeNormalsTexturePath, asNormalMap: true) : null; var material = CreateMaterial(GetTexture(pipeTexturePath), mainTexNrm: nrmTexture, overrideShaderName: shaderNameOverride, overrideColor: colorOverride); linkPipe = Meshes.CreateCylinder(pipeDiameter, 1f, material, partModelTransform, colliderType: Colliders.PrimitiveCollider.Shape); Colliders.UpdateColliders(linkPipe, isPhysical: pipeColliderIsPhysical); if (pipeColliderIsPhysical) { CollisionManager.IgnoreCollidersOnVessel( vessel, linkPipe.GetComponentsInChildren <Collider>()); // TODO(ihsoft): Ignore the parts when migrated to the interfaces. Colliders.SetCollisionIgnores(sourceTransform.root, targetTransform.root, true); } linkPipeMR = linkPipe.GetComponent <Renderer>(); // To speedup OnUpdate() handling. SetupPipe(linkPipe.transform, sourceJointNode.pipeAttach.position, targetJointNode.pipeAttach.position); RescaleTextureToLength(linkPipe, pipeTextureSamplesPerMeter, renderer: linkPipeMR, scaleRatio: 1 / stretchRatio); // Let the part know about the new mesh so that it could be properly highlighted. part.RefreshHighlighter(); }
/// <summary> /// Creates a mesh that represents the connecting pipe between the source and the target. /// </summary> /// <remarks>It's only called in the started state.</remarks> /// <seealso cref="CreatePipeMesh"/> /// <seealso cref="pipeTransform"/> protected virtual void CreateLinkPipe() { var colliderType = pipeColliderIsPhysical ? Colliders.PrimitiveCollider.Shape : Colliders.PrimitiveCollider.None; pipeTransform = Meshes.CreateCylinder( pipeDiameter, 1.0f, pipeMaterial, sourceTransform, colliderType: colliderType).transform; pipeTransform.GetComponent <Renderer>().sharedMaterial = pipeMaterial; pipeMeshRenderer = pipeTransform.GetComponent <Renderer>(); // To speedup OnUpdate() handling. SetupPipe(sourceJointNode.pipeAttach, targetJointNode.pipeAttach); var extraScale = 1.0f; if (pipeTextureRescaleMode == PipeTextureRescaleMode.Stretch) { extraScale /= (sourceJointNode.pipeAttach.position - targetJointNode.pipeAttach.position).magnitude; } RescalePipeTexture( pipeTransform, pipeTransform.localScale.z * extraScale, renderer: pipeMeshRenderer); }
/// <summary>Builds a model for the joint end basing on the configuration.</summary> /// <param name="node">The node to setup.</param> /// <param name="alignTo"> /// The object to align the conenctor to. If it's <c>null</c>, then the model will be parked. /// </param> protected virtual void UpdateJointNode(ModelPipeEndNode node, Transform alignTo) { var config = node.config; var makeProceduralModels = alignTo != null || !config.parkPrefabOnly; // Return the models back to the owner part to make the search working properly. if (node.rootModel != null) { node.AlignToTransform(null); } node.parkRootObject = partModelTransform; // Create basic setup. var nodeName = ModelBasename + "-pipeNode" + node.name; node.rootModel = partModelTransform.Find(nodeName) ?? new GameObject(nodeName).transform; node.rootModel.parent = partModelTransform; node.partAttach = node.rootModel.Find(PartJointTransformName) ?? new GameObject(PartJointTransformName).transform; Hierarchy.MoveToParent(node.partAttach, node.rootModel, newPosition: Vector3.zero, newRotation: Quaternion.LookRotation(Vector3.back)); node.pipeAttach = node.rootModel.Find(PipeJointTransformName) ?? new GameObject(PipeJointTransformName).transform; Hierarchy.MoveToParent(node.pipeAttach, node.rootModel, newPosition: Vector3.zero, newRotation: Quaternion.LookRotation(Vector3.forward)); // Add a pipe attachment sphere if set. const string sphereName = "pipeSphere"; var sphere = node.pipeAttach.Find(sphereName); if (config.sphereDiameter > float.Epsilon && makeProceduralModels) { if (sphere == null) { sphere = Meshes.CreateSphere(config.sphereDiameter, pipeMaterial, node.pipeAttach, colliderType: Colliders.PrimitiveCollider.Shape).transform; sphere.name = sphereName; sphere.rotation = Quaternion.LookRotation(node.partAttach.up, node.partAttach.forward); } sphere.GetComponent <Renderer>().sharedMaterial = pipeMaterial; // For performance. RescalePipeTexture(sphere, sphere.localScale.z * config.sphereDiameter * 2.0f); } else if (sphere != null) { Hierarchy.SafeDestory(sphere); } // Parking position, if defined. var parkObjectName = ModelBasename + "-park" + node.name; var parkAttach = partModelTransform.Find(parkObjectName); if (config.parkAttachAt != null) { node.parkAttach = parkAttach ?? new GameObject(parkObjectName).transform; Hierarchy.MoveToParent(node.parkAttach, partModelTransform, newPosition: config.parkAttachAt.pos, newRotation: config.parkAttachAt.rot); } else if (parkAttach != null) { Hierarchy.SafeDestory(parkAttach); } // Place prefab between the part and the pipe if specified. if (!string.IsNullOrEmpty(config.modelPath)) { // The prefab model can move to the part's model, so make a unique name for it. const string prefabName = "prefabConnector"; var prefabModel = node.rootModel.Find(prefabName) ?? Hierarchy.FindTransformByPath(partModelTransform, config.modelPath); if (prefabModel != null) { prefabModel.gameObject.SetActive(true); prefabModel.name = prefabName; prefabModel.parent = node.rootModel; prefabModel.rotation = node.partAttach.rotation * config.modelPartAttachAt.rot.Inverse(); prefabModel.position = node.partAttach.TransformPoint(config.modelPartAttachAt.pos); node.pipeAttach.rotation = prefabModel.rotation * config.modelPipeAttachAt.rot; node.pipeAttach.position = prefabModel.TransformPoint(config.modelPipeAttachAt.pos); } else { HostedDebugLog.Error(this, "Cannot find prefab model: {0}", config.modelPath); } } // The offset is intended for the sphere/arm models only. if (makeProceduralModels) { node.pipeAttach.localPosition += new Vector3(0, 0, config.sphereOffset); } // Add arm pipe. const string armName = "sphereArm"; var arm = node.pipeAttach.Find(armName); if (config.armDiameter > float.Epsilon && config.sphereOffset > float.Epsilon && makeProceduralModels) { if (arm == null) { arm = Meshes.CreateCylinder(config.armDiameter, config.sphereOffset, pipeMaterial, node.pipeAttach, colliderType: Colliders.PrimitiveCollider.Shape).transform; arm.name = armName; } arm.GetComponent <Renderer>().sharedMaterial = pipeMaterial; // For performance. arm.transform.localPosition = new Vector3(0, 0, -config.sphereOffset / 2); arm.transform.localRotation = Quaternion.LookRotation(Vector3.forward); RescalePipeTexture(arm.transform, arm.localScale.z * config.sphereOffset); } else if (arm != null) { Hierarchy.SafeDestory(arm); } // Adjust to the new target. node.AlignToTransform(alignTo); }
/// <summary>Builds a model for the joint end basing on the procedural configuration.</summary> /// <param name="modelName">Joint transform name.</param> /// <param name="config">Joint configuration from the part's config.</param> protected virtual void CreateJointEndModels(string modelName, JointConfig config) { // FIXME: Prefix the model name with the renderer name. // Make or get the root. Transform root = null; if (config.type == PipeEndType.PrefabModel) { root = Hierarchy.FindTransformByPath(partModelTransform, config.modelPath); if (root != null) { root.parent = partModelTransform; // We need the part's model to be the root. var partAttach = new GameObject(PartJointTransformName).transform; Hierarchy.MoveToParent(partAttach, root, newPosition: config.partAttachAt.pos, newRotation: config.partAttachAt.rot); var pipeAttach = new GameObject(PipeJointTransformName); Hierarchy.MoveToParent(pipeAttach.transform, root, newPosition: config.pipeAttachAt.pos, newRotation: config.pipeAttachAt.rot); } else { HostedDebugLog.Error(this, "Cannot find model: {0}", config.modelPath); config.type = PipeEndType.Simple; // Fallback. } } if (root == null) { root = new GameObject().transform; Hierarchy.MoveToParent(root, partModelTransform); var partJoint = new GameObject(PartJointTransformName).transform; Hierarchy.MoveToParent(partJoint, root); partJoint.rotation = Quaternion.LookRotation(Vector3.forward); if (config.type == PipeEndType.ProceduralModel) { // Create procedural models at the point where the pipe connects to the part's node. var material = CreateMaterial( GetTexture(config.texture), mainTexNrm: config.textureNrm != "" ? GetTexture(config.textureNrm) : null); var sphere = Meshes.CreateSphere(config.sphereDiameter, material, root, colliderType: Colliders.PrimitiveCollider.Shape); sphere.name = PipeJointTransformName; sphere.transform.rotation = Quaternion.LookRotation(Vector3.back); RescaleTextureToLength(sphere, samplesPerMeter: config.textureSamplesPerMeter); if (Mathf.Abs(config.sphereOffset) > float.Epsilon) { sphere.transform.localPosition += new Vector3(0, 0, config.sphereOffset); if (config.armDiameter > float.Epsilon) { var arm = Meshes.CreateCylinder( config.armDiameter, config.sphereOffset, material, root, colliderType: Colliders.PrimitiveCollider.Shape); arm.transform.localPosition += new Vector3(0, 0, config.sphereOffset / 2); arm.transform.LookAt(sphere.transform.position); RescaleTextureToLength(arm, samplesPerMeter: config.textureSamplesPerMeter); } } } else { // No extra models are displayed at the joint, just attach the pipe to the part's node. if (config.type != PipeEndType.Simple) { // Normally, this error should never pop up. HostedDebugLog.Error(this, "Unknown joint type: {0}", config.type); } var pipeJoint = new GameObject(PipeJointTransformName); Hierarchy.MoveToParent(pipeJoint.transform, root); pipeJoint.transform.rotation = Quaternion.LookRotation(Vector3.back); } } Colliders.UpdateColliders(root.gameObject, isPhysical: config.colliderIsPhysical); root.gameObject.SetActive(false); root.name = modelName; }