Beispiel #1
0
        // A simplified version of TwoPointObjectTransformation. The following properties are true:
        //   1. The object-local-space direction between the left and right hands remains constant.
        //   2. The object-local-space position of LerpUnclamped(left, right, constraintPositionT) remains
        //      constant.
        //   3. obj1 has the same scale as obj0.
        //   4. (Corollary of 1-3) The object-local-space positions of left and right remain constant, if
        //      the distance between them does not change.
        public static TrTransform TwoPointObjectTransformationNoScale(
            TrTransform gripL0, TrTransform gripR0,
            TrTransform gripL1, TrTransform gripR1,
            TrTransform obj0, float constraintPositionT)
        {
            // Vectors from left-hand to right-hand
            Vector3 vLR0 = (gripR0.translation - gripL0.translation);
            Vector3 vLR1 = (gripR1.translation - gripL1.translation);

            Vector3     pivot0;
            TrTransform xfDelta; {
                pivot0 = Vector3.LerpUnclamped(gripL0.translation, gripR0.translation, constraintPositionT);
                var pivot1 = Vector3.LerpUnclamped(gripL1.translation, gripR1.translation, constraintPositionT);
                xfDelta.translation = pivot1 - pivot0;

                xfDelta.translation = Vector3.LerpUnclamped(
                    gripL1.translation - gripL0.translation,
                    gripR1.translation - gripR0.translation,
                    constraintPositionT);
                // TODO: check edge cases:
                // - |vLR0| or |vLR1| == 0 (ie, from and/or to are undefined)
                // - vLR1 == vLR0 * -1 (ie, infinite number of axes of rotation)
                xfDelta.rotation = Quaternion.FromToRotation(vLR0, vLR1);
                xfDelta.scale    = 1;
            }

            Quaternion deltaL = ConstrainRotationDelta(gripL0.rotation, gripL1.rotation, vLR0);
            Quaternion deltaR = ConstrainRotationDelta(gripR0.rotation, gripR1.rotation, vLR0);

            xfDelta = TrTransform.R(Quaternion.Slerp(deltaL, deltaR, 0.5f)) * xfDelta;

            // Set pivot point
            xfDelta = xfDelta.TransformBy(TrTransform.T(pivot0));
            return(xfDelta * obj0);
        }
Beispiel #2
0
        public void CreatePreviewModel()
        {
            // Is there a model, is it valid, and does it need a new preview object?
            if (m_Model == null || !m_Model.m_Valid || m_Model == m_ModelPreviewModel)
            {
                return;
            }

            // We know the model has changed, so destroy the preview.
            if (m_ModelPreview != null)
            {
                Destroy(m_ModelPreview.gameObject);
            }

            // Remember the model for which this preview was created.
            m_ModelPreviewModel = m_Model;

            // Build the actual preview.
            m_ModelPreview = Instantiate(m_Model.m_ModelParent);
            HierarchyUtils.RecursivelySetLayer(m_ModelPreview, LayerMask.NameToLayer("Panels"));
            m_ModelPreview.gameObject.SetActive(true);
            m_ModelPreview.parent = m_PreviewParent;
            float maxSide = Mathf.Max(m_Model.m_MeshBounds.size.x,
                                      Mathf.Max(m_Model.m_MeshBounds.size.y, m_Model.m_MeshBounds.size.z));
            TrTransform xf = TrTransform.S(1 / maxSide) * TrTransform.T(-m_Model.m_MeshBounds.center);

            Coords.AsLocal[m_ModelPreview] = xf;
            HierarchyUtils.RecursivelyDisableShadows(m_ModelPreview);
        }
        private static void SanityCheckVersusReplacementBrush(Stroke oldStroke)
        {
            BrushDescriptor desc            = BrushCatalog.m_Instance.GetBrush(oldStroke.m_BrushGuid);
            BrushDescriptor replacementDesc = desc.m_Supersedes;

            if (replacementDesc == null)
            {
                return;
            }

            // Make a copy, since Begin/EndLineFromMemory mutate little bits of MemoryBrushStroke
            Stroke newStroke = new Stroke {
                m_BrushGuid      = replacementDesc.m_Guid,
                m_IntendedCanvas = oldStroke.Canvas,
                m_ControlPoints  = oldStroke.m_ControlPoints,
                m_BrushScale     = oldStroke.m_BrushScale,
                m_BrushSize      = oldStroke.m_BrushSize,
                m_Color          = oldStroke.m_Color,
                m_Seed           = oldStroke.m_Seed
            };

            Array.Copy(oldStroke.m_ControlPointsToDrop, newStroke.m_ControlPointsToDrop,
                       oldStroke.m_ControlPointsToDrop.Length);

            newStroke.Recreate(TrTransform.T(new Vector3(0.5f, 0, 0)));
        }
 public void ExtendPath(Vector3 pos, CameraPathTool.ExtendPathType extendType) {
   Debug.Assert(extendType != CameraPathTool.ExtendPathType.None);
   int index = (extendType == CameraPathTool.ExtendPathType.ExtendAtHead) ? 0 :
       Path.NumPositionKnots;
   // If we're extending the path into a loop, ignore the passed position.
   if (extendType == CameraPathTool.ExtendPathType.Loop) {
     pos = Path.PositionKnots[0].transform.position;
   }
   SketchMemoryScript.m_Instance.PerformAndRecordCommand(new CreatePathKnotCommand(
       this, CameraPathKnot.Type.Position, new PathT(index), TrTransform.T(pos)));
 }
