Example #1
0
        //
        void Spawn(float deltaTime, ref DirectedGraph <SplineGraphPayload, SplineGraphPayloadSerializable> splineGraph)
        {
            if (count >= requestedCount)
            {
                return;
            }

            for (; count < requestedCount; ++count)
            {
                float t          = UnityEngine.Random.value;
                Int16 edgeIndex  = (Int16)Mathf.FloorToInt((splineGraph.edgePoolChildren.data.Length - 1) * UnityEngine.Random.value + 0.5f);
                int   isComplete = 0;
                int   isReverse  = isTwoWayPathEnabled ? ((UnityEngine.Random.value >= 0.5f) ? 1 : 0) : 0;
                followStates[count] = new SplineMath.SplineGraphFollowState(t, edgeIndex, isComplete, isReverse);

                float velocityRandom = UnityEngine.Random.value;
                randoms[count]    = new Unity.Mathematics.Random((uint)count + 1);
                velocities[count] = Mathf.Lerp(velocityMin, velocityMax, velocityRandom);
                scales[count]     = Mathf.Lerp(1.0f, 1.0f, UnityEngine.Random.value);

                float leashPolarRadiusNormalized = math.lerp(leashNormalizedMin, leashNormalizedMax, math.pow(1.0f - velocityRandom, 2.0f));

                float leashPolarThetaMin = isTwoWayPathEnabled ? (-0.5f * math.PI) : 0.0f;
                float leashPolarThetaMax = isTwoWayPathEnabled ? (0.5f * math.PI) : math.PI * 2.0f;
                float leashPolarTheta    = math.lerp(leashPolarThetaMin, leashPolarThetaMax, UnityEngine.Random.value);

                float2 leashCartesianNormalized = new float2(
                    math.cos(leashPolarTheta),
                    math.sin(leashPolarTheta)
                    ) * leashPolarRadiusNormalized;

                leashes[count] = leashCartesianNormalized;

                // Seed position and rotation with their initial values, so that dampening does not lerp between the previous garbage position of a newly spawned vehicle.
                // If this probes to make Spawn() significantly more expensive, we can store a flag that says whether or not we should apply dampening instead.
                {
                    SplineMath.Spline spline = (isReverse == 0)
                        ? splineGraph.payload.edgeParentToChildSplines.data[edgeIndex]
                        : splineGraph.payload.edgeChildToParentSplines.data[edgeIndex];

                    Int16 vertexIndexChild  = splineGraph.edgePoolChildren.data[edgeIndex].vertexIndex;
                    Int16 vertexIndexParent = splineGraph.edgePoolParents.data[edgeIndex].vertexIndex;
                    if (isReverse == 1)
                    {
                        Int16 vertexIndexTemp = vertexIndexChild;
                        vertexIndexChild  = vertexIndexParent;
                        vertexIndexParent = vertexIndexTemp;
                    }

                    quaternion rotationParent = splineGraph.payload.rotations.data[vertexIndexParent];
                    quaternion rotationChild  = splineGraph.payload.rotations.data[vertexIndexChild];

                    float3 positionOnSpline = SplineMath.EvaluatePositionFromT(spline, t);
                    positions[count] = positionOnSpline;
                    rotations[count] = SplineMath.EvaluateRotationWithRollFromT(spline, rotationParent, rotationChild, t);
                }

                followPool.EnableInstanceNext();
            }
        }
        // TODO: Debug only:
        private void OnSceneGUI()
        {
            // If we are currently tumbling the camera, do not attempt to do anything else.
            if (Event.current.alt)
            {
                return;
            }

            serializedObject.Update();

            var sgm = target as SplineGraphManager;

            sgm.Verify();

            if (!sgm.isEditingEnabled)
            {
                return;
            }

            // return; // TODO: Remove?

            if (Tools.current == Tool.Move)
            {
                sgm.debugPosition = Handles.PositionHandle(sgm.debugPosition, quaternion.identity);

                for (int i = 0, iCount = sgm.debugFollowStates.Count; i < iCount; ++i)
                {
                    SplineMath.SplineGraphFollowState state = sgm.debugFollowStates[i];

                    int               isReverse = state.DecodeIsReverse();
                    Int16             edgeIndex = state.DecodeEdgeIndex();
                    float             t         = state.t;
                    SplineMath.Spline spline    = (state.DecodeIsReverse() == 0)
                        ? sgm.splineGraph.payload.edgeParentToChildSplines.data[edgeIndex]
                        : sgm.splineGraph.payload.edgeChildToParentSplines.data[edgeIndex];

                    float3     position = SplineMath.EvaluatePositionFromT(spline, t);
                    quaternion rotation = SplineMath.EvaluateRotationFromT(spline, t);

                    Handles.PositionHandle(position, rotation);
                }
            }

            if (sgm.isAutoUpdateEnabled)
            {
                sgm.BuildGraphFromInstances();
            }
        }
        void UpdateMeshFromSplineGraph()
        {
            Mesh mesh = meshFilter.sharedMesh;

            if (mesh == null)
            {
                mesh = new Mesh();
            }

            if (radialEdgeCount <= 0)
            {
                // TODO: Cleanup case when radialEdgeCount == 0 due to user still editing into inspector field.
                // This should be handled with a delayed int field.
                return;
            }

            var splineGraph = (splineGraphComponent != null)
                ? splineGraphComponent.GetSplineGraph()
                : splineGraphManager.GetSplineGraph();

            // First, go through and compute the number of vertex ring subdivisions required to represent the spline graph based on curvature.
            Int16 vertexIsValidCount = splineGraph.ComputeVertexIsValidCount();
            Int16 edgeIsValidCount   = splineGraph.ComputeEdgeIsValidCount();

            int[] edgeSubdivisionIndex      = new int[splineGraph.edgePoolChildren.count];
            int[] edgeSubdivisionCount      = new int[splineGraph.edgePoolChildren.count];
            int   meshSubdivisionCountTotal = 0;
            int   meshRingCountTotal        = 0;


            for (Int16 edgeIndex = 0; edgeIndex < splineGraph.edgePoolChildren.count; ++edgeIndex)
            {
                DirectedEdge edge = splineGraph.edgePoolChildren.data[edgeIndex];
                if (edge.IsValid() == 0)
                {
                    continue;
                }

                float splineLength     = splineGraph.payload.edgeLengths.data[edgeIndex];
                int   subdivisionCount = math.max(1, (int)math.floor(subdivisionsPerMeter * splineLength + 0.5f));
                int   ringCount        = subdivisionCount + 1;

                edgeSubdivisionIndex[edgeIndex] = meshSubdivisionCountTotal;
                edgeSubdivisionCount[edgeIndex] = subdivisionCount;
                meshSubdivisionCountTotal      += subdivisionCount;
                meshRingCountTotal += ringCount;
            }

            // TODO: Calculate counts.
            int meshVertexCount = meshRingCountTotal * radialEdgeCount;

            Vector3[] vertices  = new Vector3[meshVertexCount];
            Vector2[] uvs       = new Vector2[meshVertexCount];
            Vector3[] normals   = new Vector3[meshVertexCount];
            Color[]   colors    = new Color[meshVertexCount];
            int[]     triangles = new int[meshVertexCount * 6];

            int meshVertexIndex   = 0;
            int meshTriangleIndex = 0;

            for (Int16 edgeIndex = 0; edgeIndex < splineGraph.edgePoolChildren.count; ++edgeIndex)
            {
                DirectedEdge edge = splineGraph.edgePoolChildren.data[edgeIndex];
                if (edge.IsValid() == 0)
                {
                    continue;
                }

                Int16 vertexIndexChild  = splineGraph.edgePoolChildren.data[edgeIndex].vertexIndex;
                Int16 vertexIndexParent = splineGraph.edgePoolParents.data[edgeIndex].vertexIndex;

                SplineMath.Spline spline       = splineGraph.payload.edgeParentToChildSplines.data[edgeIndex];
                float             splineLength = splineGraph.payload.edgeLengths.data[edgeIndex];

                quaternion rotationParent = splineGraph.payload.rotations.data[vertexIndexParent];
                quaternion rotationChild  = splineGraph.payload.rotations.data[vertexIndexChild];

                SplineMath.Spline splineLeash = splineGraph.payload.edgeParentToChildSplinesLeashes.data[edgeIndex];

                // Find neighboring edge (if it exists) for use in CSG style operation against current one to handle intersections in branching region.
                DirectedVertex vertexParent = splineGraph.vertices.data[vertexIndexParent];
                Debug.Assert(vertexParent.IsValid() == 1);

                DirectedVertex vertexChild = splineGraph.vertices.data[vertexIndexChild];
                Debug.Assert(vertexChild.IsValid() == 1);

                int edgeSubdivisionCurrentCount = edgeSubdivisionCount[edgeIndex];
                for (int s = 0; s <= edgeSubdivisionCurrentCount; ++s)
                {
                    float t = (float)s / (float)edgeSubdivisionCurrentCount;
                    // Debug.Log("s = " + s + ", sCount = " + edgeSubdivisionCurrentCount + ", t = " + t);

                    float vNormalized = t;
                    if (s > 0)
                    {
                        // Compute the relative distance we have traveled between our current edge ring and previous.
                        SplineMath.ComputeSplitAtT(out SplineMath.Spline s00, out SplineMath.Spline s01, spline, t);

                        vNormalized = SplineMath.ComputeLengthEstimate(s00, 1e-5f) / splineLength;
                    }

                    float3     positionOnSpline = SplineMath.EvaluatePositionFromT(spline, t);
                    quaternion rotationOnSpline = SplineMath.EvaluateRotationWithRollFromT(spline, rotationParent, rotationChild, t);
                    // float2 leashMaxOS = math.lerp(leashParent, leashChild, t);
                    float2 leashMaxOS = SplineMath.EvaluatePositionFromT(splineLeash, t).xy;

                    // Generate radial ring of triangles
                    if (s < edgeSubdivisionCurrentCount)
                    {
                        for (int v = 0, vLen = radialEdgeCount; v < vLen; ++v)
                        {
                            triangles[meshTriangleIndex + v * 6 + 0] = meshVertexIndex + ((v + 0) % radialEdgeCount) + (0 * radialEdgeCount);
                            triangles[meshTriangleIndex + v * 6 + 1] = meshVertexIndex + ((v + 0) % radialEdgeCount) + (1 * radialEdgeCount);
                            triangles[meshTriangleIndex + v * 6 + 2] = meshVertexIndex + ((v + 1) % radialEdgeCount) + (1 * radialEdgeCount);

                            triangles[meshTriangleIndex + v * 6 + 3] = meshVertexIndex + ((v + 1) % radialEdgeCount) + (1 * radialEdgeCount);
                            triangles[meshTriangleIndex + v * 6 + 4] = meshVertexIndex + ((v + 1) % radialEdgeCount) + (0 * radialEdgeCount);
                            triangles[meshTriangleIndex + v * 6 + 5] = meshVertexIndex + ((v + 0) % radialEdgeCount) + (0 * radialEdgeCount);
                        }

                        meshTriangleIndex += radialEdgeCount * 6;
                    }

                    // Generate radial ring of vertices.
                    // bool ringIntersectsSibling = false;
                    for (int v = 0; v < radialEdgeCount; ++v)
                    {
                        float  thetaNormalized = (float)v / (float)radialEdgeCount;
                        float  theta           = thetaNormalized * 2.0f * math.PI;
                        float2 vertexOffsetOS  = new float2(
                            math.cos(theta),
                            math.sin(theta)
                            ) * leashMaxOS;// * radius;

                        float3 vertexOffsetWS   = math.mul(rotationOnSpline, new float3(vertexOffsetOS, 0.0f));
                        float3 vertexPositionWS = positionOnSpline + vertexOffsetWS;

                        float vertexIntersectionSignedDistanceMin = float.MaxValue;
                        {
                            for (Int16 edgeIndexSibling = vertexParent.childHead; edgeIndexSibling != -1; edgeIndexSibling = splineGraph.edgePoolChildren.data[edgeIndexSibling].next)
                            {
                                DirectedEdge edgeSibling = splineGraph.edgePoolChildren.data[edgeIndexSibling];
                                Debug.Assert(edgeSibling.IsValid() == 1);

                                if (edgeIndexSibling == edgeIndex)
                                {
                                    // Ignore ourselves.
                                    continue;
                                }

                                // Found our sibling edge. Only use the first one, as we only currently support CSG against a single branch.
                                Int16 siblingVertexIndexChild = edgeSibling.vertexIndex;
                                Debug.Assert(siblingVertexIndexChild != -1);

                                Int16 siblingVertexIndexParent = splineGraph.edgePoolParents.data[edgeIndexSibling].vertexIndex;
                                Debug.Assert(siblingVertexIndexParent != -1);

                                SplineMath.Spline splineSibling         = splineGraph.payload.edgeParentToChildSplines.data[edgeIndexSibling];
                                SplineMath.Spline splineLeashSibling    = splineGraph.payload.edgeParentToChildSplinesLeashes.data[edgeIndexSibling];
                                quaternion        siblingRotationParent = splineGraph.payload.rotations.data[siblingVertexIndexParent];
                                quaternion        siblingRotationChild  = splineGraph.payload.rotations.data[siblingVertexIndexChild];

                                SplineMath.FindTFromClosestPointOnSpline(out float siblingClosestT, out float siblingClosestDistance, vertexPositionWS, splineSibling);

                                float3     siblingPositionOnSpline = SplineMath.EvaluatePositionFromT(splineSibling, siblingClosestT);
                                quaternion siblingRotationOnSpline = SplineMath.EvaluateRotationWithRollFromT(splineSibling, siblingRotationParent, siblingRotationChild, siblingClosestT);
                                float2     siblingLeashMaxOS       = SplineMath.EvaluatePositionFromT(splineLeashSibling, siblingClosestT).xy;

                                float vertexIntersectionSignedDistance = math.length(math.mul(math.inverse(siblingRotationOnSpline), vertexPositionWS - siblingPositionOnSpline).xy / siblingLeashMaxOS) - 1.0f;
                                vertexIntersectionSignedDistanceMin = math.min(vertexIntersectionSignedDistanceMin, vertexIntersectionSignedDistance);
                            }

                            for (Int16 edgeIndexSibling = vertexChild.parentHead; edgeIndexSibling != -1; edgeIndexSibling = splineGraph.edgePoolParents.data[edgeIndexSibling].next)
                            {
                                DirectedEdge edgeSibling = splineGraph.edgePoolChildren.data[edgeIndexSibling];
                                Debug.Assert(edgeSibling.IsValid() == 1);

                                if (edgeIndexSibling == edgeIndex)
                                {
                                    // Ignore ourselves.
                                    continue;
                                }

                                // Found our sibling edge. Only use the first one, as we only currently support CSG against a single branch.
                                Int16 siblingVertexIndexParent = edgeSibling.vertexIndex;
                                Debug.Assert(siblingVertexIndexParent != -1);

                                Int16 siblingVertexIndexChild = splineGraph.edgePoolChildren.data[edgeIndexSibling].vertexIndex;
                                Debug.Assert(siblingVertexIndexChild != -1);

                                SplineMath.Spline splineSibling         = splineGraph.payload.edgeParentToChildSplines.data[edgeIndexSibling];
                                SplineMath.Spline splineLeashSibling    = splineGraph.payload.edgeParentToChildSplinesLeashes.data[edgeIndexSibling];
                                quaternion        siblingRotationParent = splineGraph.payload.rotations.data[siblingVertexIndexParent];
                                quaternion        siblingRotationChild  = splineGraph.payload.rotations.data[siblingVertexIndexChild];

                                SplineMath.FindTFromClosestPointOnSpline(out float siblingClosestT, out float siblingClosestDistance, vertexPositionWS, splineSibling);

                                float3     siblingPositionOnSpline = SplineMath.EvaluatePositionFromT(splineSibling, siblingClosestT);
                                quaternion siblingRotationOnSpline = SplineMath.EvaluateRotationWithRollFromT(splineSibling, siblingRotationParent, siblingRotationChild, siblingClosestT);
                                float2     siblingLeashMaxOS       = SplineMath.EvaluatePositionFromT(splineLeashSibling, siblingClosestT).xy;

                                float vertexIntersectionSignedDistance = math.length(math.mul(math.inverse(siblingRotationOnSpline), vertexPositionWS - siblingPositionOnSpline).xy / siblingLeashMaxOS) - 1.0f;
                                vertexIntersectionSignedDistanceMin = math.min(vertexIntersectionSignedDistanceMin, vertexIntersectionSignedDistance);
                            }
                        }

                        vertices[meshVertexIndex] = vertexPositionWS;
                        uvs[meshVertexIndex]      = new float2(
                            thetaNormalized,
                            vNormalized * splineLength * uvScale // TODO: Gotta figure out propogation of UVs.
                            );
                        normals[meshVertexIndex] = math.normalize(vertexOffsetWS);

                        float vertexIntersectionDistanceFade = math.smoothstep(vertexIntersectionSignedDistanceFadeMin, vertexIntersectionSignedDistanceFadeMax, vertexIntersectionSignedDistanceMin);
                        colors[meshVertexIndex] = new Color(vertexIntersectionDistanceFade, vertexIntersectionDistanceFade, vertexIntersectionDistanceFade, vertexIntersectionDistanceFade);

                        ++meshVertexIndex;
                    }
                }
            }

            // Trim arrays to final size.
            Vector3[] verticesTrimmed  = new Vector3[meshVertexIndex];
            Vector2[] uvsTrimmed       = new Vector2[meshVertexIndex];
            Vector3[] normalsTrimmed   = new Vector3[meshVertexIndex];
            Color[]   colorsTrimmed    = new Color[meshVertexIndex];
            int[]     trianglesTrimmed = new int[meshTriangleIndex];

            Array.Copy(vertices, verticesTrimmed, meshVertexIndex);
            Array.Copy(uvs, uvsTrimmed, meshVertexIndex);
            Array.Copy(normals, normalsTrimmed, meshVertexIndex);
            Array.Copy(colors, colorsTrimmed, meshVertexIndex);
            Array.Copy(triangles, trianglesTrimmed, meshTriangleIndex);

            // Finally assign back data (causes GC allocs).
            mesh.Clear();
            mesh.vertices         = verticesTrimmed;
            mesh.uv               = uvsTrimmed;
            mesh.normals          = normalsTrimmed;
            mesh.colors           = colorsTrimmed;
            mesh.triangles        = trianglesTrimmed;
            meshFilter.sharedMesh = mesh;

            MeshCollider meshCollider = meshFilter.gameObject.GetComponent <MeshCollider>();

            if (meshCollider != null)
            {
                meshCollider.sharedMesh = mesh;
            }
        }
