예제 #1
0
        public void SetSmallestExtension(float t)
        {
            TelescopeShell shell = (Reversed) ? shells[1] : LastShell;

            shell.extensionRatio = t;
            shell.SetTransform();
        }
예제 #2
0
        public void SetShellExtensions(float t)
        {
            float tRemaining = t;
            float tStep      = 1f / (shells.Count - 1);

            shells[0].extensionRatio = Mathf.Clamp01(t);

            for (int i = 1; i < shells.Count; i++)
            {
                TelescopeShell ts = shells[i];
                if (tRemaining > tStep)
                {
                    ts.extensionRatio = 1;
                    tRemaining       -= tStep;
                }
                else if (tRemaining > 0)
                {
                    float tNormalized = tRemaining / tStep;
                    ts.extensionRatio = tNormalized;
                    tRemaining        = 0;
                }
                else
                {
                    ts.extensionRatio = 0;
                }
            }

            if (LastShell.extensionRatio < MinExt)
            {
                LastShell.extensionRatio = MinExt;
                LastShell.SetTransform();
            }
        }
예제 #3
0
        public void PrependShell()
        {
            Vector3    loc = shells[0].getLocalLocationAlongPath(-1);
            Quaternion rot = shells[0].getLocalRotationAlongPath(-1);

            TelescopeParameters p = new TelescopeParameters(shells[0].getParameters());

            p.radius += p.thickness;

            // Create the new shell object
            GameObject     newShell = new GameObject();
            TelescopeShell shell    = newShell.AddComponent <TelescopeShell>();

            shell.GenerateGeometry(p, null);
            shell.setMaterial(material);

            Vector3    shellLoc = shells[0].transform.position;
            Quaternion shellRot = shells[0].transform.rotation;

            // Move the new shell to be in position to receive the old one
            shell.transform.parent   = shells[0].transform.parent;
            shell.transform.position = shellLoc + shellRot * loc;
            shell.transform.rotation = shellRot * rot;

            // Reparent the old starting shell so it becomes the child of the new one
            shells[0].baseTranslation  = Vector3.zero;
            shells[0].baseRotation     = Quaternion.identity;
            shells[0].transform.parent = shell.transform;
            shell.extensionRatio       = shells[0].extensionRatio;
            shells.Insert(0, shell);
        }
예제 #4
0
        public TelescopeShell addChildShell(TelescopeShell parent,
                                            TelescopeParameters parentParams, TelescopeParameters childParams,
                                            TelescopeParameters nextParams, bool isNotLast)
        {
            int i = shells.Count;
            // Make the new shell, and set the previous shell as its parent
            GameObject shellObj = new GameObject();

            shellObj.transform.parent = parent.transform;
            shellObj.name             = name + "-shell" + i;

            // Make the geometry, etc.
            TelescopeShell newShell = shellObj.AddComponent <TelescopeShell>();

            newShell.GenerateGeometry(childParams, nextParams, doOverhang: true);
            newShell.setMaterial(material);

            // Set the shell's rest transformation relative to its parent.
            // When the shell's current extension ratio is 0, this is where
            // it is located relative to its parent.
            // newShell.baseRadians = newShell.radiansOfLength(wallThickness);
            newShell.baseTranslation = TelescopeUtils.childBasePosition(parentParams, childParams);
            newShell.baseRotation    = TelescopeUtils.childBaseRotation(parentParams, childParams);
            shells.Add(newShell);

            shellObj.layer = 8;

            newShell.containingSegment = this;

            return(newShell);
        }
 public void Deselect()
 {
     if (selected)
     {
         selected.setMaterial(defaultTelescopeMaterial);
     }
     selected = null;
 }
 public void ShrinkToFit()
 {
     if (selected)
     {
         TelescopeSegment           segment   = selected.containingSegment;
         List <TelescopeParameters> allParams = segment.getParamList();
         TelescopeUtils.growChainToFit(allParams, shrinkFit: true);
         segment.MakeAllShells(allParams);
         segment.SetShellExtensions(1);
         selected = null;
     }
 }
