コード例 #1
0
        private void RenderLine(Vector3 start, Vector3 end, Plane plane, Color color, ICamera camera, I2DRenderer im)
        {
            var line = new Line(start, end);
            var cls  = line.ClassifyAgainstPlane(plane);

            if (cls == PlaneClassification.Back)
            {
                return;
            }
            if (cls == PlaneClassification.Spanning)
            {
                var isect = plane.GetIntersectionPoint(line, true);
                var first = plane.OnPlane(line.Start) > 0 ? line.Start : line.End;
                if (!isect.HasValue)
                {
                    return;
                }
                line = new Line(first, isect.Value);
            }

            var st = camera.WorldToScreen(line.Start);
            var en = camera.WorldToScreen(line.End);

            im.AddLine(st.ToVector2(), en.ToVector2(), color, 2);
        }
コード例 #2
0
        public static void AlignWithTexture(this Texture tex, Plane currentPlane, Plane alignToPlane, Texture alignToTexture)
        {
            // Get reference values for the axes
            var refU = alignToTexture.UAxis;
            var refV = alignToTexture.VAxis;
            // Reference points in the texture plane to use for shifting later on
            var refX = alignToTexture.UAxis * alignToTexture.XShift * alignToTexture.XScale;
            var refY = alignToTexture.VAxis * alignToTexture.YShift * alignToTexture.YScale;

            // Two non-parallel planes intersect at an edge. We want the textures on this face
            // to line up with the textures on the provided face. To do this, we rotate the texture
            // normal on the provided face around the intersection edge to get the new texture axes.
            // Then we rotate the texture reference point around this edge as well to get the new shift values.
            // The scale values on both faces will always end up being the same value.

            // Find the intersection edge vector
            var intersectionEdge = alignToPlane.Normal.Cross(currentPlane.Normal);

            // If the planes are parallel, the texture doesn't need any rotation - just different shift values.
            if (Math.Abs(intersectionEdge.Length()) > 0.01f)
            {
                // Create a plane using the intersection edge as the normal
                var intersectionPlane = new Plane(intersectionEdge, 0);

                var intersect = Plane.Intersect(alignToPlane, currentPlane, intersectionPlane);
                if (intersect != null)
                {
                    // Since the intersection plane is perpendicular to both face planes, we can find the angle
                    // between the two planes (the align plane and the plane of this face) by projecting
                    // the normals of the planes onto the perpendicular plane and taking the cross product.

                    // Project the two normals onto the perpendicular plane
                    var apNormal = intersectionPlane.Project(alignToPlane.Normal).Normalise();
                    var cpNormal = intersectionPlane.Project(currentPlane.Normal).Normalise();

                    // Get the angle between the projected normals
                    var dot   = Math.Round(apNormal.Dot(cpNormal), 4);
                    var angle = (float)Math.Acos(dot);  // A.B = cos(angle)

                    // Rotate the texture axis by the angle around the intersection edge
                    var transform = Matrix4x4.CreateFromAxisAngle(intersectionEdge.Normalise(), angle);
                    refU = transform.Transform(refU);
                    refV = transform.Transform(refV);

                    // Rotate the texture reference points as well, but around the intersection line, not the origin
                    refX = transform.Transform(refX + intersect.Value) - intersect.Value;
                    refY = transform.Transform(refY + intersect.Value) - intersect.Value;
                }
            }

            // Convert the reference points back to get the final values
            tex.Rotation = 0;
            tex.UAxis    = refU;
            tex.VAxis    = refV;
            tex.XShift   = refU.Dot(refX) / alignToTexture.XScale;
            tex.YShift   = refV.Dot(refY) / alignToTexture.YScale;
            tex.XScale   = alignToTexture.XScale;
            tex.YScale   = alignToTexture.YScale;
        }