Beispiel #5
0
        public void TestTwoPointObjectTransformationNoScale(
            float l0x, float l0y, float l0z, float r0x, float r0y, float r0z,
            float l1x, float l1y, float l1z, float r1x, float r1y, float r1z,
            float constraintPositionT,
            float tx, float ty, float tz,
            float angle, float ax, float ay, float az)
        {
            var obj1 = MathUtils.TwoPointObjectTransformationNoScale(
                TrTransform.T(new Vector3(l0x, l0y, l0z)),
                TrTransform.T(new Vector3(r0x, r0y, r0z)),
                TrTransform.T(new Vector3(l1x, l1y, l1z)),
                TrTransform.T(new Vector3(r1x, r1y, r1z)),
                TrTransform.identity, constraintPositionT);

            AssertAlmostEqual(obj1.translation, new Vector3(tx, ty, tz));
            AssertAlmostEqual(obj1.rotation, angle, new Vector3(ax, ay, az));
            AssertAlmostEqual(obj1.scale, 1);
        }
Beispiel #6
0
            protected override TrTransform CalculateChildXf(List <PbKnot> parentKnots)
            {
                PbKnot lastKnot = parentKnots[parentKnots.Count - 1];

                float distanceMeters = lastKnot.m_distance * App.UNITS_TO_METERS;
                float t = O.CyclesPerMeter * distanceMeters + (float)m_strand / O.NumStrands;
                // Our periodic function makes the plait look pretty square; maybe add
                // some rotation to break things up a bit?
                float rotations = (O.CyclesPerMeter * distanceMeters) * O.RotationsPerCycle;

                float       amplitude = lastKnot.m_pressuredSize / 2; // /2 because size is diameter, not radius.
                TrTransform action    =
                    TrTransform.R(rotations * 360, Vector3.forward) *
                    TrTransform.T(SomePeriodicFunction(t) * amplitude);

                TrTransform actionInCanvasSpace = action.TransformBy(lastKnot.GetFrame(AttachFrame.LineTangent));

                return(actionInCanvasSpace * lastKnot.m_pointer);
            }
