public static Rect FromLineSegment(float2 p1, float2 p2, int id)
        {
            float2 right = (p2 - p1) / 2;
            float2 up    = Lin.Cross(math.normalize(right), -1) * .001f;

            return(new Rect(p1 + right, right, up, id));
        }
Esempio n. 2
0
        // Does the same as IntersectionParameters except it only returns the u
        // parameter.
        public static float IntersectionParameter(float2 p1, float2 p1Dir, float2 p2, float2 p2Dir)
        {
            float2 d   = p2 - p1;
            float  det = Lin.Cross(p2Dir, p1Dir);

            float u = Lin.Cross(p2Dir, d) / det;

            return(u);
        }
Esempio n. 3
0
        // Gives the parameters u and v such that:
        //     p1 + u*p1Dir = p2 + v*p2Dir
        public static float2 IntersectionParameters(float2 p1, float2 p1Dir, float2 p2, float2 p2Dir)
        {
            float2 d   = p2 - p1;
            float  det = Lin.Cross(p2Dir, p1Dir);

            float u = Lin.Cross(p2Dir, d) / det;
            float v = Lin.Cross(p1Dir, d) / det;

            return(new float2(u, v));
        }
        public static float SignedAngleOf(float2 vector)
        {
            var   v1    = math.normalize(vector);
            var   v2    = new float2(1, 0);
            float angle = math.acos(math.dot(v1, v2));

            if (Lin.Cross(v2, v1) < 0)
            {
                angle = -angle;
            }
            return(angle);
        }
Esempio n. 5
0
        // The slop term is so that if two edges of the rect are close to the shadow edge, then both edges are stored in ShadowGeometry.
        public static void CalculateShadowGeometry(Rect rect, float2 lightSource, float slop, out ShadowGeometry sg1, out ShadowGeometry sg2)
        {
            if (rect.Contains(lightSource))
            {
                Debug.Log("TODO: Opaque object contains light source. Behavior will not be correct.");
            }

            FixedList64 <float2> corners = new FixedList32 <float2>();

            corners.Add(rect.c1 - lightSource);
            corners.Add(rect.c2 - lightSource);
            corners.Add(rect.c3 - lightSource);
            corners.Add(rect.c4 - lightSource);

            FixedList64 <float2> cornersN = new FixedList32 <float2>();

            foreach (float2 c in corners)
            {
                cornersN.Add(math.normalize(c));
            }

            float lowestDot = math.INFINITY;
            int   l1        = -1;
            int   l2        = -1;

            // The idea is to find the pair of corners which are the
            // furthest angle apart.
            for (int i = 0; i < 3; i++)
            {
                for (int j = i + 1; j < 4; j++)
                {
                    float dot = math.dot(cornersN[i], cornersN[j]);
                    if (dot < lowestDot)
                    {
                        lowestDot = dot;
                        l1        = i;
                        l2        = j;
                    }
                }
            }

            // Ensure they always have the same relative rotation. Improves
            // coherence of contact ids.
            if (Lin.Cross(cornersN[l1], cornersN[l2]) < 0)
            {
                var tmp = l1;
                l1 = l2;
                l2 = tmp;
            }


            // Finding the other 2 corners
            int o1 = 0;
            int o2 = 0;

            for (int i = 0; i < 4; i++)
            {
                if (i != l1 && i != l2)
                {
                    o1 = i;
                    break;
                }
            }
            for (int i = o1 + 1; i < 4; i++)
            {
                if (i != l1 && i != l2)
                {
                    o2 = i;
                    break;
                }
            }

            if (math.dot(cornersN[l1], cornersN[o1]) < math.dot(cornersN[l1], cornersN[o2]))
            {
                var tmp = o1;
                o1 = o2;
                o2 = tmp;
            }

            float reject1 = math.abs(Lin.Cross(cornersN[l1], corners[o1]));

            sg1 = new ShadowGeometry {
                contact1 = corners[l1] + lightSource,
                id1      = new float2(rect.id, l1).GetHashCode(),
            };
            if (reject1 < slop)
            {
                sg1.contact2 = corners[o1] + lightSource;
                sg1.id2      = new float2(rect.id, o1).GetHashCode();
            }

            float reject2 = math.abs(Lin.Cross(cornersN[l2], corners[o2]));

            sg2 = new ShadowGeometry {
                contact1 = corners[l2] + lightSource,
                id1      = new float2(rect.id, l2).GetHashCode()
            };
            if (reject2 < slop)
            {
                sg2.contact2 = corners[o2] + lightSource;
                sg2.id2      = new float2(rect.id, o2).GetHashCode();
            }
        }
