예제 #1
0
        /**
         * Iterative version of the Ramer-Douglas-Peucker path decimation algorithm.
         */
        private bool Decimate(ObiList <ObiPathFrame> input, ObiList <ObiPathFrame> output, float threshold)
        {
            // no decimation, no work to do, just return:
            if (threshold < 0.00001f || input.Count < 3)
            {
                return(false);
            }

            float scaledThreshold = threshold * threshold * 0.01f;

            stack.Push(new Vector2Int(0, input.Count - 1));
            var bitArray = new BitArray(input.Count, true);

            while (stack.Count > 0)
            {
                var range = stack.Pop();

                float dmax  = 0;
                int   index = range.x;
                float mu    = 0;

                for (int i = index + 1; i < range.y; ++i)
                {
                    if (bitArray[i])
                    {
                        float d = Vector3.SqrMagnitude(ObiUtils.ProjectPointLine(input[i].position, input[range.x].position, input[range.y].position, out mu) - input[i].position);

                        if (d > dmax)
                        {
                            index = i;
                            dmax  = d;
                        }
                    }
                }

                if (dmax > scaledThreshold)
                {
                    stack.Push(new Vector2Int(range.x, index));
                    stack.Push(new Vector2Int(index, range.y));
                }
                else
                {
                    for (int i = range.x + 1; i < range.y; ++i)
                    {
                        bitArray[i] = false;
                    }
                }
            }

            output.Clear();
            for (int i = 0; i < bitArray.Count; ++i)
            {
                if (bitArray[i])
                {
                    output.Add(input[i]);
                }
            }

            return(true);
        }
예제 #2
0
        /**
         * Generates raw curve chunks from the rope description.
         */
        private void AllocateRawChunks(ObiRopeBase actor)
        {
            using (m_AllocateRawChunksPerfMarker.Auto())
            {
                rawChunks.Clear();

                if (actor.path == null)
                {
                    return;
                }

                // Count particles for each chunk.
                int particles = 0;
                for (int i = 0; i < actor.elements.Count; ++i)
                {
                    particles++;
                    // At discontinuities, start a new chunk.
                    if (i < actor.elements.Count - 1 && actor.elements[i].particle2 != actor.elements[i + 1].particle1)
                    {
                        AllocateChunk(++particles);
                        particles = 0;
                    }
                }
                AllocateChunk(++particles);
            }
        }
예제 #3
0
        /**
         * Counts the amount of continuous sections in each chunk of rope.
         */
        protected void CountContinuousSegments()
        {
            rawCurves.Clear();

            int segmentCount = 0;
            int lastParticle = -1;

            // Iterate trough all distance constraints in order. If we find a discontinuity, reset segment count:
            int constraintCount = GetStructuralConstraintCount();

            for (int i = 0; i < constraintCount; ++i)
            {
                int particle1 = -1, particle2 = -1;
                if (GetStructuralConstraintParticles(i, ref particle1, ref particle2))
                {
                    // start new curve at discontinuities:
                    if (particle1 != lastParticle && segmentCount > 0)
                    {
                        // add a new curve with the correct amount of sections:
                        AddCurve(segmentCount + 1);
                        segmentCount = 0;
                    }

                    lastParticle = particle2;
                    segmentCount++;
                }
            }

            // add the last curve:
            AddCurve(segmentCount + 1);
        }
예제 #4
0
        /**
         * Counts the amount of continuous sections in each chunk of rope.
         */
        protected void CountContinuousSegments()
        {
            rawCurves.Clear();
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            int segmentCount = 0;
            int lastParticle = -1;

            // Iterate trough all distance constraints in order. If we find a discontinuity, reset segment count:
            for (int i = 0; i < distanceBatch.ConstraintCount; ++i)
            {
                int particle1 = distanceBatch.springIndices[i * 2];
                int particle2 = distanceBatch.springIndices[i * 2 + 1];

                // start new curve at discontinuities:
                if (particle1 != lastParticle && segmentCount > 0)
                {
                    // add a new curve with the correct amount of sections:
                    AddCurve(segmentCount + 1);
                    segmentCount = 0;
                }

                lastParticle = particle2;
                segmentCount++;
            }

            // add the last curve:
            AddCurve(segmentCount + 1);
        }