Beispiel #7
0
        /// Given the position of a main pointer, find a corresponding symmetry position.
        /// Results are undefined unless you pass MainPointer or one of its
        /// dedicated symmetry pointers.
        public TrTransform GetSymmetryTransformFor(PointerScript pointer, TrTransform xfMain)
        {
            int child = pointer.ChildIndex;

            // "active pointers" is the number of pointers the symmetry widget is using,
            // including the main pointer.
            if (child == 0 || child >= m_NumActivePointers)
            {
                return(xfMain);
            }

            // This needs to be kept in sync with UpdateSymmetryPointerTransforms
            switch (m_CurrentSymmetryMode)
            {
            case SymmetryMode.SinglePlane:
            {
                return(m_SymmetryWidgetScript.ReflectionPlane.ReflectPoseKeepHandedness(xfMain));
            }

            case SymmetryMode.FourAroundY:
            {
                // aboutY is an operator that rotates worldspace objects N degrees around the widget's Y
                TrTransform aboutY;
                {
                    var   xfWidget = TrTransform.FromTransform(m_SymmetryWidget);
                    float angle    = (360f * child) / m_NumActivePointers;
                    aboutY = TrTransform.TR(Vector3.zero, Quaternion.AngleAxis(angle, Vector3.up));
                    // convert from widget-local coords to world coords
                    aboutY = aboutY.TransformBy(xfWidget);
                }
                return(aboutY * xfMain);
            }

            case SymmetryMode.DebugMultiple:
            {
                var xfLift = TrTransform.T(m_SymmetryDebugMultipleOffset * child);
                return(xfLift * xfMain);
            }

            default:
                return(xfMain);
            }
        }
Beispiel #8
0
        public void TestTwoPointTransformationAxisResize_Simple()
        {
            // TODO: test the bounds clamping too
            // Just a simple sanity check
            var   xfL0 = TrTransform.T(new Vector3(3, 4, 1));
            var   xfR0 = TrTransform.T(new Vector3(4, 4, 1));
            var   xfL1 = TrTransform.T(new Vector3(3, 2, 2));
            var   xfR1 = TrTransform.T(new Vector3(3, 4, 2));
            var   axis = new Vector3(1, 0, 0);
            var   size = 8;
            var   obj0 = TrTransform.T(new Vector3(2, 3, 0));
            float deltaScale;
            var   obj1 = MathUtils.TwoPointObjectTransformationAxisResize(
                axis, size, xfL0, xfR0, xfL1, xfR1, obj0,
                out deltaScale);

            AssertAlmostEqual(deltaScale, 9 / 8f);
            AssertAlmostEqual(obj1.rotation, 90, new Vector3(0, 0, 1));
            AssertAlmostEqual(obj1.translation, new Vector3(4, 1.5f, 1));
        }
Beispiel #9
0
        protected override void MaybeCreateChildrenImpl()
        {
            // Only create children once
            if (m_children.Count > 1)
            {
                return;
            }

            if (m_recursionLevel == 0)
            {
                float   radiusMeters = m_caneGrossRadiusPct * BaseSize_LS * App.UNITS_TO_METERS;
                Vector3 offsetT      = m_caneGrossRadiusPct * Vector3.right;
                for (int i = 0; i < m_numGrossParents; ++i)
                {
                    Quaternion  rotation        = Quaternion.AngleAxis(i * 360.0f / m_numGrossParents, Vector3.forward);
                    TrTransform offset          = TrTransform.T(rotation * offsetT);
                    float       degreesPerMeter = m_grossRotationsPerRadian / radiusMeters * 360;
                    InitializeAndAddChild(
                        new PbChildWithOffset(-1, AttachFrame.LineTangent, offset, degreesPerMeter),
                        m_Desc, // Recurse with same brush
                        Color.white);
                }
            }
            else
            {
                float   radiusMeters = m_caneFineRadiusPct * BaseSize_LS * App.UNITS_TO_METERS;
                Vector3 offsetT      = m_caneFineRadiusPct * Vector3.right;
                for (int i = 0; i < kCaneColors.Length; ++i)
                {
                    Quaternion  rotation        = Quaternion.AngleAxis(i * 360.0f / kCaneColors.Length, Vector3.forward);
                    TrTransform offset          = TrTransform.T(rotation * offsetT);
                    float       degreesPerMeter = m_fineRotationsPerRadian / radiusMeters * 360;
                    InitializeAndAddChild(
                        new PbChildWithOffset(-1, AttachFrame.LineTangent, offset, degreesPerMeter),
                        m_caneBrushes[i % m_caneBrushes.Length],
                        kCaneColors[i],
                        m_strandSizePercent);
                }
            }
        }