Example #4
0
            public void Execute(int i)
            {
                float positionDelta = math.length(velocities[i]) * deltaTime;

                SplineMath.SplineGraphFollowState followState = followStates[i];
                Unity.Mathematics.Random          random      = randoms[i];

                SplineMath.AdvanceTFromDelta(
                    ref followState,
                    ref random,
                    positionDelta,
                    splineGraph.vertices.data,
                    splineGraph.vertices.count,
                    splineGraph.edgePoolChildren.data,
                    splineGraph.edgePoolParents.data,
                    splineGraph.payload.edgeParentToChildSplines.data,
                    splineGraph.payload.edgeChildToParentSplines.data
                    );

                followStates[i] = followState;
                randoms[i]      = random;

                Int16 edgeIndex = followState.DecodeEdgeIndex();

                SplineMath.Spline spline = (followState.DecodeIsReverse() == 0)
                    ? splineGraph.payload.edgeParentToChildSplines.data[edgeIndex]
                    : splineGraph.payload.edgeChildToParentSplines.data[edgeIndex];

                Int16 vertexIndexChild  = splineGraph.edgePoolChildren.data[edgeIndex].vertexIndex;
                Int16 vertexIndexParent = splineGraph.edgePoolParents.data[edgeIndex].vertexIndex;

                if (followState.DecodeIsReverse() == 1)
                {
                    Int16 vertexIndexTemp = vertexIndexChild;
                    vertexIndexChild  = vertexIndexParent;
                    vertexIndexParent = vertexIndexTemp;
                }

                quaternion rotationParent = splineGraph.payload.rotations.data[vertexIndexParent];
                quaternion rotationChild  = splineGraph.payload.rotations.data[vertexIndexChild];

                float3     positionPrevious = positions[i];
                quaternion rotationPrevious = rotations[i];

                float3 positionOnSpline = SplineMath.EvaluatePositionFromT(spline, followState.t);

                positions[i] = positionOnSpline;
                // rotations[i] = SplineMath.EvaluateRotationFromT(spline, followState.t);
                // rotations[i] = math.slerp(rotationParent, rotationChild, followState.t);
                rotations[i] = SplineMath.EvaluateRotationWithRollFromT(spline, rotationParent, rotationChild, followState.t);

                // For now, simply evaluate the current leash value by lerping between the parent and child leash values, rather than using spline interpolation.
                // This seems good enough for now (there is a bug in the spline interpolation code commented out below.)
                float2 leashParent = splineGraph.payload.leashes.data[vertexIndexParent];
                float2 leashChild  = splineGraph.payload.leashes.data[vertexIndexChild];
                float2 leashMaxOS  = math.lerp(leashParent, leashChild, followState.t);

                // SplineMath.Spline splineLeash = (followState.DecodeIsReverse() == 0)
                //     ? splineGraph.payload.edgeParentToChildSplinesLeashes.data[edgeIndex]
                //     : splineGraph.payload.edgeChildToParentSplinesLeashes.data[edgeIndex];
                // float2 leashMaxOS = SplineMath.EvaluatePositionFromT(splineLeash, followState.t).xy;

                float2 leashOS = leashMaxOS * leashes[i];
                float3 leashWS = math.mul(rotations[i], new float3(leashOS, 0.0f));

                positions[i] += leashWS;

                if (avoidanceSoftBodySphereRadius > 1e-5f)
                {
                    float3 avoidanceDirection = positions[i] - avoidanceSoftBodySphereOrigin;
                    float  avoidanceLength    = math.length(avoidanceDirection);
                    avoidanceDirection *= (avoidanceLength > 1e-5f) ? (1.0f / avoidanceLength) : 0.0f;
                    float avoidanceOffset = math.saturate((avoidanceLength / avoidanceSoftBodySphereRadius) * -0.5f + 1.0f) * avoidanceSoftBodySphereRadius;
                    if (avoidanceOffset > 0.0f)
                    {
                        float3 leashPlaneNormal = math.mul(rotations[i], new float3(0.0f, 0.0f, 1.0f));
                        float3 avoidanceDirectionLeashPlaneT = avoidanceDirection - leashPlaneNormal * math.dot(leashPlaneNormal, avoidanceDirection);
                        avoidanceDirectionLeashPlaneT = (math.lengthsq(avoidanceDirectionLeashPlaneT) > 1e-3f)
                            ? avoidanceDirectionLeashPlaneT
                            : leashWS;
                        avoidanceDirectionLeashPlaneT = (math.lengthsq(avoidanceDirectionLeashPlaneT) > 1e-3f)
                            ? avoidanceDirectionLeashPlaneT
                            : new float3(0.0f, 1.0f, 0.0f);
                        avoidanceDirectionLeashPlaneT = math.normalize(avoidanceDirectionLeashPlaneT);
                        positions[i] += avoidanceDirectionLeashPlaneT * avoidanceOffset;
                    }
                }

                positions[i] = math.lerp(positions[i], positionPrevious, dampeningPosition);
                rotations[i] = math.slerp(rotations[i], rotationPrevious, dampeningRotation);


                // {
                //     float3 acceleration = SplineMath.EvaluateAccelerationFromT(spline, followState.t);

                //     float3 directionRightWS = math.mul(rotations[i], new float3(1.0f, 0.0f, 0.0f));
                //     float accelerationRight = math.dot(directionRightWS, acceleration);

                //     float rollAngle = math.lerp(-0.25f * math.PI, 0.25f * math.PI, math.saturate((accelerationRight * rollFromAccelerationScale) * -0.5f + 0.5f));
                //     rotations[i] = math.mul(rotations[i], quaternion.AxisAngle(new float3(0.0f, 0.0f, 1.0f), rollAngle));
                // }
            }