예제 #5
0
        /**
         * Generate a list of smooth curves using particles as control points. Will take into account cuts in the rope,
         * generating one curve for each continuous piece of rope.
         */
        public void SmoothCurvesFromParticles()
        {
            curves.Clear();

            curveSections = 0;
            curveLength   = 0;

            // count amount of segments in each rope chunk:
            CountContinuousSegments();

            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();
            Matrix4x4 w2l = transform.worldToLocalMatrix;

            int firstSegment = 0;

            // generate curve for each rope chunk:
            for (int i = 0; i < rawCurves.Count; ++i)
            {
                int segments = rawCurves[i].Count - 1;

                // allocate memory for the curve:
                ObiList <CurveSection> controlPoints = rawCurves[i];

                // get control points position:
                for (int m = 0; m < segments; ++m)
                {
                    int particleIndex = distanceBatch.springIndices[(firstSegment + m) * 2];
                    controlPoints[m] = new CurveSection(w2l.MultiplyPoint3x4(GetParticlePosition(particleIndex)),
                                                        solidRadii[particleIndex],
                                                        (this.colors != null && particleIndex < this.colors.Length) ? this.colors[particleIndex] : Color.white);

                    // last segment adds its second particle too:
                    if (m == segments - 1)
                    {
                        particleIndex        = distanceBatch.springIndices[(firstSegment + m) * 2 + 1];
                        controlPoints[m + 1] = new CurveSection(w2l.MultiplyPoint3x4(GetParticlePosition(particleIndex)),
                                                                solidRadii[particleIndex],
                                                                (this.colors != null && particleIndex < this.colors.Length) ? this.colors[particleIndex] : Color.white);
                    }
                }

                firstSegment += segments;

                // get smooth curve points:
                ChaikinSmoothing(controlPoints, curves[i], smoothing);

                // count total curve sections and total curve length:
                curveSections += curves[i].Count - 1;
                curveLength   += CalculateCurveLength(curves[i]);
            }
        }
예제 #6
0
        /**
         * Generates smooth curve chunks.
         */
        public void GenerateSmoothChunks(ObiRopeBase actor, uint smoothingLevels)
        {
            using (m_GenerateSmoothChunksPerfMarker.Auto())
            {
                smoothChunks.Clear();
                smoothSections = 0;
                smoothLength   = 0;

                if (!Application.isPlaying)
                {
                    actor.RebuildElementsFromConstraints();
                }

                AllocateRawChunks(actor);

                w2l         = actor.transform.worldToLocalMatrix;
                w2lRotation = w2l.rotation;

                // keep track of the first element of each chunk
                int chunkStart = 0;

                ObiPathFrame frame_0 = new ObiPathFrame(); // "next" frame
                ObiPathFrame frame_1 = new ObiPathFrame(); // current frame
                ObiPathFrame frame_2 = new ObiPathFrame(); // previous frame

                // generate curve for each rope chunk:
                for (int i = 0; i < rawChunks.Count; ++i)
                {
                    int elementCount = rawChunks[i].Count - 1;

                    // Initialize frames:
                    frame_0.Reset();
                    frame_1.Reset();
                    frame_2.Reset();

                    PathFrameFromParticle(actor, ref frame_1, actor.elements[chunkStart].particle1, false);

                    frame_2 = frame_1;

                    for (int m = 1; m <= rawChunks[i].Count; ++m)
                    {
                        int index;
                        if (m >= elementCount)
                        {
                            // second particle of last element in the chunk.
                            index = actor.elements[chunkStart + elementCount - 1].particle2;
                        }
                        else
                        {
                            //first particle of current element.
                            index = actor.elements[chunkStart + m].particle1;
                        }

                        // generate curve frame from particle:
                        PathFrameFromParticle(actor, ref frame_0, index);

                        if (actor.usesOrientedParticles)
                        {
                            // copy frame directly.
                            frame_2 = frame_1;
                        }
                        else
                        {
                            // perform parallel transport, using forward / backward average to calculate tangent.
                            frame_1.tangent = ((frame_1.position - frame_2.position) + (frame_0.position - frame_1.position)).normalized;
                            frame_2.Transport(frame_1, twist);
                        }

                        // in case we wrapped around the rope, average first and last frames:
                        if (chunkStart + m > actor.activeParticleCount)
                        {
                            frame_2 = rawChunks[0][0] = 0.5f * frame_2 + 0.5f * rawChunks[0][0];
                        }

                        frame_1 = frame_0;

                        rawChunks[i][m - 1] = frame_2;
                    }

                    // increment chunkStart by the amount of elements in this chunk:
                    chunkStart += elementCount;

                    // adaptive curvature-based decimation:
                    if (Decimate(rawChunks[i], smoothChunks[i], decimation))
                    {
                        // if any decimation took place, swap raw and smooth chunks:
                        var aux = rawChunks[i];
                        rawChunks[i]    = smoothChunks[i];
                        smoothChunks[i] = aux;
                    }

                    // get smooth curve points:
                    Chaikin(rawChunks[i], smoothChunks[i], smoothingLevels);

                    // count total curve sections and total curve length:
                    smoothSections += smoothChunks[i].Count - 1;
                    smoothLength   += CalculateChunkLength(smoothChunks[i]);
                }
            }
        }
