/// <summary> /// @TODO - Could split this function up to run over multiple frames by turning it into a Coroutine /// and calling 'yield return null' /// Could also use Threading /// @TODO - Could also set the # of vertices on each bend to a constant value so that we can /// avoid reallocating new vertices each time. /// </summary> public static void GenerateConduit(Conduit conduit) { #if UNITY_EDITOR if (conduit.bend.conduitOrder.Count < 2) { Debug.LogError("ConduitGenerator: GenerateConduit() Invalid Conduit Order."); return; } #endif // Start Fresh conduit.Clear(); var conduitOrder = conduit.bend.conduitOrder; var bentCenterline = conduit.centerline; var bentCenterlineBendStartIndices = conduit.centerlineBendIndices; //var straightCenterline = conduit.centerline; //var straightCenterlineBendStartIndices = conduit.centerlineBendIndices; Vector3 startPosition = Vector3.zero; Vector3 radialCenter; Vector3 reverseRadial; Quaternion rot; Marker currConduitMark = null; CenterlineMarker prevCenterMark = new CenterlineMarker(startPosition, conduitOrder[0].forwardDir, conduitOrder[0].radialDir, 0f); CenterlineMarker currCenterMark = new CenterlineMarker(); float distSoFar = 0f; float degPerRotate; int numRotations; // Record starting centerline point bentCenterline.Add(prevCenterMark); //---------------------------------------------------- // Build the 'Bent' and 'Straight' Conduit Centerline //---------------------------------------------------- for (int i = 1; i < conduitOrder.Count; ++i) { currConduitMark = conduitOrder[i]; // ConduitMarker (Calculate next 'straight segment' centerline vertice) currCenterMark.Set( prevCenterMark.point + (currConduitMark.distFromStartM - distSoFar) * prevCenterMark.forwardDir, currConduitMark.forwardDir, currConduitMark.radialDir, currConduitMark.distFromStartM ); prevCenterMark.Set(currCenterMark.point, currCenterMark.forwardDir, currCenterMark.radialDir, currCenterMark.distFromStartM); distSoFar = currConduitMark.distFromStartM; if (currConduitMark is BendMarker) { // BendMarker BendMarker currBendMark = (BendMarker)currConduitMark; // Add new Centerline Marker bentCenterline.Add(currCenterMark); bentCenterlineBendStartIndices.Add(new CenterlineIndice(BendMarkType.Start, bentCenterline.Count - 1)); radialCenter = (currCenterMark.point + currBendMark.radialDir * currBendMark.radiusM); reverseRadial = Vector3.Normalize(currCenterMark.point - radialCenter); numRotations = (int)Mathf.Ceil(currBendMark.angleDeg / s_DegreesPerVerticeSet); degPerRotate = currBendMark.angleDeg / numRotations; // Negative angle results in counterclockwise rotation (which is what we want for our Bender) // Positive angle results in clockwise rotation rot = Quaternion.AngleAxis(-degPerRotate, Vector3.Cross(currBendMark.radialDir, currBendMark.forwardDir)); float lbPerRotate = Lb(currBendMark.radiusM, degPerRotate * Mathf.Deg2Rad); // Iterate through entire rotation for (int r = 0; r < numRotations; ++r) { reverseRadial = rot * reverseRadial; currCenterMark.Set( radialCenter + reverseRadial * currBendMark.radiusM, rot * prevCenterMark.forwardDir, rot * prevCenterMark.radialDir, (distSoFar + lbPerRotate * (r + 1)) ); prevCenterMark.Set(currCenterMark.point, currCenterMark.forwardDir, currCenterMark.radialDir, currCenterMark.distFromStartM); // Record Centerline Vertices bentCenterline.Add(currCenterMark); } bentCenterlineBendStartIndices.Add(new CenterlineIndice(BendMarkType.End, bentCenterline.Count - 1)); // Update centerline distance so far distSoFar += Lb(currBendMark.radiusM, currBendMark.angleDeg * Mathf.Deg2Rad); } else { // Add new Centerline Marker bentCenterline.Add(currCenterMark); } } //------------------------------------------- // Build the bent 'Mesh' //------------------------------------------- // To Create SubMeshes: // Use Mesh.subMeshCount to set the number of submeshes, then SetTriangles() to set the triangle list for each submesh. // There isn't a "main" mesh in this case. Then, make a Material array (where each material corresponds to each submesh), // and set renderer.materials to that Material array. // Make Conduit Vertices Vector3[] conduitVerts = new Vector3[s_NumberOfSides * bentCenterline.Count]; Vector2[] conduitUVs = new Vector2[s_NumberOfSides * bentCenterline.Count]; int[] conduitTriangles = new int[s_NumberOfSides * 6 * (bentCenterline.Count - 1)]; // Indices // Get Reference to Circle Mesh Vertices var circleVerts = s_CircleVertices; // Move Circle Mesh Along Centerline, Rotate, and Record Vertice Positions Vector3 prevForwardDir = Vector3.forward; for (int v = 0; v < bentCenterline.Count; ++v) { // @TODO - Could make this more efficient by caching the rotation from the centerline calculations // @TODO - Could also use the SetFromToRotation instead of allocating new Quaternion each time rot = Quaternion.FromToRotation(prevForwardDir, bentCenterline[v].forwardDir); for (int s = 0; s < s_NumberOfSides; ++s) { conduitVerts[v * s_NumberOfSides + s] = (rot * circleVerts[s + 1]) + (bentCenterline[v].point - bentCenterline[0].point); } } // B D // |\``| // | \ | // |__\| // A C int C; int totalSides = s_NumberOfSides * (bentCenterline.Count - 1); for (int s = 0, t; s < totalSides; ++s) { //int sideStartI = (s % m_NumberOfSides) + (s / m_NumberOfSides) * m_NumberOfSides; // 4 vertices per side t = s * 6; if ((s + 1) % s_NumberOfSides == 0) { C = (s / s_NumberOfSides) * s_NumberOfSides; } else { C = s + 1; } // Bottom Triangle conduitTriangles[t] = s; // A conduitTriangles[t + 1] = s + s_NumberOfSides; // B conduitTriangles[t + 2] = C; // C // Top Triangle conduitTriangles[t + 3] = C; // C conduitTriangles[t + 4] = s + s_NumberOfSides; // B conduitTriangles[t + 5] = C + s_NumberOfSides; // D } // Calculate UVs // TODO: (Currently these UVs are stretched/compressed along the bent areas - i.e. inaccurate) for (int i = 0; i < conduitUVs.Length; ++i) { conduitUVs[i] = new Vector2(circleVerts[i % s_NumberOfSides].x, (i % s_NumberOfSides) / (float)bentCenterline.Count); } conduit.SetMesh(conduitVerts, conduitUVs, conduitTriangles); conduit.conduitDiameterM = conduitDiameterM; // Remember Conduit Data s_LastConduitVerts = conduitVerts; s_LastConduitUVs = conduitUVs; s_LastConduitTris = conduitTriangles; //Debug.Log( "ConduitGenerator: GenerateConduit() Model Name: " + conduit.bend.modelName ); } // End GenConduitMesh()