Beispiel #1
0
        public int randomSeed = 0;                                      // A property specific random seed;

        // CONVERT COORDINATE SYSTEM //
        //
        // TODO: Move it to AmpsHelpers.
        public Vector4 ConvertCoordinateSystem(Vector4 v, int particleIndex)
        {
            Vector3 returnValue = v;

            switch (coordSystem)
            {
            case AmpsHelpers.eCoordSystems.World:
                //switch (coordSystemConversionMode)
                //{
                //    case eCoordSystemConversionMode.AsPosition:
                //        break;
                //    case eCoordSystemConversionMode.AsRotation:
                //        break;
                //    case eCoordSystemConversionMode.AsVelocity:
                //        break;
                //    case eCoordSystemConversionMode.AsScale:
                //        break;
                //}
                break;

            case AmpsHelpers.eCoordSystems.Emitter:
                switch (coordSystemConversionMode)
                {
                case eCoordSystemConversionMode.AsPosition:
                    returnValue += ownerBlueprint.ownerEmitter.transform.position;
                    returnValue  = AmpsHelpers.RotateAroundPoint(returnValue, ownerBlueprint.ownerEmitter.emitterPosition, ownerBlueprint.ownerEmitter.transform.rotation);
                    break;

                case eCoordSystemConversionMode.AsRotation:
                    returnValue += ownerBlueprint.ownerEmitter.transform.rotation.eulerAngles;
                    break;

                case eCoordSystemConversionMode.AsVelocity:
                    returnValue = ownerBlueprint.ownerEmitter.emitterMatrixPositionZero.MultiplyPoint3x4(returnValue);
                    break;

                case eCoordSystemConversionMode.AsScale:
                    returnValue.x *= ownerBlueprint.ownerEmitter.transform.lossyScale.x;
                    returnValue.y *= ownerBlueprint.ownerEmitter.transform.lossyScale.y;
                    returnValue.z *= ownerBlueprint.ownerEmitter.transform.lossyScale.z;
                    break;
                }
                break;
            }

            return(AmpsHelpers.ConvertVector3Vector4(returnValue, 0));
        }
