public void CalculateDecalGeometry() { _decalGeometry = new List<Face>(); if (Decal == null) return; // Texture not found var boxRadius = Coordinate.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(Origin - boxRadius, Origin + boxRadius); var root = GetRoot(Parent); // Get the faces that intersect with the decal's radius var faces = root.GetAllNodesIntersectingWith(box).OfType<Solid>() .SelectMany(x => x.Faces).Where(x => x.IntersectsWithBox(box)); var idg = new IDGenerator(); // Dummy generator foreach (var face in faces) { // Project the decal onto the face var center = face.Plane.Project(Origin); var texture = face.Texture.Clone(); texture.Name = Decal.Name; texture.Texture = Decal; texture.XShift = -Decal.Width / 2m; texture.YShift = -Decal.Height / 2m; var decalFace = new Face(idg.GetNextFaceID()) { Colour = Colour, IsSelected = IsSelected, IsHidden = IsCodeHidden, 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[] { new Vertex(face.Plane.Project(center + xShift - yShift), decalFace), // Bottom Right new Vertex(face.Plane.Project(center + xShift + yShift), decalFace), // Top Right new Vertex(face.Plane.Project(center - xShift + yShift), decalFace), // Top Left new Vertex(face.Plane.Project(center - xShift - yShift), decalFace) // 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].Location, verts[1].Location, verts[2].Location); if (!face.Plane.Normal.EquivalentTo(vertPlane.Normal)) { Array.Reverse(verts); } decalFace.Vertices.AddRange(verts); decalFace.UpdateBoundingBox(); // 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.Location.Dot(decalFace.Texture.UAxis)) / decalFace.Texture.XScale; decalFace.Texture.YShift = -(vtx.Location.Dot(decalFace.Texture.VAxis)) / decalFace.Texture.YScale; decalFace.CalculateTextureCoordinates(); // Next, the decal geometry needs to be clipped to the face so it doesn't spill into the void // Create a fake solid out of the decal geometry and clip it against all the brush planes var fake = CreateFakeDecalSolid(decalFace); foreach (var f in face.Parent.Faces.Except(new[] { face })) { Solid back, front; fake.Split(f.Plane, out back, out front, idg); fake = back ?? fake; } // Extract out the original face decalFace = fake.Faces.First(x => x.Plane.EquivalentTo(face.Plane, 0.05m)); // 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.2m; decalFace.Transform(new UnitTranslate(normalAdd), TransformFlags.TextureLock); _decalGeometry.Add(decalFace); } }
private void UpdateCurrentFace(Viewport3D viewport, ViewportEvent e) { var ray = viewport.CastRayFromScreen(e.X, e.Y); // The face doesn't change when drawing, just update the intersection if (_state == SketchState.DrawingBase || _state == SketchState.DrawingVolume) { _intersection = (_state == SketchState.DrawingBase ? _currentFace.Plane : _volumePlane).GetIntersectionPoint(ray, true, true); return; } var isect = Document.Map.WorldSpawn.GetAllNodesIntersectingWith(ray) .OfType<Solid>() .SelectMany(x => x.Faces) .Select(x => new { Item = x, Intersection = x.GetIntersectionPoint(ray) }) .Where(x => x.Intersection != null) .OrderBy(x => (x.Intersection - ray.Start).VectorMagnitude()) .FirstOrDefault(); if (isect != null) { if (_currentFace != isect.Item) { _cloneFace = isect.Item.Clone(); _cloneFace.Transform(new UnitTranslate(isect.Item.Plane.Normal * 0.1m), TransformFlags.None); } _currentFace = isect.Item; _intersection = isect.Intersection; _state = SketchState.Ready; } else { _cloneFace = null; _currentFace = null; _intersection = null; _state = SketchState.None; } }
private void BevelFace(Face face, int num) { var solid = face.Parent; var vertexCoordinates = face.Vertices.ToDictionary(x => x, x => x.Location); // Scale the face a bit and move it away by the bevel distance face.Transform(new UnitScale(Coordinate.One * 0.9m, face.BoundingBox.Center), TransformFlags.TextureLock); face.Transform(new UnitTranslate(face.Plane.Normal * num), TransformFlags.TextureLock); foreach (var edge in face.GetEdges()) { var v1 = face.Vertices.First(x => x.Location == edge.Start); var v2 = face.Vertices.First(x => x.Location == edge.End); var verts = new[] { vertexCoordinates[v1], vertexCoordinates[v2], v2.Location, v1.Location }; var f = new Face(Document.Map.IDGenerator.GetNextFaceID()) { Parent = solid, Plane = new Plane(verts[0], verts[1], verts[2]), Colour = solid.Colour, Texture = face.Texture.Clone() }; f.Vertices.AddRange(verts.Select(x => new Vertex(x, face))); f.UpdateBoundingBox(); f.AlignTextureToFace(); solid.Faces.Add(f); _selection.Add(f); } solid.UpdateBoundingBox(); UpdateSelection(); MainTool.SetDirty(true, true); }