private void GenerateMesh(out Mesh mesh, out Mesh meshDebug)
        {
            mesh      = new Mesh();
            meshDebug = new Mesh {
                vertices = carrierMesh.vertices, triangles = carrierMesh.triangles, uv = carrierMesh.uv
            };

            Vector3 particleAverageNormal = Vector3.zero;

            for (int i = 0; i < particleMesh.vertexCount; i++)
            {
                particleAverageNormal += particleMesh.normals[i];
            }

            particleAverageNormal.Normalize();

            var numTriangles = carrierMesh.triangles.Length / 3;

            CombineInstance[] combine = new CombineInstance[numParticles];

            for (int i = 0; i < numParticles; i++)
            {
                var particle = new Mesh {
                    vertices  = particleMesh.vertices,
                    triangles = particleMesh.triangles,
                    colors    = particleMesh.colors,
                    normals   = particleMesh.normals,
                    tangents  = particleMesh.tangents,
                    uv        = particleMesh.uv,
                };

                int triangleIndex = carrierSampling == CarrierSampling.Random
                ? Random.Range(0, numTriangles)
                : (int)Mathf.Lerp(0, numTriangles, i / (float)numParticles);

                var vertexIndex0 = carrierMesh.triangles[triangleIndex * 3 + 0];
                var vertexIndex1 = carrierMesh.triangles[triangleIndex * 3 + 1];
                var vertexIndex2 = carrierMesh.triangles[triangleIndex * 3 + 2];

                var a = carrierMesh.vertices[vertexIndex0];
                var b = carrierMesh.vertices[vertexIndex1];
                var c = carrierMesh.vertices[vertexIndex2];

                var p = GetRandomPositionWithinTriangle(a, b, c);
                p = Vector3.Scale(p, carrierScale);

                var triangleNormal = (carrierMesh.normals[vertexIndex0] + carrierMesh.normals[vertexIndex1] +
                                      carrierMesh.normals[vertexIndex2]).normalized;
                var triangleTangent = (carrierMesh.tangents[vertexIndex0] + carrierMesh.tangents[vertexIndex1] +
                                       carrierMesh.tangents[vertexIndex2]).normalized;
                if (Random.value <= offsetAlongNormalFraction)
                {
                    p += triangleNormal * offsetAlongNormal;
                }

                var up       = Vector3.Cross(triangleNormal, triangleTangent);
                var rotation = Quaternion.LookRotation(-triangleNormal, up);

                // Random rotation for particle clouds.
                rotation = Quaternion.RotateTowards(rotation, Random.rotationUniform, particleRotationRange);

                // Rotation bias for billboard meshes.
                var biasTowards = Quaternion.Euler(biasTowardRotation);
                rotation = Quaternion.RotateTowards(rotation, biasTowards, 180f * particleRotationBias);

                // Give the particle a random forward rotation.
                rotation *= Quaternion.AngleAxis(360f * Random.value, particleAverageNormal);

                var scaleVariance     = Random.Range(1f - particleScaleVariance, 1f);
                var particleTransform = Matrix4x4.Translate(p) * Matrix4x4.Rotate(rotation) *
                                        Matrix4x4.Scale(particleScale * scaleVariance) * Matrix4x4.identity;

                var combineInstance = new CombineInstance {
                    mesh = particle, transform = particleTransform
                };
                combine[i] = combineInstance;
            }

            mesh.CombineMeshes(combine, true, true);

            // Calculate normals.
            {
                var normals = new Vector3[mesh.vertexCount];

                for (int i = 0; i < normals.Length; i++)
                {
                    int vertexIndex = oneNormalPerParticle ? i - i % particleMesh.vertexCount : i;
                    var v           = mesh.vertices[vertexIndex].normalized;

                    Vector3 noise = Vector3.zero;
                    if (noiseEnabled)
                    {
                        var nv = v * noiseFrequency;
                        var nx = NoiseUtil.Fbm(Hash.Float(noiseSeed, 0u, -1000, 1000), nv.x, noiseOctaves);
                        var ny = NoiseUtil.Fbm(Hash.Float(noiseSeed, 1u, -1000, 1000), nv.y, noiseOctaves);
                        var nz = NoiseUtil.Fbm(Hash.Float(noiseSeed, 2u, -1000, 1000), nv.z, noiseOctaves);
                        noise = Vector3.Scale(new Vector3(nx, ny, nz), noiseScale) / 0.75f * noiseAmplitude;
                    }

                    normals[i] = (v + noise).normalized;
                }

                mesh.normals = normals;
            }
        }