Beispiel #2
0
        public int randomSeed            = 0;                           // A curve specific random seed;

        // GET CURVE INPUT //
        //
        public float GetCurveInput(AmpsBlueprint ownerBlueprint, int particleIndex)
        {
            float   returnValue = 0;
            Vector3 v3          = Vector3.zero;
            Vector4 v4          = Vector4.zero;

            //Matrix4x4 toMatrix = AmpsHelpers.identityMatrix; // Slightly faster than Matrix4x4.identity;

            v4 = AmpsHelpers.GetSystemProperty(ownerBlueprint, particleIndex, curveInput);

            // The only need to work with conversion to emitter space as everything
            // is world relative by default.
            if (curveInputCoordSystem == AmpsHelpers.eCoordSystems.Emitter)
            {
                //toMatrix = ownerBlueprint.ownerEmitter.emitterMatrixFull;
                if (AmpsHelpers.isPositionInput(curveInput))
                {
                    v3  = AmpsHelpers.ConvertVector4Vector3(v4);
                    v3 -= ownerBlueprint.ownerEmitter.transform.position;
                    v3  = AmpsHelpers.RotateAroundPoint(v3, ownerBlueprint.ownerEmitter.emitterPosition, ownerBlueprint.ownerEmitter.transform.rotation);
                    v4  = AmpsHelpers.ConvertVector3Vector4(v3, 0);
                }
                else if (AmpsHelpers.isRotationInput(curveInput))
                {
                    v3  = AmpsHelpers.ConvertVector4Vector3(v4);
                    v3 += ownerBlueprint.ownerEmitter.transform.rotation.eulerAngles;
                    v4  = AmpsHelpers.ConvertVector3Vector4(v3, 0);
                }
                else if (AmpsHelpers.isVelocityInput(curveInput))
                {
                    v3  = AmpsHelpers.ConvertVector4Vector3(v4);
                    v3 += ownerBlueprint.ownerEmitter.emitterMatrixPositionZero.MultiplyPoint3x4(v3);
                    v4  = AmpsHelpers.ConvertVector3Vector4(v3, 0);
                }
                else if (AmpsHelpers.isScaleInput(curveInput))
                {
                    v3    = AmpsHelpers.ConvertVector4Vector3(v4);
                    v3.x *= ownerBlueprint.ownerEmitter.transform.lossyScale.x;
                    v3.y *= ownerBlueprint.ownerEmitter.transform.lossyScale.y;
                    v3.z *= ownerBlueprint.ownerEmitter.transform.lossyScale.z;
                    v4    = AmpsHelpers.ConvertVector3Vector4(v3, 0);
                }
            }

            if (AmpsHelpers.isFloatInput(curveInput))
            {
                returnValue = v4.x;
            }
            else
            {
                //if (AmpsHelpers.isPositionInput(curveInput)) v = toMatrix.MultiplyPoint3x4(v);
                //else v = toMatrix.MultiplyVector(v);

                switch (curveInputVectorComponent)
                {
                case AmpsHelpers.eVectorComponents.X:
                    returnValue = v4.x;
                    break;

                case AmpsHelpers.eVectorComponents.Y:
                    returnValue = v4.y;
                    break;

                case AmpsHelpers.eVectorComponents.Z:
                    returnValue = v4.z;
                    break;

                case AmpsHelpers.eVectorComponents.W:
                    returnValue = v4.w;
                    break;

                case AmpsHelpers.eVectorComponents.Mag:
                    returnValue = new Vector3(v4.x, v4.y, v4.z).magnitude;
                    break;
                }
                // TODO: Handle Color.
            }

            // Normalize input.
            float finalInputRangeMin = inputRangeMin;
            float finalInputRangeMax = inputRangeMax;

            if (isInputRangeRandom)
            {
                System.Random theRandom = new System.Random(ownerBlueprint.ownerEmitter.randomSeed + randomSeed + ownerBlueprint.ownerEmitter.particleIds[particleIndex]);
                finalInputRangeMin = Mathf.Lerp(inputRangeMin, inputRangeRandomMin, (float)theRandom.NextDouble());
                finalInputRangeMax = Mathf.Lerp(inputRangeMax, inputRangeRandomMax, (float)theRandom.NextDouble());
            }

            if (finalInputRangeMax - finalInputRangeMin == 0)
            {
                returnValue = 0;
            }
            else
            {
                returnValue = (returnValue - finalInputRangeMin) / (finalInputRangeMax - finalInputRangeMin);
            }

            return(returnValue);
        }
        // MANAGE SAMPLING //
        //
        // Does the actual sampling.
        override public void ManageSampling(int particleIndex)
        {
            Vector3 v = Vector3.zero;
            bool    shouldIndeedSample = (particleIndex < 0 && ShouldSample()) || (particleIndex >= 0 && ShouldSample(particleIndex));

            // Leave if it's not time yet to sample.
            if (shouldIndeedSample == false)
            {
                return;
            }

            // INVALID SAMPLE: Referenced GameObject is not available.
            if (theMesh == null)
            {
                isValidSample[particleIndex] = false;
                return;
            }

            // TODO: Regenerate element index if order is random or sequential.

            Vector3[]     triangleVertexPositions = new Vector3[3];
            Vector3[]     triangleVertexNormals   = new Vector3[3];
            Vector3[]     triangleVertexTangents  = new Vector3[3];
            eMeshElements selectedMeshElement     = (eMeshElements)sampledMeshElement.GetValue();

            switch (selectedMeshElement)
            {
            case eMeshElements.VertexPosition:
                v = theMesh.vertices[elementIndices[particleIndex]];
                break;

            case eMeshElements.VertexNormal:
                if (theMesh.normals.Length > 0)
                {
                    v = theMesh.normals[elementIndices[particleIndex]];
                }
                else
                {
                    v = Vector3.zero;
                }
                break;

            case eMeshElements.TrianglePosition:
                triangleVertexPositions[0] = theMesh.vertices[theMesh.triangles[elementIndices[particleIndex] * 3]];
                triangleVertexPositions[1] = theMesh.vertices[theMesh.triangles[elementIndices[particleIndex] * 3] + 1];
                triangleVertexPositions[2] = theMesh.vertices[theMesh.triangles[elementIndices[particleIndex] * 3] + 2];
                v = (triangleVertexPositions[0] + triangleVertexPositions[1] + triangleVertexPositions[2]) / 3;
                break;

            case eMeshElements.TriangleNormal:
                triangleVertexNormals[0] = theMesh.normals[theMesh.triangles[elementIndices[particleIndex] * 3]];
                triangleVertexNormals[1] = theMesh.normals[theMesh.triangles[elementIndices[particleIndex] * 3] + 1];
                triangleVertexNormals[2] = theMesh.normals[theMesh.triangles[elementIndices[particleIndex] * 3] + 2];
                v = (triangleVertexNormals[0] + triangleVertexNormals[1] + triangleVertexNormals[2]) / 3;
                break;

            case eMeshElements.TriangleRotation:
                triangleVertexNormals[0] = theMesh.normals[theMesh.triangles[elementIndices[particleIndex] * 3]];
                triangleVertexNormals[1] = theMesh.normals[theMesh.triangles[elementIndices[particleIndex] * 3] + 1];
                triangleVertexNormals[2] = theMesh.normals[theMesh.triangles[elementIndices[particleIndex] * 3] + 2];
                Vector3 triangleNormal = (triangleVertexNormals[0] + triangleVertexNormals[1] + triangleVertexNormals[2]) / 3;
                triangleNormal           *= -1;
                triangleVertexTangents[0] = theMesh.tangents[theMesh.triangles[elementIndices[particleIndex] * 3]];
                triangleVertexTangents[1] = theMesh.tangents[theMesh.triangles[elementIndices[particleIndex] * 3] + 1];
                triangleVertexTangents[2] = theMesh.tangents[theMesh.triangles[elementIndices[particleIndex] * 3] + 2];
                Vector3 triangleTangent = (triangleVertexTangents[0] + triangleVertexTangents[1] + triangleVertexTangents[2]) / 3;

                Quaternion theQuaternion = Quaternion.LookRotation(triangleNormal, triangleTangent);
                v = theQuaternion.eulerAngles;
                break;
            }

            // Optional conversion to emitter space.
            if (isEmitterRelative.GetValue())
            {
                if (selectedMeshElement == eMeshElements.TrianglePosition ||
                    selectedMeshElement == eMeshElements.VertexPosition)
                {
                    v += ownerBlueprint.ownerEmitter.transform.position;
                    v  = AmpsHelpers.RotateAroundPoint(v, ownerBlueprint.ownerEmitter.emitterPosition, ownerBlueprint.ownerEmitter.transform.rotation);
                }
                else if (selectedMeshElement == eMeshElements.TriangleRotation)
                {
                    v += ownerBlueprint.ownerEmitter.transform.rotation.eulerAngles;
                }
            }


            vectors[particleIndex] = new Vector4(v.x, v.y, v.z, 0);

            isValidSample[particleIndex] = true;
        }
        // EVALUATE //
        //
        override public void Evaluate()
        {
            Vector4    customVector;
            Vector3    position;
            Vector3    pivotOffset;
            Vector3    rotation;
            Quaternion quaternionRotation;
            Vector3    scale;
            Color      color;
            bool       shouldRecreateMesh;
            bool       shouldBeDoubleSided;
            Quaternion camFacingRotation;
            int        vertexIndexSteps;
            int        triangleIndexSteps;

            if (ownerBlueprint == null ||
                ownerBlueprint.ownerEmitter == null ||
                ownerBlueprint.ownerEmitter.particleMarkers == null)
            {
                return;                                                                                 // To fix null ref log spam after script rebuild.
            }
            if (finalMesh == null)
            {
                SoftReset();
            }

            ownerBlueprint.ownerEmitter.activeParticleIndices = ownerBlueprint.ownerEmitter.particleMarkers.GetActiveIndices();
            particleCount       = ownerBlueprint.ownerEmitter.activeParticleIndices.Length;
            shouldRecreateMesh  = (particleCount != lastParticleCount);
            shouldBeDoubleSided = isDoubleSided.GetValue();
            if (shouldBeDoubleSided)
            {
                vertexIndexSteps   = 8;
                triangleIndexSteps = 12;
            }
            else
            {
                vertexIndexSteps   = 4;
                triangleIndexSteps = 6;
            }
            // TODO: Investigate why this line fails at runtime around 2 * 4:
            //shouldRecreateMesh = triangles.Length != particleCount * vertexIndexSteps;

            if (shouldRecreateMesh)
            {
                finalMesh.Clear();

                vertices  = new Vector3[particleCount * vertexIndexSteps];
                normals   = new Vector3[particleCount * vertexIndexSteps];
                tangents  = new Vector4[particleCount * vertexIndexSteps];
                uvs       = new Vector2[particleCount * vertexIndexSteps];
                uv2s      = new Vector2[particleCount * vertexIndexSteps];
                colors    = new Color[particleCount * vertexIndexSteps];
                triangles = new int[particleCount * triangleIndexSteps];

                lastParticleCount = particleCount;
            }

            int particleIndex;

            for (int i = 0; i < ownerBlueprint.ownerEmitter.activeParticleIndices.Length; i++)
            {
                particleIndex = ownerBlueprint.ownerEmitter.activeParticleIndices[i];
                customVector  = ownerBlueprint.customVectorStack.values[particleIndex];

                position  = AmpsHelpers.ConvertVector4Vector3(ownerBlueprint.ownerEmitter.blueprint.positionStack.values[particleIndex]); // Raw position stack result.
                position  = AmpsHelpers.RotateAroundPoint(position, ownerBlueprint.ownerEmitter.transform.position, Quaternion.Inverse(ownerBlueprint.ownerEmitter.transform.rotation));
                position -= ownerBlueprint.ownerEmitter.emitterPosition;                                                                  // Compensating for emitter position, turning position world relative.

                pivotOffset = AmpsHelpers.ConvertVector4Vector3(ownerBlueprint.pivotOffsetStack.values[particleIndex]);

                rotation           = AmpsHelpers.ConvertVector4Vector3(ownerBlueprint.rotationStack.values[particleIndex]);       // Raw particle stack result.
                quaternionRotation = Quaternion.Inverse(ownerBlueprint.ownerEmitter.transform.rotation) * Quaternion.Euler(rotation);

                scale = AmpsHelpers.ConvertVector4Vector3(ownerBlueprint.scaleStack.values[particleIndex]);

                color = ownerBlueprint.colorStack.values[particleIndex];

                // Setting up the quad, particle is at the center.
                vertices[i * vertexIndexSteps]     = (position + pivotOffset) + (new Vector3(scale.x, scale.y, 0) / 2);
                vertices[i * vertexIndexSteps + 1] = (position + pivotOffset) + (new Vector3(-scale.x, scale.y, 0) / 2);
                vertices[i * vertexIndexSteps + 2] = (position + pivotOffset) + (new Vector3(-scale.x, -scale.y, 0) / 2);
                vertices[i * vertexIndexSteps + 3] = (position + pivotOffset) + (new Vector3(scale.x, -scale.y, 0) / 2);

                switch (orientation.GetValue())
                {
                case (int)eOrientation.Rotated:
                    // Rotating the (pivot offset) vertices around particle position.
                    vertices[i * vertexIndexSteps]     = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps], position, quaternionRotation);
                    vertices[i * vertexIndexSteps + 1] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 1], position, quaternionRotation);
                    vertices[i * vertexIndexSteps + 2] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 2], position, quaternionRotation);
                    vertices[i * vertexIndexSteps + 3] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 3], position, quaternionRotation);
                    break;

                case (int)eOrientation.CameraFacing:
                    camFacingRotation = Quaternion.LookRotation(ownerBlueprint.ownerEmitter.currentCameraTransform.forward, ownerBlueprint.ownerEmitter.currentCameraTransform.up);
                    camFacingRotation = Quaternion.Inverse(ownerBlueprint.ownerEmitter.transform.rotation) * camFacingRotation;
                    // TODO: Research if camera-particle position diff vector is better.

                    // Rotating all vertices around the center so the quad faces the camera.
                    vertices[i * vertexIndexSteps]     = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 1] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 1], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 2] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 2], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 3] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 3], position, camFacingRotation);
                    break;

                case (int)eOrientation.RotatedCameraFacing:
                    camFacingRotation = Quaternion.LookRotation(ownerBlueprint.ownerEmitter.currentCameraTransform.forward, ownerBlueprint.ownerEmitter.currentCameraTransform.up);
                    camFacingRotation = Quaternion.Inverse(ownerBlueprint.ownerEmitter.transform.rotation) * camFacingRotation;
                    camFacingRotation = camFacingRotation * Quaternion.Euler(rotation);

                    // Rotating all vertices around the center so the quad faces the camera.
                    vertices[i * vertexIndexSteps]     = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 1] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 1], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 2] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 2], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 3] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 3], position, camFacingRotation);
                    break;

                case (int)eOrientation.VectorAlignedCameraFacing:
                    //Vector3 upVector = Vector3.Normalize(alignmentVector.GetValue());
                    //Vector3 forwardVector = ownerBlueprint.ownerEmitter.currentCameraTransform.forward;
                    ////ownerBlueprint.ownerEmitter.currentCameraTransform.up;
                    //camFacingRotation = Quaternion.LookRotation(forwardVector, upVector);
                    ////camFacingRotation = Quaternion.Inverse(ownerBlueprint.ownerEmitter.transform.rotation) * camFacingRotation;

                    camFacingRotation = Quaternion.LookRotation(ownerBlueprint.ownerEmitter.currentCameraTransform.forward, Vector3.up);
                    Vector3 eulerRotation = camFacingRotation.eulerAngles;
                    eulerRotation.x   = 0;                              // Discard rotation around x.
                    eulerRotation.z   = 0;                              // Discard rotation around z.
                    camFacingRotation = Quaternion.Euler(eulerRotation);

                    // Rotating all vertices around the center so the quad faces the camera.
                    vertices[i * vertexIndexSteps]     = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 1] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 1], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 2] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 2], position, camFacingRotation);
                    vertices[i * vertexIndexSteps + 3] = AmpsHelpers.RotateAroundPoint(vertices[i * vertexIndexSteps + 3], position, camFacingRotation);
                    break;
                }

                if (shouldBeDoubleSided)
                {
                    vertices[i * vertexIndexSteps + 4] = vertices[i * vertexIndexSteps];
                    vertices[i * vertexIndexSteps + 5] = vertices[i * vertexIndexSteps + 1];
                    vertices[i * vertexIndexSteps + 6] = vertices[i * vertexIndexSteps + 2];
                    vertices[i * vertexIndexSteps + 7] = vertices[i * vertexIndexSteps + 3];
                }

                colors[i * vertexIndexSteps]     = color;
                colors[i * vertexIndexSteps + 1] = color;
                colors[i * vertexIndexSteps + 2] = color;
                colors[i * vertexIndexSteps + 3] = color;
                if (shouldBeDoubleSided)
                {
                    if ((eDoubleSidedColorMode)doubleSidedColorMode.GetValue() == eDoubleSidedColorMode.ColorStack)
                    {
                        colors[i * vertexIndexSteps + 4] = colors[i * vertexIndexSteps];
                        colors[i * vertexIndexSteps + 5] = colors[i * vertexIndexSteps + 1];
                        colors[i * vertexIndexSteps + 6] = colors[i * vertexIndexSteps + 2];
                        colors[i * vertexIndexSteps + 7] = colors[i * vertexIndexSteps + 3];
                    }
                    else
                    {
                        colors[i * vertexIndexSteps + 4] = customVector;
                        colors[i * vertexIndexSteps + 5] = customVector;
                        colors[i * vertexIndexSteps + 6] = customVector;
                        colors[i * vertexIndexSteps + 7] = customVector;
                    }
                }

                switch (normalMode.GetValue())
                {
                case (int)eNormalMode.Skip:
                    break;

                case (int)eNormalMode.Flat:
                    Vector3 calculatedNormal = Vector3.Cross((vertices[i * vertexIndexSteps + 2] - vertices[i * vertexIndexSteps]), (vertices[i * vertexIndexSteps + 1] - vertices[i * vertexIndexSteps]));
                    calculatedNormal = Vector3.Normalize(calculatedNormal);
                    normals[i * vertexIndexSteps]     = calculatedNormal;
                    normals[i * vertexIndexSteps + 1] = calculatedNormal;
                    normals[i * vertexIndexSteps + 2] = calculatedNormal;
                    normals[i * vertexIndexSteps + 3] = calculatedNormal;
                    break;
                }
                if (shouldBeDoubleSided)
                {
                    normals[i * vertexIndexSteps + 4] = normals[i * vertexIndexSteps] * -1;
                    normals[i * vertexIndexSteps + 5] = normals[i * vertexIndexSteps + 1] * -1;
                    normals[i * vertexIndexSteps + 6] = normals[i * vertexIndexSteps + 2] * -1;
                    normals[i * vertexIndexSteps + 7] = normals[i * vertexIndexSteps + 3] * -1;
                }

                switch (uv2Mode.GetValue())
                {
                case (int)eUV2Mode.Skip:
                    break;

                case (int)eUV2Mode.CustomVectorXY:
                    uv2s[i * vertexIndexSteps]     = new Vector2(customVector.x, customVector.y);
                    uv2s[i * vertexIndexSteps + 1] = new Vector2(customVector.x, customVector.y);
                    uv2s[i * vertexIndexSteps + 2] = new Vector2(customVector.x, customVector.y);
                    uv2s[i * vertexIndexSteps + 3] = new Vector2(customVector.x, customVector.y);
                    break;

                case (int)eUV2Mode.CustomVectorXYZW:                            // BUG: Doesn't really work...
                    Vector2 calculatedUv2 = Vector2.zero;
                    calculatedUv2.x                = AmpsHelpers.PackVector2(new Vector2(customVector.x, customVector.y));
                    calculatedUv2.y                = AmpsHelpers.PackVector2(new Vector2(customVector.z, customVector.w));
                    uv2s[i * vertexIndexSteps]     = calculatedUv2;
                    uv2s[i * vertexIndexSteps + 1] = calculatedUv2;
                    uv2s[i * vertexIndexSteps + 2] = calculatedUv2;
                    uv2s[i * vertexIndexSteps + 3] = calculatedUv2;
                    break;
                }
                if (shouldBeDoubleSided)
                {
                    uv2s[i * vertexIndexSteps + 4] = uv2s[i * vertexIndexSteps];
                    uv2s[i * vertexIndexSteps + 5] = uv2s[i * vertexIndexSteps + 1];
                    uv2s[i * vertexIndexSteps + 6] = uv2s[i * vertexIndexSteps + 2];
                    uv2s[i * vertexIndexSteps + 7] = uv2s[i * vertexIndexSteps + 3];
                }

                switch (tangentMode.GetValue())
                {
                case (int)eTangentMode.Skip:
                    break;

                case (int)eTangentMode.Generate:
                    //Vector3 rawTangent = Vector3.Normalize(vertices[i * 4 + 3] - vertices[i * 4 + 1]);
                    Vector3 rawTangent        = Vector3.Normalize(vertices[i * 4 + 0] - vertices[i * 4 + 1]);
                    Vector4 calculatedTangent = new Vector4(rawTangent.x, rawTangent.y, rawTangent.z, -1);
                    tangents[i * vertexIndexSteps]     = calculatedTangent;
                    tangents[i * vertexIndexSteps + 1] = calculatedTangent;
                    tangents[i * vertexIndexSteps + 2] = calculatedTangent;
                    tangents[i * vertexIndexSteps + 3] = calculatedTangent;
                    break;

                case (int)eTangentMode.CustomVectorXYZW:
                    tangents[i * vertexIndexSteps]     = customVector;
                    tangents[i * vertexIndexSteps + 1] = customVector;
                    tangents[i * vertexIndexSteps + 2] = customVector;
                    tangents[i * vertexIndexSteps + 3] = customVector;
                    break;
                }
                if (shouldBeDoubleSided)
                {
                    // TODO: Should it be multiplied by -1?
                    tangents[i * vertexIndexSteps + 4] = tangents[i * vertexIndexSteps];
                    tangents[i * vertexIndexSteps + 5] = tangents[i * vertexIndexSteps + 1];
                    tangents[i * vertexIndexSteps + 6] = tangents[i * vertexIndexSteps + 2];
                    tangents[i * vertexIndexSteps + 7] = tangents[i * vertexIndexSteps + 3];
                }


                if (shouldRecreateMesh)                 // Things go in here which don't change unless particle count changes.
                {
                    uvs[i * vertexIndexSteps]     = new Vector2(1, 1);
                    uvs[i * vertexIndexSteps + 1] = new Vector2(0, 1);
                    uvs[i * vertexIndexSteps + 2] = new Vector2(0, 0);
                    uvs[i * vertexIndexSteps + 3] = new Vector2(1, 0);
                    if (shouldBeDoubleSided)
                    {
                        uvs[i * vertexIndexSteps + 4] = new Vector2(1, 1);
                        uvs[i * vertexIndexSteps + 5] = new Vector2(0, 1);
                        uvs[i * vertexIndexSteps + 6] = new Vector2(0, 0);
                        uvs[i * vertexIndexSteps + 7] = new Vector2(1, 0);
                    }

                    //First triangle.
                    triangles[i * triangleIndexSteps]     = i * vertexIndexSteps;
                    triangles[i * triangleIndexSteps + 1] = i * vertexIndexSteps + 2;
                    triangles[i * triangleIndexSteps + 2] = i * vertexIndexSteps + 1;
                    //Second triangle.
                    triangles[i * triangleIndexSteps + 3] = i * vertexIndexSteps + 2;
                    triangles[i * triangleIndexSteps + 4] = i * vertexIndexSteps;
                    triangles[i * triangleIndexSteps + 5] = i * vertexIndexSteps + 3;

                    if (shouldBeDoubleSided)
                    {
                        //Third triangle.
                        triangles[i * triangleIndexSteps + 6] = i * vertexIndexSteps + 5;
                        triangles[i * triangleIndexSteps + 7] = i * vertexIndexSteps + 6;
                        triangles[i * triangleIndexSteps + 8] = i * vertexIndexSteps + 4;
                        //Forth triangle.
                        triangles[i * triangleIndexSteps + 9]  = i * vertexIndexSteps + 7;
                        triangles[i * triangleIndexSteps + 10] = i * vertexIndexSteps + 4;
                        triangles[i * triangleIndexSteps + 11] = i * vertexIndexSteps + 6;
                    }
                }
            }

            // TODO: Bubble sort with limited run time.
            finalMesh.vertices  = vertices;
            finalMesh.uv        = uvs;
            finalMesh.uv2       = uv2s;
            finalMesh.normals   = normals;
            finalMesh.tangents  = tangents;
            finalMesh.colors    = colors;
            finalMesh.triangles = triangles;
        }
        // EVALUATE //
        //
        override public void Evaluate()
        {
            bool       shouldRecreateMesh;
            Vector3    position;
            Vector3    pivotOffset;
            Vector3    rotation;
            Quaternion quaternionRotation;
            Vector3    scale;
            Color      color;

            System.Random theRandom = new System.Random();

            InitializeNewParticles();

            if (ownerBlueprint.ownerEmitter.particleMarkers == null)
            {
                return;                                                                                 // To fix null ref log spam after script rebuild.
            }
            ownerBlueprint.ownerEmitter.activeParticleIndices = ownerBlueprint.ownerEmitter.particleMarkers.GetActiveIndices();
            particleCount      = ownerBlueprint.ownerEmitter.activeParticleIndices.Length;
            shouldRecreateMesh = particleCount != lastParticleCount;

            if (shouldRecreateMesh)
            {
                ownerBlueprint.ownerEmitter.emitterMesh.Clear();
                lastParticleCount = particleCount;
            }

            CombineInstance[] combine = new CombineInstance[particleCount];
            int particleIndex;

            for (int i = 0; i < ownerBlueprint.ownerEmitter.activeParticleIndices.Length; i++)
            {
                particleIndex = ownerBlueprint.ownerEmitter.activeParticleIndices[i];
                position      = AmpsHelpers.ConvertVector4Vector3(ownerBlueprint.ownerEmitter.blueprint.positionStack.values[particleIndex]); // Raw position stack result.
                position      = AmpsHelpers.RotateAroundPoint(position, ownerBlueprint.ownerEmitter.transform.position, Quaternion.Inverse(ownerBlueprint.ownerEmitter.transform.rotation));
                position     -= ownerBlueprint.ownerEmitter.emitterPosition;                                                                  // Compensating for emitter position, turning position world relative.

                pivotOffset = AmpsHelpers.ConvertVector4Vector3(ownerBlueprint.pivotOffsetStack.values[particleIndex]);

                rotation           = AmpsHelpers.ConvertVector4Vector3(ownerBlueprint.rotationStack.values[particleIndex]);       // Raw particle stack result.
                quaternionRotation = Quaternion.Inverse(ownerBlueprint.ownerEmitter.transform.rotation) * Quaternion.Euler(rotation);

                scale = AmpsHelpers.ConvertVector4Vector3(ownerBlueprint.scaleStack.values[particleIndex]);
                color = ownerBlueprint.colorStack.values[particleIndex];

                if (meshIndices[particleIndex] < 0)
                {
                    meshIndices[particleIndex] = theRandom.Next(0, inputMeshes.Length);
                }
                Mesh pickedMesh = inputMeshes[meshIndices[particleIndex]].GetValue();

                meshCache[particleIndex].Clear();
                combine[i].mesh = meshCache[particleIndex];
                if (pickedMesh != null)
                {
                    combine[i].mesh.vertices  = pickedMesh.vertices;
                    combine[i].mesh.normals   = pickedMesh.normals;
                    combine[i].mesh.uv        = pickedMesh.uv;
                    combine[i].mesh.triangles = pickedMesh.triangles;
                    combine[i].mesh.tangents  = pickedMesh.tangents;

                    Color[] vertexColors = new Color[pickedMesh.vertices.Length];
                    for (int j = 0; j < pickedMesh.vertices.Length; j++)
                    {
                        vertexColors[j] = color;
                    }
                    combine[i].mesh.colors = vertexColors;

                    combine[i].transform = Matrix4x4.TRS(position + pivotOffset, quaternionRotation, scale);
                }
                else
                {
                    combine[i].transform = new Matrix4x4();
                }

                if (shouldRecreateMesh)                 // Things go in here which don't change unless particle count changes.
                {
                }
            }

            finalMesh.CombineMeshes(combine, true, true);
        }