Beispiel #10
0
        public void TestTwoPointNonUniformScale(
            float l0x, float l0y, float l0z, float r0x, float r0y, float r0z,
            float l1x, float l1y, float l1z, float r1x, float r1y, float r1z,
            float deltaScaleMin, float deltaScaleMax,
            float desiredDeltaScale,
            float tx, float ty, float tz,
            float angle, float ax, float ay, float az)
        {
            float deltaScale;
            var   obj1 = MathUtils.TwoPointObjectTransformationNonUniformScale(
                new Vector3(1, 0, 0), // axis
                TrTransform.T(new Vector3(l0x, l0y, l0z)),
                TrTransform.T(new Vector3(r0x, r0y, r0z)),
                TrTransform.T(new Vector3(l1x, l1y, l1z)),
                TrTransform.T(new Vector3(r1x, r1y, r1z)),
                TrTransform.identity,
                out deltaScale,
                deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax);

            AssertAlmostEqual(deltaScale, desiredDeltaScale);
            AssertAlmostEqual(obj1.translation, new Vector3(tx, ty, tz));
            AssertAlmostEqual(obj1.rotation, angle, new Vector3(ax, ay, az));
        }
Beispiel #11
0
        // Constructs an updated transform obj1 such that the object-space
        // positions of the left and right grip are invariant. More generally,
        // all points on the line L->R are invariant. Precisely:
        //
        // - inv(obj0) * L0.pos == inv(obj1) * L1.pos
        // - inv(obj0) * R0.pos == inv(obj1) * L1.pos
        //
        // If abs(R0-L0) != abs(R1-L1), then a scaling is necessary.
        // This method chooses to apply a uniform scale.
        //
        // If scale bounds kick in, only a single point on the L->R line can
        // be made invariant; see bUseLeftAsPivot for how this is chosen.
        //
        // Given 2 position deltas, there are 6 DOFs available:
        //  = 3DOF (movement of average position)
        //  + 2DOF (change of direction of vector between L and R)
        //  + 1DOF (scaling, change of length of vector between L and R)
        //
        // One more DOF is needed to fully specify the rotation. This method
        // chooses to get it from L and R's rotations about the L-R vector.
        //
        // Pass:
        //   gripL0, L1, R0, R1 -
        //     The left and right grip points, in their old (0) and new (1) positions.
        //     The scale portion of the TrTransform is ignored.
        //   obj0 -
        //     Transform of the object being manipulated.
        //   deltaScale{Min,Max} -
        //     Constrains the range of result.scale.
        //     Negative means do not constrain that endpoint.
        //   rotationAxisConstraint -
        //     Constrains the value of result.rotation. If passed, the delta rotation
        //     from obj0 -> result will only be about this axis.
        //   bUseLeftAsPivot -
        //     Controls the invariant point if scale constraints are applied.
        //     If false, uses the midpoint between L and R.
        //     If true, uses the point L.
        //
        // Returns:
        //   New position, rotation, and scale
        static public TrTransform TwoPointObjectTransformation(
            TrTransform gripL0, TrTransform gripR0, // prev
            TrTransform gripL1, TrTransform gripR1, // next
            TrTransform obj0,
            float deltaScaleMin            = -1.0f, float deltaScaleMax = -1.0f,
            Vector3 rotationAxisConstraint = default(Vector3),
            bool bUseLeftAsPivot           = false)
        {
            // Vectors from left-hand to right-hand
            Vector3 vLR0 = (gripR0.translation - gripL0.translation);
            Vector3 vLR1 = (gripR1.translation - gripL1.translation);

            // World-space position whose object-space position is used to constrain obj1
            //    inv(obj0) * vInvariant0 = inv(obj1) * vInvariant1
            Vector3 vInvariant0, vInvariant1; {
                // Use left grip or average of grips as pivot point. Maybe switch the
                // bool to be a parametric t instead, so if caller wants to use right
                // grip as pivot they don't need to swap arguments?
                float t = bUseLeftAsPivot ? 0f : 0.5f;
                vInvariant0 = Vector3.Lerp(gripL0.translation, gripR0.translation, t);
                vInvariant1 = Vector3.Lerp(gripL1.translation, gripR1.translation, t);
            }

            // Strategy:
            // 1. Move invariant point to the correct spot, with a translation.
            // 2. Rotate about that point.
            // 3. Uniform scale about that point.
            // Items 2 and 3 can happen in the same TrTransform, since rotation
            // and uniform scale commute as long as they use the same pivot.

            TrTransform xfDelta1 = TrTransform.T(vInvariant1 - vInvariant0);

            TrTransform xfDelta23; {
                // calculate worldspace scale; will adjust center-of-scale later
                float dist0      = vLR0.magnitude;
                float dist1      = vLR1.magnitude;
                float deltaScale = (dist0 == 0) ? 1 : dist1 / dist0;

                // Clamp scale if requested.
                if (deltaScaleMin >= 0)
                {
                    deltaScale = Mathf.Max(deltaScale, deltaScaleMin);
                }
                if (deltaScaleMax >= 0)
                {
                    deltaScale = Mathf.Min(deltaScale, deltaScaleMax);
                }

                // This gets the left-right axis pointing in the correct direction
                Quaternion qSwing0To1 = Quaternion.FromToRotation(vLR0, vLR1);
                // This applies some twist about that left-right axis. The choice of constraint axis
                // (vLR0 vs vLR1) depends on whether qTwist is right- or left-multiplied vs qReach.
                Quaternion qTwistAbout0 = Quaternion.Slerp(
                    ConstrainRotationDelta(gripL0.rotation, gripL1.rotation, vLR0),
                    ConstrainRotationDelta(gripR0.rotation, gripR1.rotation, vLR0),
                    0.5f);
                Quaternion qDelta = qSwing0To1 * qTwistAbout0;
                // Constrain the rotation if requested.
                if (rotationAxisConstraint != default(Vector3))
                {
                    qDelta = ConstrainRotationDelta(Quaternion.identity, qDelta, rotationAxisConstraint);
                }

                xfDelta23 = TrTransform
                            .TRS(Vector3.zero, qDelta, deltaScale)
                            .TransformBy(TrTransform.T(vInvariant1));
            }

            return(xfDelta23 * xfDelta1 * obj0);
        }