예제 #7
0
 Vector3 contactDirection(TelescopeShell shell, bool reversed)
 {
     if (!reversed)
     {
         return(shell.transform.forward);
     }
     else
     {
         Vector3 dirLocal = shell.getLocalRotationAlongPath(1) * Vector3.forward;
         Vector3 dir      = shell.transform.rotation * dirLocal;
         dir *= -1;
         return(dir);
     }
 }
예제 #8
0
        public void SetTransform()
        {
            float extendT = Mathf.Clamp01(extensionRatio * 2);

            if (AlwaysExtend)
            {
                extendT = 1;
            }
            float twistT = Mathf.Clamp01(extensionRatio * 2 - 1);

            if (!Reversed)
            {
                Vector3    localTranslation = getLocalLocationAlongPath(extendT);
                Quaternion localRotation    = getLocalRotationAlongPath(extendT);

                // Add twist angle.
                Vector3    forwardAxis = localRotation * baseRotation * Vector3.forward;
                Quaternion roll        = Quaternion.AngleAxis(-twistAngle * twistT, forwardAxis);
                localRotation = roll * localRotation;

                // Set the shell's local translation from parent based on how extended it is.
                transform.localPosition = baseRotation * localTranslation + baseTranslation;
                transform.localRotation = baseRotation * localRotation;
            }

            else
            {
                TelescopeShell parent = transform.parent.GetComponent <TelescopeShell>();
                if (!parent)
                {
                    return;
                }

                Vector3    localTranslation = parent.getInvLocationAlongPath(extendT);
                Quaternion localRotation    = parent.getLocalRotationAlongPath(extendT);

                Vector3    forwardPosition = Quaternion.Inverse(baseRotation) * localTranslation - parent.baseTranslation;
                Quaternion forwardRotation = Quaternion.Inverse(parent.baseRotation) * Quaternion.Inverse(localRotation);

                // Set the shell's local translation from parent based on how extended it is.
                transform.localRotation = forwardRotation;
                transform.localPosition = forwardPosition;

                // Now we need to rotate by the twist angle, with the pivot being the parent's angle.
                Vector3 parentWorld = parent.transform.position;
                Vector3 axis        = parent.transform.forward;
                transform.RotateAround(parentWorld, axis, parent.twistAngle * twistT);
            }
        }
        void Rescale(float newRadius)
        {
            float radiusDiff = newRadius - Radius;

            // Offset us from the parent segment so that we are in contact again.
            if (parentSegment)
            {
                Vector3 normal = parentSegment.WorldEndTangent();
                Vector3 offset = -radiusDiff * normal;

                transform.position = transform.position + offset;
            }

            // Offset child segments from this bulb so that they are in contact.
            foreach (TelescopeSegment seg in childSegments)
            {
                float childRadius = seg.shells[0].radius;
                float childOffset = Mathf.Sqrt(newRadius * newRadius - childRadius * childRadius);

                if (seg.Reversed)
                {
                    TelescopeShell shell       = seg.shells[0];
                    Vector3        shellOffset = shell.transform.rotation * shell.getLocalLocationAlongPath(1);

                    Vector3 localPos        = seg.LocalContactTangent() * childOffset;
                    Vector3 desiredShellEnd = transform.rotation * localPos + transform.position;

                    Vector3 shellBaseWorld  = shell.transform.position;
                    Vector3 currentShellEnd = shellBaseWorld + shellOffset;

                    Vector3 worldOffset = desiredShellEnd - currentShellEnd;

                    seg.transform.position += worldOffset;// + shellOffset;
                }

                else
                {
                    seg.transform.localPosition = seg.LocalContactTangent() * childOffset;
                }
            }

            // Rescale the bulb itself
            Radius = newRadius;
        }
예제 #10
0
        public Vector3 LocalContactTangent()
        {
            TelescopeShell firstShell = shells[0];

            if (!Reversed)
            {
                Quaternion local = firstShell.transform.localRotation;
                return(local * Vector3.forward);
            }
            else
            {
                Quaternion baseQ = firstShell.transform.localRotation;
                Quaternion pathQ = firstShell.getLocalRotationAlongPath(1);

                Quaternion local = baseQ * pathQ;

                return(-(local * Vector3.forward));
            }
        }