コード例 #3
0
        /// <summary>
        /// Same as Plane.GetClosestAxisToNormal(), but prioritises the axes Z, X, Y.
        /// </summary>
        /// <param name="plane">Input plane</param>
        /// <returns>Vector3.UnitX, Vector3.UnitY, or Vector3.UnitZ depending on the plane's normal</returns>
        private static Vector3 QuakeEdClosestAxisToNormal(Plane plane)
        {
            var norm = plane.Normal.Absolute();

            if (norm.Z >= norm.X && norm.Z >= norm.Y)
            {
                return(Vector3.UnitZ);
            }
            if (norm.X >= norm.Y)
            {
                return(Vector3.UnitX);
            }
            return(Vector3.UnitY);
        }
コード例 #4
0
        private void AddLine(CircleType type, Vector3 start, Vector3 end, Plane test, CachedLines cache)
        {
            var line = new Line(start, end);
            var cls  = line.ClassifyAgainstPlane(test);

            if (cls == PlaneClassification.Back)
            {
                return;
            }
            if (cls == PlaneClassification.Spanning)
            {
                var isect = test.GetIntersectionPoint(line, true);
                var first = test.OnPlane(line.Start) > 0 ? line.Start : line.End;
                if (isect.HasValue)
                {
                    line = new Line(first, isect.Value);
                }
            }
            cache.Cache[type].Add(new Line(cache.Viewport.Camera.WorldToScreen(line.Start), cache.Viewport.Camera.WorldToScreen(line.End)));
        }
コード例 #5
0
        private void PerformClip(MapDocument document)
        {
            var objects = document.Selection.OfType <Solid>().ToList();

            if (!objects.Any())
            {
                return;
            }

            var plane = new Plane(_clipPlanePoint1.Value, _clipPlanePoint2.Value, _clipPlanePoint3.Value);
            var clip  = new Transaction();
            var found = false;

            foreach (var solid in objects)
            {
                solid.Split(document.Map.NumberGenerator, plane, out var backSolid, out var frontSolid);
                found = true;

                // Remove the clipped solid
                clip.Add(new Detatch(solid.Hierarchy.Parent.ID, solid));

                if (_side != ClipSide.Back && frontSolid != null)
                {
                    // Add front solid
                    clip.Add(new Attach(solid.Hierarchy.Parent.ID, frontSolid));
                }

                if (_side != ClipSide.Front && backSolid != null)
                {
                    // Add back solid
                    clip.Add(new Attach(solid.Hierarchy.Parent.ID, backSolid));
                }
            }
            if (found)
            {
                MapDocumentOperation.Perform(document, clip);
            }
        }
コード例 #6
0
        internal static Task ConvertBox(BufferBuilder builder, IMapObject obj, Box box)
        {
            // It's always a box, these numbers are known
            const uint numVertices = 4 * 6;

            // Pack the indices like this [ solid1 ... solidn ] [ wireframe1 ... wireframe n ]
            const uint numSolidIndices     = 36;
            const uint numWireframeIndices = numVertices * 2;

            var points  = new VertexStandard[numVertices];
            var indices = new uint[numSolidIndices + numWireframeIndices];

            var c      = obj.IsSelected ? Color.Red : obj.Data.GetOne <ObjectColor>()?.Color ?? Color.Magenta;
            var colour = new Vector4(c.R, c.G, c.B, c.A) / 255f;

            var flags = obj.IsSelected ? VertexFlags.SelectiveTransformed : VertexFlags.None;

            var vi = 0u;
            var si = 0u;
            var wi = numSolidIndices;

            foreach (var face in box.GetBoxFaces())
            {
                var offs = vi;

                var normal = new Plane(face[0], face[1], face[2]).Normal;
                foreach (var v in face)
                {
                    points[vi++] = new VertexStandard
                    {
                        Position = v,
                        Colour   = colour,
                        Normal   = normal,
                        Texture  = Vector2.Zero,
                        Tint     = Vector4.One,
                        Flags    = flags | VertexFlags.FlatColour
                    };
                }

                // Triangles - [0 1 2]  ... [0 n-1 n]
                for (uint i = 2; i < 4; i++)
                {
                    indices[si++] = offs;
                    indices[si++] = offs + i - 1;
                    indices[si++] = offs + i;
                }

                // Lines - [0 1] ... [n-1 n] [n 0]
                for (uint i = 0; i < 4; i++)
                {
                    indices[wi++] = offs + i;
                    indices[wi++] = offs + (i == 4 - 1 ? 0 : i + 1);
                }
            }

            var origin = obj.Data.GetOne <Origin>()?.Location ?? box.Center;

            var groups = new List <BufferGroup>();

            if (!obj.Data.OfType <IContentsReplaced>().Any(x => x.ContentsReplaced))
            {
                groups.Add(new BufferGroup(PipelineType.TexturedOpaque, CameraType.Perspective, 0, numSolidIndices));
            }

            groups.Add(new BufferGroup(PipelineType.Wireframe, obj.IsSelected ? CameraType.Both : CameraType.Orthographic, numSolidIndices, numWireframeIndices));

            builder.Append(points, indices, groups);

            // Also push the untransformed wireframe when selected
            if (obj.IsSelected)
            {
                for (var i = 0; i < points.Length; i++)
                {
                    points[i].Flags = VertexFlags.None;
                }
                var untransformedIndices = indices.Skip((int)numSolidIndices);
                builder.Append(points, untransformedIndices, new[]
                {
                    new BufferGroup(PipelineType.Wireframe, CameraType.Both, 0, numWireframeIndices)
                });
            }

            return(Task.FromResult(0));
        }