Beispiel #12
0
        protected override void MaybeCreateChildrenImpl()
        {
            // TODO: when too many children, starve an old one in order to create another.

            int kBranchCount = 12;
            int kFrondCount  = 16;

            if (m_recursionLevel == 0)
            {
                // Trunk
                if (m_children.Count == 0)
                {
                    InitializeAndAddChild(new PbChildIdentityXf(), m_trunkBrush, m_trunkColor);
                }
                if (DistanceSinceLastKnotBasedChild() > m_branchFrequency &&
                    m_children.Count < kBranchCount + 1)
                {
                    int salt = m_children.Count;
                    // Children 1 - 13 are the branches; early ones grow faster.
                    float       growthPercent = (kBranchCount + 1 - (float)m_children.Count) / kBranchCount;
                    TrTransform offset        =
                        // Branches don't extend as quickly as the trunk
                        TrTransform.S(m_branchScale * growthPercent) *
                        // Randomly place around the tree
                        TrTransform.R(m_rng.InRange(salt, 0f, 360f), Vector3.forward) *
                        // Angle the branches backwards (away from the stroke tip)
                        TrTransform.R(120, Vector3.right);
                    InitializeAndAddChild(
                        new PbChildWithOffset(m_knots.Count - 1, AttachFrame.LineTangent, offset, 0),
                        m_Desc, // Recurse with same brush
                        Color.white, m_branchRelativeSize);
                }
            }
            else if (m_recursionLevel == 1)
            {
                // Branch
                if (m_children.Count == 0)
                {
                    InitializeAndAddChild(new PbChildIdentityXf(), m_branchBrush, m_branchColor);
                }
                // TODO: would like this frequency to be higher for tinier branches
                if (DistanceSinceLastKnotBasedChild() > m_frondFrequency &&
                    m_children.Count < kFrondCount + 1)
                {
                    float growthPercent = 1; // (kFrondCount + 1 - (float)m_children.Count) / kFrondCount;
                    for (int deg = -30; deg <= 30; deg += 60)
                    {
                        TrTransform offset =
                            // Fronds don't grow as quickly as the branch
                            TrTransform.S(m_frondScale * growthPercent) *
                            TrTransform.R(deg, Vector3.up);
                        InitializeAndAddChild(
                            new PbChildWithOffset(m_knots.Count - 1, AttachFrame.LineTangent, offset, 0),
                            m_frondBrush, m_frondColor, m_frondRelativeSize);
                        TrTransform decoOffset = TrTransform.T(m_decoOffset);
                        InitializeAndAddChild(
                            new PbChildWithOffset(-1, AttachFrame.LineTangent, decoOffset, m_decoTwist),
                            m_decoBrush, Color.white, m_frondRelativeSize);
                    }
                }
            }
        }