예제 #11
0
        public bool CollisionIteration(float delta)
        {
            bool hasCollision = false;

            if (parentSegment)
            {
                foreach (TelescopeSegment seg in childSegments)
                {
                    TelescopeShell parentShell = parentSegment.LastShell;
                    TelescopeShell childShell  = seg.FirstShell;
                    float          moveDistance;
                    Vector3        moveDirection;

                    if (parentShell.CollidesWith(childShell, out moveDistance, out moveDirection))
                    {
                        hasCollision = true;
                        childShell.transform.position  += 0.5f * moveDistance * delta * moveDirection;
                        parentShell.transform.position -= 0.5f * moveDistance * delta * moveDirection;
                    }
                }
            }

            for (int i = 0; i < childSegments.Count; i++)
            {
                for (int j = i + 1; j < childSegments.Count; j++)
                {
                    TelescopeShell shell1 = childSegments[i].FirstShell;
                    TelescopeShell shell2 = childSegments[j].FirstShell;
                    float          moveDistance;
                    Vector3        moveDirection;

                    if (shell1.CollidesWith(shell2, out moveDistance, out moveDirection))
                    {
                        hasCollision = true;
                        shell2.transform.position += 0.5f * moveDistance * delta * moveDirection;
                        shell1.transform.position -= 0.5f * moveDistance * delta * moveDirection;
                    }
                }
            }

            return(!hasCollision);
        }
예제 #12
0
        /*
         * public Vector3 WorldEndPosition()
         * {
         *  TelescopingShell lastShell = shells[shells.Count - 1];
         * }*/

        public Vector3 WorldEndTangent()
        {
            TelescopeShell lastShell = shells[shells.Count - 1];

            if (!Reversed)
            {
                Quaternion baseQ    = lastShell.transform.rotation;
                Quaternion local    = lastShell.getLocalRotationAlongPath(1);
                Quaternion combined = baseQ * local;

                Vector3 worldForward = combined * Vector3.forward;
                return(-worldForward);
            }

            else
            {
                Quaternion local = lastShell.transform.rotation;
                return(local * Vector3.forward);
            }
        }
        public void SelectShell(TelescopeShell selection)
        {
            if (!selection)
            {
                return;
            }
            if (selected)
            {
                selected.setMaterial(defaultTelescopeMaterial);
            }
            selected = selection;
            selected.setMaterial(selectedTelescopeMaterial);

            TelescopeParameters tp = selected.getParameters();

            lengthField.text    = tp.length.ToString();
            radiusField.text    = tp.radius.ToString();
            curvatureField.text = tp.curvature.ToString();
            twistField.text     = tp.twistFromParent.ToString();
        }
