Beispiel #1
0
            private void InteractionTest(int A, int B, ref BurstSimplex simplexShape)
            {
                // skip the pair if their bounds don't intersect:
                if (!simplexBounds[A].IntersectsAabb(simplexBounds[B]))
                {
                    return;
                }

                // get the start index and size of each simplex:
                int simplexStartA = simplexCounts.GetSimplexStartAndSize(A, out int simplexSizeA);
                int simplexStartB = simplexCounts.GetSimplexStartAndSize(B, out int simplexSizeB);

                // immediately reject simplex pairs that share particles:
                for (int a = 0; a < simplexSizeA; ++a)
                {
                    for (int b = 0; b < simplexSizeB; ++b)
                    {
                        if (simplices[simplexStartA + a] == simplices[simplexStartB + b])
                        {
                            return;
                        }
                    }
                }

                // get phases for each simplex:
                bool restPositionsEnabled = false;
                int  groupA = GetSimplexPhase(simplexStartA, simplexSizeA, out Oni.ParticleFlags flagsA, ref restPositionsEnabled);
                int  groupB = GetSimplexPhase(simplexStartB, simplexSizeB, out Oni.ParticleFlags flagsB, ref restPositionsEnabled);

                // if all particles have the same group and none have self-collision, reject the pair.
                if (groupA == groupB && (flagsA & flagsB & Oni.ParticleFlags.SelfCollide) == 0)
                {
                    return;
                }

                // if all simplices are fluid, check their smoothing radii:
                if ((flagsA & Oni.ParticleFlags.Fluid) != 0 && (flagsB & Oni.ParticleFlags.Fluid) != 0)
                {
                    int particleA = simplices[simplexStartA];
                    int particleB = simplices[simplexStartB];

                    // for fluid we only consider the first particle in each simplex.
                    float4 predictedPositionA = positions[particleA] + velocities[particleA] * dt;
                    float4 predictedPositionB = positions[particleB] + velocities[particleB] * dt;

                    // Calculate particle center distance:
                    float d2 = math.lengthsq(predictedPositionA - predictedPositionB);

                    float fluidDistance = math.max(fluidRadii[particleA], fluidRadii[particleB]);
                    if (d2 <= fluidDistance * fluidDistance)
                    {
                        fluidInteractionsQueue.Enqueue(new FluidInteraction {
                            particleA = particleA, particleB = particleB
                        });
                    }
                }
                else // at least one solid particle is present:
                {
                    // swap simplices so that B is always the one-sided one.
                    if ((flagsA & Oni.ParticleFlags.OneSided) != 0 && groupA < groupB)
                    {
                        ObiUtils.Swap(ref A, ref B);
                        ObiUtils.Swap(ref simplexStartA, ref simplexStartB);
                        ObiUtils.Swap(ref simplexSizeA, ref simplexSizeB);
                        ObiUtils.Swap(ref flagsA, ref flagsB);
                        ObiUtils.Swap(ref groupA, ref groupB);
                    }

                    float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSizeA);
                    float4 simplexPoint;

                    simplexShape.simplexStart = simplexStartB;
                    simplexShape.simplexSize  = simplexSizeB;
                    simplexShape.positions    = restPositions;
                    simplexShape.CacheData();

                    float simplexRadiusA = 0, simplexRadiusB = 0;

                    // skip the contact if there's self-intersection at rest:
                    if (groupA == groupB && restPositionsEnabled)
                    {
                        var restPoint = BurstLocalOptimization.Optimize <BurstSimplex>(ref simplexShape, restPositions, radii,
                                                                                       simplices, simplexStartA, simplexSizeA, ref simplexBary, out simplexPoint, 4, 0);

                        for (int j = 0; j < simplexSizeA; ++j)
                        {
                            simplexRadiusA += radii[simplices[simplexStartA + j]].x * simplexBary[j];
                        }

                        for (int j = 0; j < simplexSizeB; ++j)
                        {
                            simplexRadiusB += radii[simplices[simplexStartB + j]].x * restPoint.bary[j];
                        }

                        // compare distance along contact normal with radius.
                        if (math.dot(simplexPoint - restPoint.point, restPoint.normal) < simplexRadiusA + simplexRadiusB)
                        {
                            return;
                        }
                    }

                    simplexBary            = BurstMath.BarycenterForSimplexOfSize(simplexSizeA);
                    simplexShape.positions = positions;
                    simplexShape.CacheData();

                    var surfacePoint = BurstLocalOptimization.Optimize <BurstSimplex>(ref simplexShape, positions, radii,
                                                                                      simplices, simplexStartA, simplexSizeA, ref simplexBary, out simplexPoint, optimizationIterations, optimizationTolerance);

                    simplexRadiusA = 0; simplexRadiusB = 0;
                    float4 velocityA = float4.zero, velocityB = float4.zero, normalB = float4.zero;

                    for (int j = 0; j < simplexSizeA; ++j)
                    {
                        int particleIndex = simplices[simplexStartA + j];
                        simplexRadiusA += radii[particleIndex].x * simplexBary[j];
                        velocityA      += velocities[particleIndex] * simplexBary[j];
                    }

                    for (int j = 0; j < simplexSizeB; ++j)
                    {
                        int particleIndex = simplices[simplexStartB + j];
                        simplexRadiusB += radii[particleIndex].x * surfacePoint.bary[j];
                        velocityB      += velocities[particleIndex] * surfacePoint.bary[j];
                        normalB        += normals[particleIndex] * surfacePoint.bary[j];
                    }

                    float dAB = math.dot(simplexPoint - surfacePoint.point, surfacePoint.normal);
                    float vel = math.dot(velocityA - velocityB, surfacePoint.normal);

                    // check if the projected velocity along the contact normal will get us within collision distance.
                    if (vel * dt + dAB <= simplexRadiusA + simplexRadiusB + collisionMargin)
                    {
                        // adapt collision normal for one-sided simplices:
                        if ((flagsB & Oni.ParticleFlags.OneSided) != 0 && groupB < groupA)
                        {
                            BurstMath.OneSidedNormal(normalB, ref surfacePoint.normal);
                        }

                        contactsQueue.Enqueue(new BurstContact()
                        {
                            bodyA  = A,
                            bodyB  = B,
                            pointA = simplexBary,
                            pointB = surfacePoint.bary,
                            normal = surfacePoint.normal
                        });
                    }
                }
            }