private static void SetupBones(Transform parent, MeshData md, Parachute p, ParachuteMeshConfig cf) { var c = p.Config; float spanStep = c.Span / c.NumCells; p.Bones = new Transform[c.NumCells]; for (int i = 0; i < c.NumCells; i++) { var go = new GameObject("Cell_" + i); var t = go.transform; t.position = Vector3.right * (0.5f * spanStep) + Vector3.right * (spanStep * i); t.rotation = Quaternion.Euler(-0.5f, 0f, 0f); // Todo: align with median chord t.parent = parent; var b = t.worldToLocalMatrix * parent.localToWorldMatrix; md.Bones.Add(t); md.BindPoses.Add(b); p.Bones[i] = t; } }
public static void CreateSkinnedMesh(Parachute p, ParachuteMeshConfig cf, Material mat) { if (_meshData == null) { throw new Exception("UnityParachuteMeshFactory needs to be initialized before use!"); } Clear(); // Todo: Reuse mesh, game objects, as much as possible. Create pool of cells. Profiler.BeginSample("GOCreation"); GameObject mo = new GameObject("ParachuteMesh"); mo.transform.parent = p.Root; SkinnedMeshRenderer r = mo.AddComponent <SkinnedMeshRenderer>(); r.updateWhenOffscreen = true; // Todo: This is not great r.skinnedMotionVectors = true; r.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; Profiler.EndSample(); Profiler.BeginSample("SetupBones"); SetupBones(mo.transform, _meshData, p, cf); Profiler.EndSample(); Profiler.BeginSample("EdgeLoops"); CreateCellLoops(_meshData, _edgeLoops, p.Config, cf); Profiler.EndSample(); Profiler.BeginSample("Caps"); CreateCap(_edgeLoops[0], _meshData, cf, 0, -0.33f); int startIndex = _meshData.Indices.Count; CreateCap(_edgeLoops[_edgeLoops.Count - 1], _meshData, cf, p.Config.NumCells - 1, 0.33f); int endIndex = _meshData.Indices.Count; InvertFaces(_meshData, startIndex, endIndex); Profiler.EndSample(); Profiler.BeginSample("CreateMesh"); Mesh m = MeshData.CreateMesh(_meshData); Profiler.EndSample(); Profiler.BeginSample("Finalize"); mat.color = p.Config.Color; r.bones = _meshData.Bones.ToArray(); r.sharedMesh = m; r.material = mat; p.CanopyMesh = r; Profiler.EndSample(); }
public static void Initialize(ParachuteMeshConfig cf) { _meshData = new MeshData(); const int maxLoops = 256; Queue <int[]> q = new Queue <int[]>(maxLoops); for (int i = 0; i < maxLoops; i++) { int[] loop = new int[cf.CellChordLoops + cf.CellChordLoops - 2]; // Todo: Make more straightforward q.Enqueue(loop); } _loopPool = new Pool <int[]>(q, ClearLoop); _edgeLoops = new List <int[]>(maxLoops); }
private static void CreateCap(int[] loop, MeshData md, ParachuteMeshConfig cf, int cell, float bulge) { // Create a strip of verts through the middle of the foil. List <int> midVerts = new List <int>(); midVerts.Add(loop[0]); for (int i = 1; i < loop.Length / 2; i++) { Vector3 a = md.Vertices[loop[i]]; Vector3 b = md.Vertices[loop[loop.Length - i]]; Vector3 v = Vector3.Lerp(a, b, 0.5f); v.x += cf.ChordUpperThickness.Evaluate(i / (loop.Length / 2f)) * bulge; md.Vertices.Add(v); var w = new BoneWeight() { boneIndex0 = cell, weight0 = 1f }; md.Weights.Add(w); midVerts.Add(md.Vertices.Count - 1); } midVerts.Add(loop[loop.Length / 2]); // Apart from the front and aft points, this can all be quads for (int i = 1; i < midVerts.Count - 2; i++) { // Bot md.Indices.Add(midVerts[i + 0]); md.Indices.Add(loop[i + 1]); md.Indices.Add(loop[i + 0]); md.Indices.Add(midVerts[i + 0]); md.Indices.Add(midVerts[i + 1]); md.Indices.Add(loop[i + 1]); // // Top md.Indices.Add(loop[loop.Length - (i + 0)]); md.Indices.Add(loop[loop.Length - (i + 1)]); md.Indices.Add(midVerts[i + 0]); md.Indices.Add(loop[loop.Length - (i + 1)]); md.Indices.Add(midVerts[i + 1]); md.Indices.Add(midVerts[i + 0]); } // Now finish up by adding 4 missing corner triangles // Afttop md.Indices.Add(midVerts[0]); md.Indices.Add(midVerts[1]); md.Indices.Add(loop[1]); // Aftbot md.Indices.Add(midVerts[1]); md.Indices.Add(midVerts[0]); md.Indices.Add(loop[loop.Length - 1]); // FrontTop md.Indices.Add(midVerts[midVerts.Count - 2]); md.Indices.Add(midVerts[midVerts.Count - 1]); md.Indices.Add(loop[loop.Length / 2 - 1]); // FrontBot md.Indices.Add(midVerts[midVerts.Count - 1]); md.Indices.Add(midVerts[midVerts.Count - 2]); md.Indices.Add(loop[loop.Length / 2] + 1); }
private static void CreateCellEdgeLoop(MeshData md, int[] loop, ParachuteConfig cfg, ParachuteMeshConfig cf, float spanPos, float scale) { // Note: top vertex row has the outermost chord tip verts. Bottom reuses those. int idx = 0; float zScale = (0.8f + scale * 0.2f); float zMin = -0.5f * cfg.Chord * zScale; // Top for (int i = 0; i < cf.CellChordLoops; i++) { float chordLerp = i / (float)(cf.CellChordLoops - 1); float c = cf.ChordLineCurvature.Evaluate(chordLerp); c += cf.ChordUpperThickness.Evaluate(chordLerp) * scale; Vector3 v = new Vector3( spanPos, c, zMin + chordLerp * cfg.Chord * zScale ); md.Vertices.Add(v); BoneWeight w = GetBoneWeight(v, cfg); md.Weights.Add(w); loop[idx++] = md.Vertices.Count - 1; } // Bottom for (int i = 1; i < cf.CellChordLoops - 1; i++) { float chordLerp = 1f - i / (float)(cf.CellChordLoops - 1); float c = cf.ChordLineCurvature.Evaluate(chordLerp); c += cf.ChordLowerThickness.Evaluate(chordLerp) * scale; Vector3 v = new Vector3( spanPos, c, zMin + chordLerp * cfg.Chord * zScale ); md.Vertices.Add(v); BoneWeight w = GetBoneWeight(v, cfg); md.Weights.Add(w); loop[idx++] = md.Vertices.Count - 1; } }
private static void CreateCellLoops(MeshData md, List <int[]> loops, ParachuteConfig cfg, ParachuteMeshConfig cf) { // Create the loops float spanStep = cfg.Span / cfg.NumCells / cf.CellSpanLoops; for (int i = 0; i < cfg.NumCells; i++) { for (int j = 0; j < cf.CellSpanLoops; j++) { float z = spanStep * (i * cf.CellSpanLoops + j); float s = cf.SpanCellCurvature.Evaluate(j / ((float)cf.CellSpanLoops)); var l = _loopPool.Take(); CreateCellEdgeLoop(md, l, cfg, cf, z, s); loops.Add(l); } } // Create the last loop var ll = _loopPool.Take(); CreateCellEdgeLoop(md, ll, cfg, cf, cfg.Span, cf.SpanCellCurvature.Evaluate(1f)); loops.Add(ll); // Create the faces between the loops for (int i = 0; i < loops.Count - 1; i++) { CreateLoopFaces(loops[i], loops[i + 1], md); } }