예제 #14
0
        public bool CollidesWith(TelescopeShell otherShell, out float moveDistance, out Vector3 contactNormal)
        {
            float tStep     = 1f / (Constants.CUTS_PER_CYLINDER - 1);
            float angleStep = 2f * Mathf.PI / Constants.VERTS_PER_CIRCLE;

            float minDistance = 1001f;

            contactNormal = Vector3.zero;

            int thisShellCuts = Constants.CUTS_PER_CYLINDER;

            if (HasOverhang)
            {
                thisShellCuts += Constants.OVERHANG_CUTS;
            }

            int otherShellCuts = Constants.CUTS_PER_CYLINDER;

            if (otherShell.HasOverhang)
            {
                otherShellCuts += Constants.OVERHANG_CUTS;
            }

            // Compare every ring on this shell against the other shell
            for (int ring = 0; ring < thisShellCuts; ring++)
            {
                // Compute the normal of the plane that is orthogonal to the shell here
                Vector3 ringNormal = CenterTangentWS(ring * tStep);
                Vector3 ringCenter = CenterPointWS(ring * tStep);

                // Need to loop over all vertices of the other shell.
                for (int cut = 0; cut < otherShellCuts; cut++)
                {
                    for (int vert = -1; vert < Constants.VERTS_PER_CIRCLE; vert++)
                    {
                        Vector3 otherVert;
                        // Use -1 as a hack to check the center point too
                        if (vert == -1)
                        {
                            otherVert = otherShell.CenterPointWS(cut * tStep);
                        }
                        else
                        {
                            // Get the vertex on the surface of the other shell
                            float otherT     = cut * tStep;
                            float otherAngle = angleStep * vert;
                            otherVert = otherShell.SurfacePointWS(otherT, otherAngle);
                        }

                        // See if it is near the normal plane to the ring of the current shell
                        Vector3 vecToOther    = otherVert - ringCenter;
                        float   orthoDistance = Vector3.Dot(vecToOther, ringNormal);
                        // If it is, compute the projected distance along the normal plane.
                        // If the distance is close to being on this ring's plane,
                        // compare it against the old min
                        if (Mathf.Abs(orthoDistance) < tStep * length * 2)
                        {
                            Vector3 planeVec  = vecToOther - orthoDistance * ringNormal;
                            float   planeDist = planeVec.magnitude;
                            if (planeDist < minDistance)
                            {
                                minDistance = planeDist;
                                Vector3 otherRingCenter = otherShell.CenterPointWS(cut * tStep);
                                contactNormal = (otherRingCenter - ringCenter).normalized;
                            }
                        }
                    }
                }
            }

            // We have now computed the minimum distance from any vertex on the
            // other shell to any vertex on the center line of this shell.
            // If any such distance is closer than this shell's radius, then
            // the shells are colliding.
            moveDistance = (radius + Constants.COLLISION_GAP - minDistance) + 0.01f;
            return(minDistance < radius + Constants.COLLISION_GAP);
        }
        // Update is called once per frame
        void Update()
        {
            if (Input.GetButtonDown("ToggleMode"))
            {
                if (currentMode == DesignerMode.Shell)
                {
                    currentMode   = DesignerMode.Curve;
                    modeText.text = "Curve mode";
                }
                else if (currentMode == DesignerMode.Curve)
                {
                    currentMode   = DesignerMode.Curvature;
                    modeText.text = "Curvature mode";
                }
                else if (currentMode == DesignerMode.Curvature)
                {
                    currentMode   = DesignerMode.Shell;
                    modeText.text = "Shell mode";
                }
            }

            RaycastHit hitInfo = new RaycastHit();

            if (Input.GetMouseButtonDown(0) && RaycastShells(Input.mousePosition, out hitInfo))
            {
                TelescopeShell selection   = hitInfo.collider.GetComponent <TelescopeShell>();
                DraggablePoint draggablePt = hitInfo.collider.GetComponent <DraggablePoint>();

                if (selection)
                {
                    SelectShell(selection);
                }

                else if (draggablePt)
                {
                    draggable     = draggablePt;
                    selectedDepth = Camera.main.WorldToScreenPoint(draggablePt.transform.position).z;
                }
            }

            else if (Input.GetKeyDown("r") && RaycastShells(Input.mousePosition, out hitInfo))
            {
                DraggablePoint draggablePt = hitInfo.collider.GetComponent <DraggablePoint>();

                if (draggablePt && draggablePt.parentSpline)
                {
                    Debug.Log("Reverse " + draggablePt.parentSpline.name);
                    draggablePt.parentSpline.Reverse();
                }
            }


            else if (Input.GetKeyDown("j") && RaycastShells(Input.mousePosition, out hitInfo))
            {
                DraggablePoint draggablePt = hitInfo.collider.GetComponent <DraggablePoint>();

                if (draggablePt)
                {
                    if (draggablePt.Type == PointType.Bulb)
                    {
                        draggablePt.SwitchBulbType(PointType.EmptyJuncture);
                    }
                    else if (draggablePt.Type == PointType.EmptyJuncture)
                    {
                        draggablePt.SwitchBulbType(PointType.Bulb);
                    }
                }
            }

            else if (Input.GetMouseButton(0) && draggable)
            {
                Vector3 clickPos = Input.mousePosition;
                clickPos.z = selectedDepth;
                Vector3 worldPos = Camera.main.ScreenToWorldPoint(clickPos);

                DraggablePoint intersectedBulb = splineCanvas.IntersectedBulb(worldPos);

                if (draggable.Type == PointType.Spline && draggable.IsEndPoint() && intersectedBulb)
                {
                    draggable.AttachToBulb(intersectedBulb);
                }
                else
                {
                    draggable.Move(worldPos);
                }
            }

            else if (Input.GetMouseButtonDown(1) && RaycastShells(Input.mousePosition, out hitInfo))
            {
                Vector3        clickPos = Input.mousePosition;
                DraggablePoint clicked  = hitInfo.collider.GetComponent <DraggablePoint>();

                if (clicked)
                {
                    if (Input.GetKey("left ctrl"))
                    {
                        clicked.Delete();
                    }
                    else
                    {
                        clicked.Duplicate();
                    }
                }

                // Store the eye-space position of the click.
                // Use eye space because we always want moving up/down to correspond
                // to bigger or smaller, regardless of camera orientation.
                lastMousePos   = clickPos;
                lastMousePos.z = selectedDepth;
                lastMousePos   = Camera.main.ScreenToViewportPoint(lastMousePos);
            }

            else if (Input.GetMouseButtonDown(2) && RaycastShells(Input.mousePosition, out hitInfo))
            {
                Vector3 clickPos = Input.mousePosition;

                // Store the eye-space position of the click.
                // Use eye space because we always want moving up/down to correspond
                // to bigger or smaller, regardless of camera orientation.
                lastMousePos   = clickPos;
                lastMousePos.z = selectedDepth;
                lastMousePos   = Camera.main.ScreenToViewportPoint(lastMousePos);
            }

            else if (Input.mouseScrollDelta.y != 0 && RaycastShells(Input.mousePosition, out hitInfo))
            {
                DraggablePoint draggablePt = hitInfo.collider.GetComponent <DraggablePoint>();
                if (draggablePt)
                {
                    float change = Input.mouseScrollDelta.y * 0.05f;
                    draggablePt.Resize(change);
                }
            }

            else if (Input.GetKeyDown("delete") && RaycastShells(Input.mousePosition, out hitInfo))
            {
                DraggablePoint draggablePt = hitInfo.collider.GetComponent <DraggablePoint>();

                if (draggablePt)
                {
                    draggablePt.Delete();
                }
            }

            else if (Input.GetKeyDown("b") && RaycastShells(Input.mousePosition, out hitInfo))
            {
                DraggablePoint draggablePt = hitInfo.collider.GetComponent <DraggablePoint>();

                if (draggablePt)
                {
                    draggablePt.ReplaceWithBulb();
                }
            }

            else if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(2))
            {
                draggable = null;
            }

            else if (Input.GetButtonDown("Cancel"))
            {
                Deselect();
            }

            else if (Input.GetButtonDown("Submit"))
            {
                Debug.Log("submit");
            }

            shootTime += Time.deltaTime;
            if (shootTime > shootDelay && Input.GetKey("z"))
            {
                ShootSphere();
            }
        }