コード例 #7
0
        private static IEnumerable <Face> CalculateDecalGeometry(Entity entity, TextureItem decal, MapDocument document, ICollection <long> solidIds)
        {
            if (decal == null || entity.Hierarchy.Parent == null)
            {
                yield break;                                                   // Texture not found
            }
            var boxRadius = Vector3.One * 4;

            // Decals apply to all faces that intersect within an 8x8x8 bounding box
            // centered at the origin of the decal
            var box = new Box(entity.Origin - boxRadius, entity.Origin + boxRadius);

            // Get the faces that intersect with the decal's radius
            var lines = box.GetBoxLines().ToList();
            var faces = GetBoxIntersections(document, box)
                        .OfType <Solid>()
                        .SelectMany(x => x.Faces.Select(f => new { Solid = x, Face = f }))
                        .Where(x =>
            {
                var p = new Polygon(x.Face.Vertices);
                return(lines.Any(l => p.GetIntersectionPoint(l, true) != null));
            });

            foreach (var sf in faces)
            {
                var solid = sf.Solid;
                var face  = sf.Face;
                solidIds.Add(solid.ID);

                // Project the decal onto the face
                var center  = face.Plane.Project(entity.Origin);
                var texture = face.Texture.Clone();
                texture.Name   = decal.Name;
                texture.XShift = -decal.Width / 2f;
                texture.YShift = -decal.Height / 2f;
                var decalFace = new Face(0)
                {
                    Plane   = face.Plane,
                    Texture = texture
                };
                // Re-project the vertices in case the texture axes are not on the face plane
                var xShift = face.Texture.UAxis * face.Texture.XScale * decal.Width / 2;
                var yShift = face.Texture.VAxis * face.Texture.YScale * decal.Height / 2;
                var verts  = new[]
                {
                    face.Plane.Project(center + xShift - yShift), // Bottom Right
                    face.Plane.Project(center + xShift + yShift), // Top Right
                    face.Plane.Project(center - xShift + yShift), // Top Left
                    face.Plane.Project(center - xShift - yShift)  // Bottom Left
                };

                // Because the texture axes don't have to align to the face, we might have a reversed face here
                // If so, reverse the points to get a valid face for the plane.
                // TODO: Is there a better way to do this?
                var vertPlane = new Plane(verts[0], verts[1], verts[2]);
                if (!face.Plane.Normal.EquivalentTo(vertPlane.Normal))
                {
                    Array.Reverse(verts);
                }

                decalFace.Vertices.AddRange(verts);

                // Calculate the X and Y shift bases on the first vertex location (assuming U/V of first vertex is zero) - we dont want these to change
                var vtx = decalFace.Vertices[0];
                decalFace.Texture.XShift = -(vtx.Dot(decalFace.Texture.UAxis)) / decalFace.Texture.XScale;
                decalFace.Texture.YShift = -(vtx.Dot(decalFace.Texture.VAxis)) / decalFace.Texture.YScale;

                // Next, the decal geometry needs to be clipped to the face so it doesn't spill into the void
                var poly = new Polygon(decalFace.Vertices).ToPrecisionPolygon();

                foreach (var f in solid.Faces.Except(new[] { decalFace }))
                {
                    poly.Split(f.Plane.ToPrecisionPlane(), out var back, out _);
                    poly = back ?? poly;
                }

                var newFace = poly.ToStandardPolygon();

                decalFace.Vertices.Clear();
                decalFace.Vertices.AddRange(newFace.Vertices);

                // Add a tiny bit to the normal axis to ensure the decal is rendered in front of the face
                var normalAdd = face.Plane.Normal * 0.2f;
                decalFace.Transform(Matrix4x4.CreateTranslation(normalAdd));

                yield return(decalFace);
            }
        }