Beispiel #13
0
        public void TestTwoPointNonUniform_Random()
        {
            // Test doesn't try very hard to avoid unstable regions
            int ALLOWED_FAILURES = 2;
            int TRIES            = 5000;

            int seed = (int)((EditorApplication.timeSinceStartup % 1) * 100000);

            Random.InitState(seed);

            int failures = 0;

            for (int i = 0; i < TRIES; ++i)
            {
                try
                {
                    var     xfL0 = TrTransform.T(10 * Random.onUnitSphere);
                    var     xfR0 = TrTransform.T(xfL0.translation + 30 * Random.onUnitSphere);
                    var     xfL1 = TrTransform.T(xfL0.translation + 5 * Random.onUnitSphere);
                    var     xfR1 = TrTransform.T(xfR0.translation + 5 * Random.onUnitSphere);
                    Vector3 axis = new Vector3(1, 0, 0);
                    var     obj0 = TrTransform.identity;
                    float   deltaScale;
                    var     obj1 = MathUtils.TwoPointObjectTransformationNonUniformScale(
                        axis, xfL0, xfR0, xfL1, xfR1, obj0, out deltaScale);
                    Assert.GreaterOrEqual(deltaScale, 0);

                    // This unit test is too vulnerable to instability; detect unstable
                    // regions in lieu of writing a better test
                    if (deltaScale < .01f || deltaScale > 100f)
                    {
                        continue;
                    }

                    // Invariant: local-space grip positions are the same, before and after.
                    // However, the invariant is broken when there is no solution (ie, when
                    // deltaScale = 0)
                    if (deltaScale > 1e-4f)
                    {
                        // inverse of identity is identity
                        Matrix4x4 mInvObj0 = Matrix4x4.identity;
                        Matrix4x4 mInvObj1 = Matrix4x4_InvTRS(
                            obj1.translation, obj1.rotation, new Vector3(deltaScale, 1, 1));

                        // Accuracy is quite poor :-/
                        float ABSEPS = 2e-3f;
                        float RELEPS = 5e-3f;

                        try
                        {
                            var vL0 = mInvObj0.MultiplyPoint(xfL0.translation);
                            var vL1 = mInvObj1.MultiplyPoint(xfL1.translation);
                            CheckAlmostEqual(vL1, vL0, ABSEPS, RELEPS, "left");

                            var vR0 = mInvObj0.MultiplyPoint(xfR0.translation);
                            var vR1 = mInvObj1.MultiplyPoint(xfR1.translation);
                            CheckAlmostEqual(vR1, vR0, ABSEPS, RELEPS, "right");
                        }
                        catch (NotAlmostEqual e)
                        {
                            Assert.Fail(e.Message);
                        }
                    }
                }
                catch (System.Exception)
                {
                    if (++failures > ALLOWED_FAILURES)
                    {
                        Debug.LogFormat("Failed on seed {0} iteration {1}", seed, i);
                        throw;
                    }
                }
            }
        }