Esempio n. 6
0
        // IMPORTANT: This function extrapolates the subtracting shape so that
        // the raycast will always hit. This function should only be run on
        // shapes that are already known to overlap angularly.
        // Casts a ray with direction (lightOrigin towards shadowOrigin) from
        // shadowOrigin for distance shadowLength, and updates shadowLength
        // with the distance it traveled before hitting toSubtract.
        public static void ShadowSubtract(float2 lightOrigin, float2 shadowDirection, float shadowStart, ref float shadowEnd, Rect toSubtract)
        {
            float2 shadowOrigin = lightOrigin + shadowDirection * shadowStart;


            // Ensure the shadowNorm points from the rect center to the shadow
            float2 shadowNorm         = Lin.Cross(shadowDirection, -1);
            float  centerToShadowProj = math.dot(lightOrigin - toSubtract.pos, shadowNorm);

            if (centerToShadowProj < 0)
            {
                centerToShadowProj *= -1;
                shadowNorm         *= -1;
            }

            //{
            //    float2 furthestVertex = toSubtract.FurthestVertexPoint(shadowNorm);
            //    float2 dir = Lin.Cross(math.normalize(furthestVertex - lightOrigin), -1);
            //    float dot = math.dot(furthestVertex - shadowOrigin, dir);
            //    if (math.abs(dot) < epsilon) {
            //        return;
            //    }
            //}


            float2 width = toSubtract.width;

            // make width point towards the shadow source
            if (math.dot(width, -shadowDirection) < 0)
            {
                width *= -1;
            }
            float widthProj = math.dot(width, shadowNorm);

            float2 height = toSubtract.height;

            // Make height point towards the shadow source
            if (math.dot(height, -shadowDirection) < 0)
            {
                height *= -1;
            }
            float heightProj = math.dot(height, shadowNorm);

            // Derived from: widthProj + heightProj*x = centerToShadowProj
            float heightMult = (centerToShadowProj - widthProj) / heightProj;

            // Derived from: heightProj + widthProj*x = centerToShadowProj
            float widthMult = (centerToShadowProj - heightProj) / widthProj;

            float2 p1 = toSubtract.pos + height + width * widthMult;
            float2 p2 = toSubtract.pos + width + height * heightMult;

            float2 intersection;

            if (heightMult <= 1 && widthMult <= 1)
            {
                if (math.dot(p1, shadowDirection) < math.dot(p2, shadowDirection))
                {
                    intersection = p1;
                }
                else
                {
                    intersection = p2;
                }
            }
            else if (widthMult <= 1)
            {
                intersection = p1;
            }
            else if (heightMult <= 1)
            {
                intersection = p2;
            }
            else
            {
                // This part theoretically shouldn't happen, but floating point
                // imprecision might make this possible.
                intersection = (p1 + p2) / 2;
            }

            shadowEnd = math.min(shadowEnd, math.dot(intersection - lightOrigin, shadowDirection));
        }
        // ccwVec and cwVec are the directions (not normalized) of the surface
        // counterclockwise from and clockwise from the closest point on the
        // rectangle. For instance, if the closest point is a corner, these
        // vectors will be perpendicular. Otherwise they will be parallel and
        // opposite.
        public float2 ClosestPoint(float2 point, out float2 ccwVec, out float2 cwVec)
        {
            point -= this.pos;

            float  widthMag  = math.length(this.width);
            float2 widthNorm = this.width / widthMag;
            float2 width     = this.width;
            float  wDist     = math.dot(widthNorm, point);

            if (wDist < 0)
            {
                widthNorm *= -1;
                width     *= -1;
                wDist     *= -1;
            }

            float  heightMag  = math.length(this.height);
            float2 heightNorm = this.height / heightMag;
            float2 height     = this.height;
            float  hDist      = math.dot(heightNorm, point);

            if (hDist < 0)
            {
                heightNorm *= -1;
                height     *= -1;
                hDist      *= -1;
            }

            bool withinWidth  = wDist < widthMag;
            bool withinHeight = hDist < heightMag;

            if (withinWidth && withinHeight)
            {
                if (widthMag - wDist < heightMag - hDist)
                {
                    ccwVec = Lin.Cross(width, -1);
                    cwVec  = -ccwVec;
                    return(this.pos + width + hDist * heightNorm);
                }
                else
                {
                    ccwVec = Lin.Cross(height, -1);
                    cwVec  = -ccwVec;
                    return(this.pos + height + wDist * widthNorm);
                }
            }
            else if (withinWidth)
            {
                ccwVec = Lin.Cross(height, -1);
                cwVec  = -ccwVec;
                return(this.pos + height + wDist * widthNorm);
            }
            else if (withinHeight)
            {
                ccwVec = Lin.Cross(width, -1);
                cwVec  = -ccwVec;
                return(this.pos + width + hDist * heightNorm);
            }
            else
            {
                if (Lin.Cross(width, height) > 0)
                {
                    ccwVec = -width;
                    cwVec  = -height;
                }
                else
                {
                    ccwVec = -height;
                    cwVec  = -width;
                }
                return(this.pos + width + height);
            }
        }