예제 #16
0
        // Write a set of STLs for a juncture, and an OpenSCAD file that
        // compiles them all into one fused part.
        public string WriteSTLOfJuncture(Vector3 minOffset, string objName)
        {
            List <string> unionSTLs = new List <string>();
            List <string> diffSTLs  = new List <string>();
            string        bulbSTL   = "";

            // If there is a parent segment, write that
            if (parentSegment)
            {
                TelescopeShell last = parentSegment.LastShell;

                // Export them in world coordinates so that the full
                // object can just be assembled without moving pieces around
                STLWriter.VectorTransform f =
                    (v => last.transform.rotation * v + last.transform.position - minOffset);

                if (parentSegment.NumShells > 1 && parentSegment.Reversed)
                {
                    Mesh inner = last.GenerateInnerVolume(parentSegment.shells[parentSegment.NumShells - 2].getParameters(),
                                                          -Constants.WALL_THICKNESS, extraRings: Constants.CUTS_PER_CYLINDER / 2);

                    string parentVolumeSTL = objName + "-parentInner.stl";
                    STLWriter.WriteSTLOfMesh(inner, "scad/" + parentVolumeSTL, f);

                    diffSTLs.Add(parentVolumeSTL);
                }
            }

            // Write each child segment as well
            int childNum = 0;

            foreach (TelescopeSegment childSegment in childSegments)
            {
                TelescopeShell first = childSegment.FirstShell;

                STLWriter.VectorTransform f =
                    (v => first.transform.rotation * v + first.transform.position - minOffset);

                // Write a mesh for the inner volume contained in the shell,
                // so that it can be subtracted out from the juncture
                if (childSegment.NumShells > 1 && !childSegment.Reversed)
                {
                    Mesh inner = first.GenerateInnerVolume(childSegment.shells[1].getParameters(),
                                                           -Constants.WALL_THICKNESS, extraRings: Constants.CUTS_PER_CYLINDER / 2);

                    string childVolumeSTL = objName + "-childInner" + childNum + ".stl";
                    STLWriter.WriteSTLOfMesh(inner, "scad/" + childVolumeSTL, f);

                    diffSTLs.Add(childVolumeSTL);
                }
                childNum++;
            }

            // Write this piece
            if (junctureType != JunctureType.None)
            {
                STLWriter.VectorTransform f =
                    (v => transform.rotation * v + transform.position - minOffset);

                bulbSTL = objName + ".stl";
                STLWriter.WriteSTLOfMesh(mFilter.mesh, "scad/" + bulbSTL, f);
            }

            string scadFile = objName + ".scad";

            STLWriter.WriteSCADOfJunctureSTLs(bulbSTL, unionSTLs, diffSTLs, "scad/" + scadFile);
            return(scadFile);
        }