Beispiel #14
0
 public void SetFixedPosition(Vector3 vPos_SS)
 {
     m_IsFixedPosition = true;
     m_Transform_SS    = TrTransform.T(vPos_SS);
 }
Beispiel #15
0
        /// Returns true on success
        /// Also updates m_TeleportForceBoundsPosition, m_LastParabolaVelocity,
        ///  m_HideValidTeleportParabola, m_TeleportPlaceIcon, m_TeleportBoundsDesired
        /// Calls SetTeleportParabola()
        /// Calls UpdateIconScale()
        bool UpdateTool_PlaceParabola()
        {
            // Given pointing Y and our scalar, determine where our parabola intersects with y == 0.
            Vector3 vel       = m_LastParabolaRay.direction * m_TeleportParabolaSpeed;
            float   yPos      = m_LastParabolaRay.origin.y;
            float   fRadicand = vel.y * vel.y - 2.0f * m_TeleportParabolaGravity * yPos;

            if (fRadicand < 0f)
            {
                return(false);
            }

            Vector3 vFeet = ViewpointScript.Head.position;

            vFeet.y = 0.0f;

            Vector3 vNewFeet = m_LastParabolaRay.origin;

            vNewFeet.y = m_TeleportBounds.position.y;

            m_LastParabolaTime     = (vel.y + Mathf.Sqrt(fRadicand)) / -m_TeleportParabolaGravity;
            m_TeleportTargetVector = new Vector3(vel.x * m_LastParabolaTime, 0, vel.z * m_LastParabolaTime);
            vNewFeet += m_TeleportTargetVector;

            // Ensure vNewFeet remains valid
            {
                // We don't have any functions for validating foot poses. In fact, the
                // room doesn't "move" since it's the root of our hierarchy. So:

                // 1. Turn foot move into room move
                TrTransform xfRoomMove = TrTransform.T(vNewFeet - vFeet);
                // 2. Turn room move into new scene pose and validate
                //    Note: assumes old room transform is identity (which it is, because
                //    the room is the root of our transform hierarchy).
                TrTransform newScene = xfRoomMove.inverse * App.Scene.Pose;
                newScene = SketchControlsScript.MakeValidSceneMove(App.Scene.Pose, newScene, BoundsRadius);
                // 3. Reverse of #2
                xfRoomMove = App.Scene.Pose * newScene.inverse;
                // 4. Reverse of #1
                vNewFeet = vFeet + xfRoomMove.translation;
            }

            // Dampen motion of vNewFeet
            // Invariant: new room center == (vNewFeet - vFeet)
            if (!m_TeleportForceBoundsPosition)
            {
                vNewFeet = Vector3.Lerp(
                    m_TeleportBoundsDesired.transform.position + vFeet,
                    vNewFeet,
                    m_TeleportParabolaDampen);
            }
            else
            {
                m_TeleportForceBoundsPosition = false;
            }

            // Curve parabola to hit final position.
            m_LastParabolaVelocity   = (vNewFeet - m_LastParabolaRay.origin) / m_LastParabolaTime;
            m_LastParabolaVelocity.y = (
                (vNewFeet.y - m_LastParabolaRay.origin.y) -
                (m_TeleportParabolaGravity * m_LastParabolaTime * m_LastParabolaTime * 0.5f))
                                       / m_LastParabolaTime;

            Vector3 vVelNoY = vel;

            vVelNoY.y = 0.0f;
            Vector3 vLastVelNoY = m_LastParabolaVelocity;

            vLastVelNoY.y = 0.0f;
            bool bReasonableAngle = Vector3.Angle(vVelNoY, vLastVelNoY) < 60.0f;

            m_HideValidTeleportParabola = !bReasonableAngle ||
                                          (m_LastParabolaVelocity.normalized.y > m_TeleportParabolaMaxY);

            SetTeleportParabola();

            // Place icon at the user's head position inside the new bounds, facing the user.
            m_TeleportPlaceIcon.position = vNewFeet;
            Vector3 vGazeDirNoY = ViewpointScript.Head.forward;

            vGazeDirNoY.y = 0.0f;
            m_TeleportPlaceIcon.forward = vGazeDirNoY.normalized;

            // Shrink the icon if it's too close to us.
            UpdateIconScale();

            // Finally, set the desired bounds.
            m_TeleportBoundsDesired.transform.position = vNewFeet - vFeet;
            return(true);
        }