예제 #7
0
        /**
         * Generate a list of smooth curves using particles as control points. Will take into account cuts in the rope,
         * generating one curve for each continuous piece of rope.
         */
        public void SmoothCurvesFromParticles()
        {
            curves.Clear();

            curveSections = 0;
            curveLength   = 0;

            // count amount of segments in each rope chunk:
            CountContinuousSegments();

            Matrix4x4  w2l            = transform.worldToLocalMatrix;
            Quaternion matrixRotation = Quaternion.LookRotation(
                w2l.GetColumn(2),
                w2l.GetColumn(1));
            int firstSegment = 0;

            // generate curve for each rope chunk:
            for (int i = 0; i < rawCurves.Count; ++i)
            {
                int segments = rawCurves[i].Count - 1;

                // allocate memory for the curve:
                ObiList <ObiCurveSection> controlPoints = rawCurves[i];

                // get control points position:
                int lastParticle = -1;
                int particle1 = -1, particle2 = -1;
                for (int m = 0; m < segments; ++m)
                {
                    if (GetStructuralConstraintParticles(firstSegment + m, ref particle1, ref particle2))
                    {
                        if (m == 0)
                        {
                            lastParticle = particle1;
                        }

                        // Find next and previous vectors:
                        Vector3 nextV = GetParticlePosition(particle2) - GetParticlePosition(particle1);
                        Vector3 prevV = GetParticlePosition(particle1) - GetParticlePosition(lastParticle);

                        Vector3    pos     = w2l.MultiplyPoint3x4(GetParticlePosition(particle1));
                        Quaternion orient  = matrixRotation * Quaternion.SlerpUnclamped(GetParticleOrientation(lastParticle), GetParticleOrientation(particle1), 0.5f);
                        Vector3    tangent = w2l.MultiplyVector(prevV + nextV).normalized;
                        Color      color   = (this.colors != null && particle1 < this.colors.Length) ? this.colors[particle1] : Color.white;

                        controlPoints[m] = new ObiCurveSection(new Vector4(pos.x, pos.y, pos.z, principalRadii[particle1][0]), tangent, orient * Vector3.up, color);

                        lastParticle = particle1;
                    }
                }

                // last segment adds its second particle too:
                if (segments > 0)
                {
                    Vector3    pos     = w2l.MultiplyPoint3x4(GetParticlePosition(particle2));
                    Quaternion orient  = matrixRotation * GetParticleOrientation(particle1);
                    Vector3    tangent = w2l.MultiplyVector(GetParticlePosition(particle2) - GetParticlePosition(particle1)).normalized;
                    Color      color   = (this.colors != null && particle2 < this.colors.Length) ? this.colors[particle2] : Color.white;

                    controlPoints[segments] = new ObiCurveSection(new Vector4(pos.x, pos.y, pos.z, principalRadii[particle2][0]), tangent, orient * Vector3.up, color);
                }

                firstSegment += segments;

                // get smooth curve points:
                ObiCurveFrame.Chaikin(controlPoints, curves[i], smoothing);

                // count total curve sections and total curve length:
                curveSections += curves[i].Count - 1;
                curveLength   += CalculateCurveLength(curves[i]);
            }
        }