Esempio n. 1
0
        //helper function to determine all viable snap candidates given an actor and a starting position
        // will automatically reject candidates outside the configured min snap offset
        // will also automatically weight the previous frame's results higher to reduce jitter between two close snap points
        private void AddToCandidates(GameThing thing, Vector3 snapFromPosition, int sourceIndex, ref List <CandidateConnection> snapCandidates)
        {
            //loop over all game things looking for pipes
            for (int i = 0; i < InGame.inGame.gameThingList.Count; i++)
            {
                GameActor actor = InGame.inGame.gameThingList[i] as GameActor;

                if (actor != null && actor.Movement != null && actor != (thing as GameActor))
                {
                    if (actor.Chassis != null && actor.Chassis is PipeChassis)
                    {
                        //found a pipe that isn't ourself, look for candidates
                        PipeChassis targetPipe = actor.Chassis as PipeChassis;

                        List <PipeConnection> potentialSnapPoints = GenerateConnections(actor, actor.Movement.Position);

                        //weight each candidate based on how far away it is - if too far, reject it completely
                        foreach (PipeConnection nextSnapPoint in potentialSnapPoints)
                        {
                            Vector3 snapVector = nextSnapPoint.SourcePos - snapFromPosition;
                            snapVector.Z = 0.0f;

                            float distance = snapVector.Length();
                            //if distance is in max offset or, if it's exactly where we snapped to last time, allow for double the radius to consider it valid
                            if (distance <= kMaxSnapOffset * Parent.ReScale ||
                                (m_bIsSnapped && m_lastSnapSourceIndex == sourceIndex && distance < kMaxSnapOffset * Parent.ReScale * 2.0f))
                            {
                                float weight     = 1.0f - MathHelper.Clamp(distance / kMaxSnapOffset, 0.0f, 1.0f);
                                bool  retainSnap = false;

                                //special case: were we previously snapped to this exact target, from the same source?
                                // if so, add some weight - we'd prefer to stay in place
                                if (m_bIsSnapped && m_lastSnapSourceIndex == sourceIndex)
                                {
                                    float distanceToLastSnapped = (m_lastSnapTarget - nextSnapPoint.SourcePos).Length();
                                    if (distanceToLastSnapped < 0.001f)
                                    {
                                        weight    += 1.0f;
                                        retainSnap = true;
                                    }
                                }

                                //found a candidate, add it to the lsit
                                CandidateConnection newCandidate = new CandidateConnection {
                                    SourceConnectionIndex = sourceIndex,
                                    SnapToPosition        = nextSnapPoint.SourcePos,
                                    UpDir          = nextSnapPoint.SourceUpDir,
                                    CenterPosition = actor.Movement.Position,
                                    Weight         = weight,
                                    RetainSnap     = retainSnap
                                };
                                snapCandidates.Add(newCandidate);
                            }
                        }
                    }
                }
            }
        }
Esempio n. 2
0
        //helper function that, based on a given actor and reference position, will find the best connection to snap to
        //returns false if no connection found (pipe is not close to other pipes)
        private bool FindSnapToConnection(GameThing thing, Vector3 snapFromPosition, out PipeConnection snapToConnection)
        {
            snapToConnection = new PipeConnection {
                SourcePos = snapFromPosition, TargetPos = snapFromPosition
            };

            GameActor actor = thing as GameActor;

            if (actor == null || actor.Movement == null)
            {
                return(false);
            }

            //build a list of connections based on our pipe type
            List <PipeConnection> connections = GenerateConnections(actor, snapFromPosition);

            //generate a list of all candidate snap to targets
            List <CandidateConnection> candidates = new List <CandidateConnection>();

            for (int i = 0; i < connections.Count; ++i)
            {
                PipeConnection connection = connections[i];

                AddToCandidates(thing, connection.SourcePos, i, ref candidates);
            }

            //no snap to targets?  early out...
            if (candidates.Count <= 0)
            {
                m_bIsSnapped = false;
                return(false);
            }

            //find the candidate with the highest weight
            CandidateConnection bestCandidate = new CandidateConnection();

            bestCandidate.Weight = float.MinValue;

            for (int j = 0; j < candidates.Count; ++j)
            {
                if (candidates[j].Weight > bestCandidate.Weight)
                {
                    bestCandidate = candidates[j];
                }
            }

            //update the snap connection we're building based on the best candidate's values
            snapToConnection = connections[bestCandidate.SourceConnectionIndex];

            //if we were already snapped and our candidate is the same, then don't recalculate push-together amount

            //based on up dir difference, bump the snap position closer to the center of the target object
            float cosAngle = Vector3.Dot(bestCandidate.UpDir, connections[bestCandidate.SourceConnectionIndex].SourceUpDir);

            Vector3 pushTogetherDirection = bestCandidate.CenterPosition - bestCandidate.SnapToPosition;

            pushTogetherDirection.Normalize();

            ///////////////////////////////////////////
            //JITTER REDUCTION + PUSH TOGETHER LOGIC
            // The below code pushes together pipe pieces whose up vectors are not aligned, attempting to cover gaps
            // It also looks for cases where the source conditions were identical to last frame, and uses cached values
            //  instead if so, preventing jitter
            ///////////////////////////////////////////

            //if snapping to same place, only allow push together amount if it is larger
            Vector3 pushTogetherAmount = pushTogetherDirection * kPushTogetherFactor * (1.0f - cosAngle);

            if (m_bIsSnapped && bestCandidate.RetainSnap)
            {
                //if the cursor hasn't moved and we're still snapped, don't ever update the source pos
                float deltaSnapFrom = (m_lastSnapFromPos - snapFromPosition).Length();
                if (deltaSnapFrom < 0.01f)
                {
                    //same connection and cursor hasn't moved, override the source pos to match what we had last time
                    snapToConnection.SourcePos = m_lastSourcePos;
                }
                else
                {
                    //cursor was moved, update source info
                    m_lastSnapFromPos = snapFromPosition;
                    m_lastSourcePos   = snapToConnection.SourcePos;
                }

                //check if push together amount has a better value than last time
                if (pushTogetherAmount.LengthSquared() > m_lastPushTogetherAmount.LengthSquared())
                {
                    //this push amount is better, let's use it
                    snapToConnection.TargetPos = bestCandidate.SnapToPosition + pushTogetherAmount;
                    m_lastPushTogetherAmount   = pushTogetherAmount;
                }
                else
                {
                    //our previously value still the best, keep it
                    snapToConnection.TargetPos = bestCandidate.SnapToPosition + m_lastPushTogetherAmount;
                }
            }
            else
            {
                //we weren't snapped here before, update various state
                snapToConnection.TargetPos = bestCandidate.SnapToPosition + pushTogetherAmount;
                m_lastPushTogetherAmount   = pushTogetherAmount;
                m_lastSnapFromPos          = snapFromPosition;
                m_lastSourcePos            = snapToConnection.SourcePos;
            }

            //if we get this far, we found a snap taget, update some final values and return true
            m_bIsSnapped          = true;
            m_lastSnapSourceIndex = bestCandidate.SourceConnectionIndex;
            m_lastSnapTarget      = bestCandidate.SnapToPosition; //where were we trying to snap to?

            return(true);
        }