Beispiel #16
0
        protected override IEnumerable <ControlPoint> DoGetPoints(TrTransform finalTransform)
        {
            double t0 = m_initialTime;
            double t1 = App.Instance.CurrentSketchTime;

            Vector3 center  = m_initialTransform.translation;
            Vector3 nRadius = finalTransform.translation - center;
            float   fRadius = nRadius.magnitude;

            nRadius /= fRadius;

            // Degenerate circle -- turn it into a line
            if (fRadius < 1e-5f)
            {
                yield return(new ControlPoint
                {
                    m_Pos = m_initialTransform.translation,
                    m_Orient = m_initialTransform.rotation,
                    m_Pressure = 1f,
                    m_TimestampMs = (uint)(t0 * kSecondsToMs)
                });

                yield return(new ControlPoint
                {
                    m_Pos = finalTransform.translation,
                    m_Orient = finalTransform.rotation,
                    m_Pressure = 1f,
                    m_TimestampMs = (uint)(t1 * kSecondsToMs)
                });

                yield break;
            }

            // Tangent must be perpendicular to nRadius

            var thisState = new ComputeTangentState
            {
                nRadius   = nRadius,
                rotation  = finalTransform.rotation,
                preferred = m_vPreferredTangent
            };
            Vector3 nTangent = ComputeTangent(thisState);

            m_vPreferredTangent = nTangent;

#if DEBUG_TANGENT
            if (m_oldState != null)
            {
                Vector3 nOldTangent = ComputeTangent(m_oldState.Value);
                if (fRadius > .5f && Vector3.Dot(nOldTangent, nTangent) < .966f)
                {
                    int nn = 20;
                    for (int i = 0; i < nn; ++i)
                    {
                        Vector3 v0 = ComputeTangent(thisState);
                        Vector3 v1 = ComputeTangent(m_oldState.Value);
                    }
                }
            }
            m_oldState = thisState;
#endif


            // Axis is perpendicular to tangent and radius
            // TODO: experiment with removing this restriction?
            Vector3 nAxis = Vector3.Cross(nTangent, nRadius).normalized;

            TrTransform xf0 = finalTransform;

            // TODO: adjust control point density

            int n = 30; // number of points; must be >= 2
            for (int i = 0; i <= n; ++i)
            {
                float t = (float)i / n;

                Quaternion  rot   = Quaternion.AngleAxis(360 * t, nAxis);
                TrTransform delta = TrTransform.R(rot).TransformBy(TrTransform.T(center));
                TrTransform xf    = delta * xf0;

                yield return(new ControlPoint
                {
                    m_Pos = xf.translation,
                    m_Orient = xf.rotation,
                    m_Pressure = 1f,
                    m_TimestampMs = (uint)(Mathf.Lerp((float)t0, (float)t1, t) * 1000)
                });
            }

#if DEBUG_TANGENT
            var start = finalTransform.translation;
            foreach (var val in DrawLine(start, nTangent, 5))
            {
                yield return(val);
            }
            foreach (var val in DrawLine(start, nAxis, 7))
            {
                yield return(val);
            }
#endif
        }