예제 #17
0
        public void MakeAllShells(List <TelescopeParameters> paramList, bool reversed = false)
        {
            DeleteTelescope();

            // Create an object for the first shell
            GameObject rootShellObj = new GameObject();

            rootShellObj.name                    = name + "-shell0";
            rootShellObj.transform.parent        = this.transform;
            rootShellObj.transform.localPosition = Vector3.zero;

            telescopeRootShell = rootShellObj;

            // Make the shell geometry
            TelescopeShell shell = rootShellObj.AddComponent <TelescopeShell>();
            // Get the twist impulse of the shell after, since we need to cut the
            // grooves in this shell to enable it
            TelescopeParameters params1 = (paramList.Count > 1) ? paramList[1] : null;

            shell.GenerateGeometry(paramList[0], params1, outerGroove: false);
            shell.setMaterial(material);

            // Shells don't know anything about their position/rotation,
            // so we set that here.
            Quaternion initialFacing = Quaternion.LookRotation(initialDirection, initialUp);

            rootShellObj.transform.rotation = initialFacing;
            rootShellObj.layer = 8;

            shell.containingSegment = this;

            // shell.baseRadians = 0;
            shell.baseTranslation = Vector3.zero;
            shell.baseRotation    = Quaternion.identity;

            shells.Add(shell);
            shell.isRoot = true;

            // Make all of the child shells here.
            TelescopeShell      prevShell      = shell;
            TelescopeParameters previousParams = paramList[0];
            TelescopeParameters currentParams  = paramList[0];

            float accumulatedTaper = shell.getTaperLoss();

            for (int i = 1; i < paramList.Count; i++)
            {
                // Get the computed parameters for this and the previous shell.
                currentParams  = paramList[i];
                previousParams = paramList[i - 1];

                // Shrink the radius by the accumulated taper so far.
                currentParams.radius -= accumulatedTaper;

                // Get the twist impulse for the next shell, so that we can cut the grooves.
                TelescopeParameters nextParams = (i < paramList.Count - 1) ? paramList[i + 1] : null;

                TelescopeParameters taperedParams = (nextParams != null) ? new TelescopeParameters(nextParams) : null;
                if (taperedParams != null)
                {
                    taperedParams.radius -= accumulatedTaper;
                }

                // Add it.
                bool isNotLast = (i < paramList.Count - 1);
                prevShell         = addChildShell(prevShell, previousParams, currentParams, taperedParams, isNotLast);
                accumulatedTaper += prevShell.getTaperLoss();
            }


            if (fountainPrefab)
            {
                GameObject fountain = Instantiate <GameObject>(fountainPrefab);
                fountain.transform.parent = prevShell.transform;
            }
        }