コード例 #8
0
        protected override void Render(MapDocument document, BufferBuilder builder, ResourceCollector resourceCollector)
        {
            base.Render(document, builder, resourceCollector);

            if (_state != ClipState.None && _clipPlanePoint1 != null && _clipPlanePoint2 != null && _clipPlanePoint3 != null)
            {
                // Draw the lines
                var p1 = _clipPlanePoint1.Value;
                var p2 = _clipPlanePoint2.Value;
                var p3 = _clipPlanePoint3.Value;

                builder.Append(
                    new []
                {
                    new VertexStandard {
                        Position = p1, Colour = Vector4.One, Tint = Vector4.One
                    },
                    new VertexStandard {
                        Position = p2, Colour = Vector4.One, Tint = Vector4.One
                    },
                    new VertexStandard {
                        Position = p3, Colour = Vector4.One, Tint = Vector4.One
                    },
                },
                    new uint [] { 0, 1, 1, 2, 2, 0 },
                    new []
                {
                    new BufferGroup(PipelineType.Wireframe, CameraType.Both, 0, 6)
                }
                    );

                if (!p1.EquivalentTo(p2) &&
                    !p2.EquivalentTo(p3) &&
                    !p1.EquivalentTo(p3) &&
                    !document.Selection.IsEmpty)
                {
                    var plane = new Plane(p1, p2, p3);
                    var pp    = plane.ToPrecisionPlane();

                    // Draw the clipped solids
                    var faces = new List <Polygon>();
                    foreach (var solid in document.Selection.OfType <Solid>().ToList())
                    {
                        var s = solid.ToPolyhedron().ToPrecisionPolyhedron();
                        s.Split(pp, out var back, out var front);

                        if (_side != ClipSide.Front && back != null)
                        {
                            faces.AddRange(back.Polygons.Select(x => x.ToStandardPolygon()));
                        }
                        if (_side != ClipSide.Back && front != null)
                        {
                            faces.AddRange(front.Polygons.Select(x => x.ToStandardPolygon()));
                        }
                    }

                    var verts   = new List <VertexStandard>();
                    var indices = new List <int>();

                    foreach (var polygon in faces)
                    {
                        var c = verts.Count;
                        verts.AddRange(polygon.Vertices.Select(x => new VertexStandard {
                            Position = x, Colour = Vector4.One, Tint = Vector4.One
                        }));
                        for (var i = 0; i < polygon.Vertices.Count; i++)
                        {
                            indices.Add(c + i);
                            indices.Add(c + (i + 1) % polygon.Vertices.Count);
                        }
                    }

                    builder.Append(
                        verts, indices.Select(x => (uint)x),
                        new[] { new BufferGroup(PipelineType.Wireframe, CameraType.Both, 0, (uint)indices.Count) }
                        );

                    // Draw the clipping plane

                    var poly  = new DataStructures.Geometric.Precision.Polygon(pp);
                    var bbox  = document.Selection.GetSelectionBoundingBox();
                    var point = bbox.Center;
                    foreach (var boxPlane in bbox.GetBoxPlanes())
                    {
                        var proj = boxPlane.Project(point);
                        var dist = (point - proj).Length() * 0.1f;
                        var pln  = new Plane(boxPlane.Normal, proj + boxPlane.Normal * Math.Max(dist, 100)).ToPrecisionPlane();
                        if (poly.Split(pln, out var b, out _))
                        {
                            poly = b;
                        }
                    }

                    verts.Clear();
                    indices.Clear();

                    var clipPoly = poly.ToStandardPolygon();
                    var colour   = Color.FromArgb(64, Color.Turquoise).ToVector4();

                    // Add the face in both directions so it renders on both sides
                    var polies = new[] { clipPoly.Vertices.ToList(), clipPoly.Vertices.Reverse().ToList() };
                    foreach (var p in polies)
                    {
                        var offs = verts.Count;
                        verts.AddRange(p.Select(x => new VertexStandard
                        {
                            Position = x,
                            Colour   = Vector4.One,
                            Tint     = colour,
                            Flags    = VertexFlags.FlatColour
                        }));

                        for (var i = 2; i < clipPoly.Vertices.Count; i++)
                        {
                            indices.Add(offs);
                            indices.Add(offs + i - 1);
                            indices.Add(offs + i);
                        }
                    }

                    builder.Append(
                        verts, indices.Select(x => (uint)x),
                        new[] { new BufferGroup(PipelineType.TexturedAlpha, CameraType.Perspective, p1, 0, (uint)indices.Count) }
                        );
                }
            }
        }
コード例 #9
0
        private void RenderCircleTypeNone(PerspectiveCamera camera, I2DRenderer im)
        {
            var center = _pivotPoint;
            var origin = new Vector3(center.X, center.Y, center.Z);

            var distance = (camera.EyeLocation - origin).Length();

            if (distance <= 1)
            {
                return;
            }

            // Ensure points that can't be projected properly don't get rendered
            var screenOrigin = camera.WorldToScreen(origin);
            var sop          = new PointF(screenOrigin.X, screenOrigin.Y);
            var rec          = new RectangleF(-200, -200, camera.Width + 400, camera.Height + 400);

            if (!rec.Contains(sop))
            {
                return;
            }

            var radius = 0.15f * distance;

            var normal = Vector3.Normalize(Vector3.Subtract(camera.EyeLocation, origin));
            var right  = Vector3.Normalize(Vector3.Cross(normal, Vector3.UnitZ));
            var up     = Vector3.Normalize(Vector3.Cross(normal, right));

            const int   sides = 32;
            const float diff  = (float)(2 * Math.PI) / sides;

            for (var i = 0; i < sides; i++)
            {
                var cos1 = (float)Math.Cos(diff * i);
                var sin1 = (float)Math.Sin(diff * i);
                var cos2 = (float)Math.Cos(diff * (i + 1));
                var sin2 = (float)Math.Sin(diff * (i + 1));

                var line = new Line(
                    origin + right * cos1 * radius + up * sin1 * radius,
                    origin + right * cos2 * radius + up * sin2 * radius
                    );

                var st = camera.WorldToScreen(line.Start);
                var en = camera.WorldToScreen(line.End);

                im.AddLine(st.ToVector2(), en.ToVector2(), Color.DarkGray);

                line = new Line(
                    origin + right * cos1 * radius * 1.2f + up * sin1 * radius * 1.2f,
                    origin + right * cos2 * radius * 1.2f + up * sin2 * radius * 1.2f
                    );

                st = camera.WorldToScreen(line.Start);
                en = camera.WorldToScreen(line.End);

                var c = _mouseOver == CircleType.Outer ? Color.White : Color.LightGray;
                im.AddLine(st.ToVector2(), en.ToVector2(), c);
            }

            var plane = new Plane(normal, Vector3.Dot(origin, normal));

            for (var i = 0; i < sides; i++)
            {
                var cos1 = (float)Math.Cos(diff * i) * radius;
                var sin1 = (float)Math.Sin(diff * i) * radius;
                var cos2 = (float)Math.Cos(diff * (i + 1)) * radius;
                var sin2 = (float)Math.Sin(diff * (i + 1)) * radius;

                RenderLine(
                    (origin + Vector3.UnitX * cos1 + Vector3.UnitY * sin1),
                    (origin + Vector3.UnitX * cos2 + Vector3.UnitY * sin2),
                    plane,
                    _mouseOver == CircleType.Z ? Color.Blue : Color.DarkBlue,
                    camera, im);

                RenderLine(
                    (origin + Vector3.UnitY * cos1 + Vector3.UnitZ * sin1),
                    (origin + Vector3.UnitY * cos2 + Vector3.UnitZ * sin2),
                    plane,
                    _mouseOver == CircleType.X ? Color.Red : Color.DarkRed,
                    camera, im);

                RenderLine(
                    (origin + Vector3.UnitZ * cos1 + Vector3.UnitX * sin1),
                    (origin + Vector3.UnitZ * cos2 + Vector3.UnitX * sin2),
                    plane,
                    _mouseOver == CircleType.Y ? Color.Lime : Color.LimeGreen,
                    camera, im);
            }
        }
コード例 #10
0
        private void UpdateCache(IViewport viewport, PerspectiveCamera camera)
        {
            var ccl  = camera.EyeLocation;
            var ccla = camera.Position + camera.Direction;

            var cache = _cachedLines.FirstOrDefault(x => x.Viewport == viewport);

            if (cache == null)
            {
                cache = new CachedLines(viewport);
                _cachedLines.Add(cache);
            }
            if (ccl == cache.CameraLocation && ccla == cache.CameraLookAt && cache.PivotPoint == _pivotPoint && cache.Width == viewport.Width && cache.Height == viewport.Height)
            {
                return;
            }

            var origin   = _pivotPoint;
            var distance = (ccl - origin).Length();

            if (distance <= 1)
            {
                return;
            }

            cache.CameraLocation = ccl;
            cache.CameraLookAt   = ccla;
            cache.PivotPoint     = _pivotPoint;
            cache.Width          = viewport.Width;
            cache.Height         = viewport.Height;

            var normal = (ccl - origin).Normalise();
            var right  = normal.Cross(Vector3.UnitZ).Normalise();
            var up     = normal.Cross(right).Normalise();

            var plane = new Plane(normal, origin.Dot(normal));

            const float sides = 32;
            var         diff  = (2 * Math.PI) / sides;

            var radius = 0.15f * distance;

            cache.Cache[CircleType.Outer].Clear();
            cache.Cache[CircleType.X].Clear();
            cache.Cache[CircleType.Y].Clear();
            cache.Cache[CircleType.Z].Clear();

            for (var i = 0; i < sides; i++)
            {
                var cos1 = (float)Math.Cos(diff * i);
                var sin1 = (float)Math.Sin(diff * i);
                var cos2 = (float)Math.Cos(diff * (i + 1));
                var sin2 = (float)Math.Sin(diff * (i + 1));

                // outer circle
                AddLine(CircleType.Outer,
                        origin + right * cos1 * radius * 1.2f + up * sin1 * radius * 1.2f,
                        origin + right * cos2 * radius * 1.2f + up * sin2 * radius * 1.2f,
                        plane, cache);

                cos1 *= radius;
                sin1 *= radius;
                cos2 *= radius;
                sin2 *= radius;

                // X/Y plane = Z axis
                AddLine(CircleType.Z,
                        origin + Vector3.UnitX * cos1 + Vector3.UnitY * sin1,
                        origin + Vector3.UnitX * cos2 + Vector3.UnitY * sin2,
                        plane, cache);

                // Y/Z plane = X axis
                AddLine(CircleType.X,
                        origin + Vector3.UnitY * cos1 + Vector3.UnitZ * sin1,
                        origin + Vector3.UnitY * cos2 + Vector3.UnitZ * sin2,
                        plane, cache);

                // X/Z plane = Y axis
                AddLine(CircleType.Y,
                        origin + Vector3.UnitZ * cos1 + Vector3.UnitX * sin1,
                        origin + Vector3.UnitZ * cos2 + Vector3.UnitX * sin2,
                        plane, cache);
            }
        }