private void RotateAroundAxis(IObject3D selectedItem, double rotationAngle) { var rotationVector = Vector3.Zero; rotationVector[RotationAxis] = -rotationAngle; var rotationMatrix = Matrix4X4.CreateRotation(rotationVector); selectedItem.Matrix = selectedItem.Matrix.ApplyAtPosition(mouseDownInfo.SelectedObjectRotationCenter, rotationMatrix); }
public static void Rotate(this IObject3D item, Vector3 origin, Vector3 axis, double angle) { // move object relative to rotation item.Matrix *= Matrix4X4.CreateTranslation(-origin); // rotate it item.Matrix *= Matrix4X4.CreateRotation(axis, angle); // move it back item.Matrix *= Matrix4X4.CreateTranslation(origin); }
/// <summary> /// Calculate the transform to move from the plane to a scaled polygon at plane z=1 offset = 0 /// </summary> /// <param name="plane">The plane to transform from</param> /// <param name="outputScale">The amout to scale up when transforming</param> /// <returns>The plane to accomplish the transform</returns> public static Matrix4X4 GetTransformTo0Plane(Plane plane, int outputScale = 1000) { var rotation = new Quaternion(plane.Normal, Vector3.UnitZ); var flattenedMatrix = Matrix4X4.CreateRotation(rotation); flattenedMatrix *= Matrix4X4.CreateTranslation(0, 0, -plane.DistanceFromOrigin); var transformTo0Plane = flattenedMatrix * Matrix4X4.CreateScale(outputScale); return(transformTo0Plane); }
public override void OnMouseUp(MouseEventArgs mouseEvent) { base.OnMouseUp(mouseEvent); if (mouseEvent.Button != MouseButtons.Left) { return; } // Make sure we don't use the trace data before it is ready if (mouseDownPosition == mouseEvent.Position && cubeTraceData != null) { Ray ray = world.GetRayForLocalBounds(mouseEvent.Position); IntersectInfo info = cubeTraceData.GetClosestIntersection(ray); if (info != null) { var hitData = GetHitData(info.HitPosition); var normalAndUp = GetDirectionForFace(hitData); var look = Matrix4X4.LookAt(Vector3.Zero, normalAndUp.normal, normalAndUp.up); var start = new Quaternion(interactionLayer.World.RotationMatrix); var end = new Quaternion(look); Task.Run(() => { // TODO: stop any spinning happening in the view double duration = .25; var timer = Stopwatch.StartNew(); var time = timer.Elapsed.TotalSeconds; while (time < duration) { var current = Quaternion.Slerp(start, end, time / duration); UiThread.RunOnIdle(() => { interactionLayer.World.RotationMatrix = Matrix4X4.CreateRotation(current); Invalidate(); }); time = timer.Elapsed.TotalSeconds; Thread.Sleep(10); } interactionLayer.World.RotationMatrix = Matrix4X4.CreateRotation(end); Invalidate(); }); } } interactionLayer.Focus(); }
public override void Draw(DrawGlContentEventArgs e) { bool shouldDrawScaleControls = controlVisible == null ? true : controlVisible(); if (Object3DControlContext.SelectedObject3DControl != null && Object3DControlContext.SelectedObject3DControl as ScaleDiameterControl == null) { shouldDrawScaleControls = false; } var selectedItem = RootSelection; if (selectedItem != null) { // Ensures that functions in this scope run against the original instance reference rather than the // current value, thus avoiding null reference errors that would occur otherwise if (shouldDrawScaleControls) { // don't draw if any other control is dragging var color = theme.PrimaryAccentColor.WithAlpha(e.Alpha0to255); if (MouseIsOver || MouseDownOnControl) { GLHelper.Render(grabControlMesh, color, TotalTransform, RenderTypes.Shaded); } else { color = theme.TextColor; GLHelper.Render(grabControlMesh, color.WithAlpha(e.Alpha0to255), TotalTransform, RenderTypes.Shaded); } Vector3 newBottomCenter = ObjectSpace.GetCenterPosition(selectedItem, placement); var rotation = Matrix4X4.CreateRotation(new Quaternion(selectedItem.Matrix)); var translation = Matrix4X4.CreateTranslation(newBottomCenter); Object3DControlContext.World.RenderRing(rotation * translation, Vector3.Zero, getDiameters[diameterIndex](), 60, color.WithAlpha(e.Alpha0to255), 2, 0, e.ZBuffered); } if (hitPlane != null) { //Object3DControlContext.World.RenderPlane(hitPlane.Plane, Color.Red, true, 50, 3); //Object3DControlContext.World.RenderPlane(initialHitPosition, hitPlane.Plane.Normal, Color.Red, true, 50, 3); } if (shouldDrawScaleControls && (MouseIsOver || MouseDownOnControl)) { DrawMeasureLines(e); } } base.Draw(e); }
public static void RenderDirectionAxis(this WorldView world, DirectionAxis axis, Matrix4X4 matrix, double size) { GLHelper.PrepareFor3DLineRender(true); Frustum frustum = world.GetClippingFrustum(); Vector3 length = axis.Normal * size; var color = Agg.Color.Red; // draw center line { var min = axis.Origin - length; Vector3 start = Vector3Ex.Transform(min, matrix); var max = axis.Origin + length; Vector3 end = Vector3Ex.Transform(max, matrix); world.Render3DLineNoPrep(frustum, start, end, color, 1); } var perpendicular = Vector3.GetPerpendicular(axis.Normal, Vector3.Zero).GetNormal(); // draw some lines to mark the rotation plane int count = 20; bool first = true; var firstEnd = Vector3.Zero; var lastEnd = Vector3.Zero; var center = Vector3Ex.Transform(axis.Origin, matrix); for (int i = 0; i < count; i++) { var rotation = size / 4 * Vector3Ex.Transform(perpendicular, Matrix4X4.CreateRotation(axis.Normal, MathHelper.Tau * i / count)); // draw center line var max = axis.Origin + rotation; Vector3 end = Vector3Ex.Transform(max, matrix); world.Render3DLineNoPrep(frustum, center, end, color, 1); if (!first) { world.Render3DLineNoPrep(frustum, end, lastEnd, color, 1); } else { firstEnd = end; } lastEnd = end; first = false; } world.Render3DLineNoPrep(frustum, firstEnd, lastEnd, color, 1); GL.Enable(EnableCap.Lighting); }
public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo) { var topPosition = GetTopPosition(selectedItem); double distBetweenPixelsWorldSpace = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(topPosition); Vector3 arrowCenter = topPosition; arrowCenter.Z += arrowSize / 2 * distBetweenPixelsWorldSpace; var rotation = Matrix4X4.CreateRotation(new Quaternion(selectedItem.Matrix)); TotalTransform = rotation * Matrix4X4.CreateScale(distBetweenPixelsWorldSpace) * Matrix4X4.CreateTranslation(arrowCenter); }
//Mouse drag, calculate rotation public void OnMouseMove(Vector2 mousePosition) { switch (CurrentTrackingType) { case TrackBallTransformType.Rotation: Quaternion activeRotationQuaternion = GetRotationForMove(ScreenCenter, world, TrackBallRadius, mouseDownPosition, mousePosition, false); if (activeRotationQuaternion != Quaternion.Identity) { mouseDownPosition = mousePosition; world.RotationMatrix = world.RotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion); OnTransformChanged(null); } break; case TrackBallTransformType.Translation: { Vector2 mouseDelta = mousePosition - lastTranslationMousePosition; Vector2 scaledDelta = mouseDelta / this.ScreenCenter.X * 4.75; Vector3 offset = new Vector3(scaledDelta.X, scaledDelta.Y, 0); offset = Vector3Ex.TransformPosition(offset, Matrix4X4.Invert(world.RotationMatrix)); offset = Vector3Ex.TransformPosition(offset, localToScreenTransform); world.TranslationMatrix = world.TranslationMatrix * Matrix4X4.CreateTranslation(offset); lastTranslationMousePosition = mousePosition; OnTransformChanged(null); } break; case TrackBallTransformType.Scale: { Vector2 mouseDelta = mousePosition - lastScaleMousePosition; double zoomDelta = 1; if (mouseDelta.Y < 0) { zoomDelta = 1 - (-1 * mouseDelta.Y / 100); } else if (mouseDelta.Y > 0) { zoomDelta = 1 + (1 * mouseDelta.Y / 100); } world.Scale = world.Scale * zoomDelta; lastScaleMousePosition = mousePosition; OnTransformChanged(null); } break; default: throw new NotImplementedException(); } }
private void RenderLine(Matrix4X4 transform, Vector3 start, Vector3 end, Color color, bool zBuffered = true) { Vector3 lineCenter = (start + end) / 2; Vector3 delta = start - end; Matrix4X4 rotateTransform = Matrix4X4.CreateRotation(new Quaternion(delta.GetNormal(), Vector3.UnitX + new Vector3(.0001, -.00001, .00002))); Matrix4X4 scaleTransform = Matrix4X4.CreateScale((end - start).Length, 1, 1); Matrix4X4 lineTransform = scaleTransform * rotateTransform * Matrix4X4.CreateTranslation(lineCenter) * transform; if (zBuffered) { GLHelper.Render(lineMesh, Color.Black, lineTransform, RenderTypes.Shaded); //drawEvent.graphics2D.Line(cornerPositionScreen, cornerPositionCcwScreen, Color.Gray); } else { // render on top of everything very lightly GLHelper.Render(lineMesh, new Color(Color.Black, 5), lineTransform, RenderTypes.Shaded); } }
public void SetPrintLevelingEquation(Vector3 position0, Vector3 position1, Vector3 position2, Vector2 bedCenter) { if (position0 == position1 || position1 == position2 || position2 == position0) { return; } Plane planeOfPoints = new Plane(position0, position1, position2); Ray ray = new Ray(new Vector3(bedCenter, 0), Vector3.UnitZ); bool inFront; double distanceToPlaneAtBedCenter = planeOfPoints.GetDistanceToIntersection(ray, out inFront); Matrix4X4 makePointsFlatMatrix = Matrix4X4.CreateTranslation(-bedCenter.x, -bedCenter.y, -distanceToPlaneAtBedCenter); makePointsFlatMatrix *= Matrix4X4.CreateRotation(planeOfPoints.PlaneNormal, Vector3.UnitZ); makePointsFlatMatrix *= Matrix4X4.CreateTranslation(bedCenter.x, bedCenter.y, 0); //distanceToPlaneAtBedCenter); bedLevelMatrix = Matrix4X4.Invert(makePointsFlatMatrix); }
public static Matrix4X4 GetMaxFaceProjection(Face face, ImageBuffer textureToUse, Matrix4X4?initialTransform = null) { // If not set than make it identity var firstTransform = initialTransform == null ? Matrix4X4.Identity : (Matrix4X4)initialTransform; var textureCoordinateMapping = Matrix4X4.CreateRotation(new Quaternion(face.Normal, Vector3.UnitZ)); var bounds = RectangleDouble.ZeroIntersection; foreach (FaceEdge faceEdge in face.FaceEdges()) { var edgeStartPosition = faceEdge.FirstVertex.Position; var textureUv = Vector3.Transform(edgeStartPosition, textureCoordinateMapping); bounds.ExpandToInclude(new Vector2(textureUv)); } var centering = Matrix4X4.CreateTranslation(new Vector3(-bounds.Left, -bounds.Bottom, 0)); var scaling = Matrix4X4.CreateScale(new Vector3(1 / bounds.Width, 1 / bounds.Height, 1)); return(textureCoordinateMapping * firstTransform * centering * scaling); }
public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo) { // create the transform for the box Vector3 cornerPosition = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex); Vector3 boxCenter = cornerPosition; double distBetweenPixelsWorldSpace = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(cornerPosition); switch (quadrantIndex) { case 0: boxCenter.X += selectCubeSize / 2 * distBetweenPixelsWorldSpace; boxCenter.Y += selectCubeSize / 2 * distBetweenPixelsWorldSpace; break; case 1: boxCenter.X -= selectCubeSize / 2 * distBetweenPixelsWorldSpace; boxCenter.Y += selectCubeSize / 2 * distBetweenPixelsWorldSpace; break; case 2: boxCenter.X -= selectCubeSize / 2 * distBetweenPixelsWorldSpace; boxCenter.Y -= selectCubeSize / 2 * distBetweenPixelsWorldSpace; break; case 3: boxCenter.X += selectCubeSize / 2 * distBetweenPixelsWorldSpace; boxCenter.Y -= selectCubeSize / 2 * distBetweenPixelsWorldSpace; break; } boxCenter.Z += selectCubeSize / 2 * distBetweenPixelsWorldSpace; var rotation = Matrix4X4.CreateRotation(new Quaternion(selectedItem.Matrix)); var centerMatrix = Matrix4X4.CreateTranslation(boxCenter); centerMatrix = rotation * Matrix4X4.CreateScale(distBetweenPixelsWorldSpace) * centerMatrix; TotalTransform = centerMatrix; }
public void OnMouseUp() { switch (currentTrackingType) { case MouseDownType.Rotation: currentRotationMatrix = currentRotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion); activeRotationQuaternion = Quaternion.Identity; break; case MouseDownType.Translation: //currentTranslationMatrix = Matrix4X4.Identity; break; case MouseDownType.Scale: break; default: throw new NotImplementedException(); } currentTrackingType = MouseDownType.None; }
public static Matrix4X4 GetMaxPlaneProjection(this Mesh mesh, int face, ImageBuffer textureToUse, Matrix4X4?initialTransform = null) { // If not set than make it identity var firstTransform = initialTransform == null ? Matrix4X4.Identity : (Matrix4X4)initialTransform; var textureCoordinateMapping = Matrix4X4.CreateRotation(new Quaternion(mesh.Faces[face].normal.AsVector3(), Vector3.UnitZ)); var bounds = RectangleDouble.ZeroIntersection; foreach (int vertexIndex in new int[] { mesh.Faces[face].v0, mesh.Faces[face].v1, mesh.Faces[face].v2 }) { var edgeStartPosition = mesh.Vertices[vertexIndex]; var textureUv = edgeStartPosition.Transform(textureCoordinateMapping); bounds.ExpandToInclude(new Vector2(textureUv)); } var centering = Matrix4X4.CreateTranslation(new Vector3(-bounds.Left, -bounds.Bottom, 0)); var scaling = Matrix4X4.CreateScale(new Vector3(1 / bounds.Width, 1 / bounds.Height, 1)); return(textureCoordinateMapping * firstTransform * centering * scaling); }
public DepthPeeling(Mesh mesh) { gl.create_shader_program(null, meshVertexShader, meshFragmentShader, out scene_p_id); gl.create_shader_program(null, tex_v_shader, tex_f_shader, out tex_p_id); var aabb = mesh.GetAxisAlignedBoundingBox(); mesh.Translate(-aabb.Center); mesh.Transform(Matrix4X4.CreateRotation(new Vector3(23, 51, 12))); mesh.Transform(Matrix4X4.CreateScale(1 / Math.Max(aabb.XSize, Math.Max(aabb.YSize, aabb.ZSize)) / 2)); var screenQuad = new Mesh(); screenQuad.CreateFace(new Vector3[] { new Vector3(-1, -1, 0), new Vector3(1, -1, 0), new Vector3(1, 1, 0), new Vector3(-1, 1, 0) }); screenQuadVao = GLMeshVertexArrayObjectPlugin.Get(screenQuad); }
public void SubtractIcosahedronsWorks() { Vector3 centering = new Vector3(100, 100, 20); Mesh meshA = PlatonicSolids.CreateIcosahedron(35); meshA.Translate(centering); Mesh meshB = PlatonicSolids.CreateIcosahedron(35); Vector3 finalTransform = new Vector3(105.240172225344, 92.9716306394062, 18.4619570261172); Vector3 rotCurrent = new Vector3(4.56890223673623, -2.67874102322035, 1.02768848238523); Vector3 scaleCurrent = new Vector3(1.07853517569753, 0.964980885267323, 1.09290934544604); Matrix4X4 transformB = Matrix4X4.CreateScale(scaleCurrent) * Matrix4X4.CreateRotation(rotCurrent) * Matrix4X4.CreateTranslation(finalTransform); meshB.Transform(transformB); Mesh meshToAdd = CsgOperations.Subtract(meshA, meshB); AxisAlignedBoundingBox a_aabb = meshA.GetAxisAlignedBoundingBox(); AxisAlignedBoundingBox b_aabb = meshB.GetAxisAlignedBoundingBox(); AxisAlignedBoundingBox intersect_aabb = meshToAdd.GetAxisAlignedBoundingBox(); Assert.IsTrue(a_aabb.XSize == 40 && a_aabb.YSize == 40 && a_aabb.ZSize == 40); Assert.IsTrue(intersect_aabb.XSize == 40 && intersect_aabb.YSize == 40 && intersect_aabb.ZSize == 40); }
//Mouse drag, calculate rotation public void OnMouseMove(Vector2 mousePosition, bool rotateOnZ) { switch (currentTrackingType) { case MouseDownType.Rotation: var activeRotationQuaternion = Quaternion.Identity; //Map the point to the sphere moveSpherePosition = MapMoveToSphere(mousePosition, rotateOnZ); //Return the quaternion equivalent to the rotation //Compute the vector perpendicular to the begin and end vectors var rotationStart3D = new Vector3(0, 0, 1); if (rotateOnZ) { rotationStart3D = new Vector3(1, 0, 0); } Vector3 Perp = Vector3.Cross(rotationStart3D, moveSpherePosition); //Compute the length of the perpendicular vector if (Perp.Length > Epsilon) { //if its non-zero //We're ok, so return the perpendicular vector as the transform after all activeRotationQuaternion.X = Perp.X; activeRotationQuaternion.Y = Perp.Y; activeRotationQuaternion.Z = Perp.Z; //In the quaternion values, w is cosine (theta / 2), where theta is the rotation angle activeRotationQuaternion.W = Vector3.Dot(rotationStart3D, moveSpherePosition); mouseDownPosition = mousePosition; world.RotationMatrix = world.RotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion); OnTransformChanged(null); } break; case MouseDownType.Translation: { Vector2 mouseDelta = mousePosition - lastTranslationMousePosition; Vector2 scaledDelta = mouseDelta / world.ScreenCenter.X * 4.75; Vector3 offset = new Vector3(scaledDelta.X, scaledDelta.Y, 0); offset = Vector3.TransformPosition(offset, Matrix4X4.Invert(world.RotationMatrix)); offset = Vector3.TransformPosition(offset, localToScreenTransform); world.TranslationMatrix = world.TranslationMatrix * Matrix4X4.CreateTranslation(offset); lastTranslationMousePosition = mousePosition; OnTransformChanged(null); } break; case MouseDownType.Scale: { Vector2 mouseDelta = mousePosition - lastScaleMousePosition; double zoomDelta = 1; if (mouseDelta.Y < 0) { zoomDelta = 1 - (-1 * mouseDelta.Y / 100); } else if (mouseDelta.Y > 0) { zoomDelta = 1 + (1 * mouseDelta.Y / 100); } world.Scale = world.Scale * zoomDelta; lastScaleMousePosition = mousePosition; OnTransformChanged(null); } break; default: throw new NotImplementedException(); } }
private Matrix4X4 GetRotationTransform(IObject3D selectedItem, out double radius) { Matrix4X4 rotationCenterTransform; AxisAlignedBoundingBox currentSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(); Vector3 controlCenter = GetControlCenter(selectedItem); Vector3 rotationCenter = GetRotationCenter(selectedItem, currentSelectedBounds); if (mouseDownInfo != null) { rotationCenter = mouseDownInfo.SelectedObjectRotationCenter; controlCenter = mouseDownInfo.ControlCenter; } double distBetweenPixelsWorldSpace = InteractionContext.World.GetWorldUnitsPerScreenPixelAtPosition(rotationCenter); double lengthFromCenterToControl = (rotationCenter - controlCenter).Length; radius = lengthFromCenterToControl * (1 / distBetweenPixelsWorldSpace); rotationTransformScale = distBetweenPixelsWorldSpace; rotationCenterTransform = Matrix4X4.CreateScale(distBetweenPixelsWorldSpace) * Matrix4X4.CreateTranslation(rotationCenter); var center = Vector3Ex.Transform(Vector3.Zero, rotationCenterTransform); switch (RotationAxis) { case 0: { rotationCenterTransform = Matrix4X4.CreateTranslation(-center) * Matrix4X4.CreateRotation(new Vector3(0, -MathHelper.Tau / 4, 0)) * Matrix4X4.CreateRotation(new Vector3(-MathHelper.Tau / 4, 0, 0)) * rotationCenterTransform; var center2 = Vector3Ex.Transform(Vector3.Zero, rotationCenterTransform); rotationCenterTransform *= Matrix4X4.CreateTranslation(center - center2); } break; case 1: { rotationCenterTransform = Matrix4X4.CreateTranslation(-center) * Matrix4X4.CreateRotation(new Vector3(MathHelper.Tau / 4, 0, 0)) * Matrix4X4.CreateRotation(new Vector3(0, MathHelper.Tau / 4, 0)) * rotationCenterTransform; var center2 = Vector3Ex.Transform(Vector3.Zero, rotationCenterTransform); rotationCenterTransform *= Matrix4X4.CreateTranslation(center - center2); } break; case 2: break; } return(rotationCenterTransform); }
public override void OnMouseUp(MouseEventArgs mouseEvent) { base.OnMouseUp(mouseEvent); // Make sure we don't use the trace data before it is ready if (mouseDownPosition == mouseEvent.Position && cubeTraceData != null) { Ray ray = world.GetRayForLocalBounds(mouseEvent.Position); IntersectInfo info = cubeTraceData.GetClosestIntersection(ray); if (info != null) { var normal = ((TriangleShape)info.closestHitObject).Plane.Normal; var directionForward = -new Vector3(normal); var directionUp = Vector3.UnitY; if (directionForward.Equals(Vector3.UnitX, .001)) { directionUp = Vector3.UnitZ; } else if (directionForward.Equals(-Vector3.UnitX, .001)) { directionUp = Vector3.UnitZ; } else if (directionForward.Equals(Vector3.UnitY, .001)) { directionUp = Vector3.UnitZ; } else if (directionForward.Equals(-Vector3.UnitY, .001)) { directionUp = Vector3.UnitZ; } else if (directionForward.Equals(Vector3.UnitZ, .001)) { directionUp = -Vector3.UnitY; } var look = Matrix4X4.LookAt(Vector3.Zero, directionForward, directionUp); var start = new Quaternion(interactionLayer.World.RotationMatrix); var end = new Quaternion(look); Task.Run(() => { // TODO: stop any spinning happening in the view double duration = .25; var timer = Stopwatch.StartNew(); var time = timer.Elapsed.TotalSeconds; while (time < duration) { var current = Quaternion.Slerp(start, end, time / duration); UiThread.RunOnIdle(() => { interactionLayer.World.RotationMatrix = Matrix4X4.CreateRotation(current); Invalidate(); }); time = timer.Elapsed.TotalSeconds; Thread.Sleep(10); } interactionLayer.World.RotationMatrix = Matrix4X4.CreateRotation(end); Invalidate(); }); } } interactionLayer.Focus(); }
static public void CreatePointer(VectorPOD <ColorVertexData> colorVertexData, VectorPOD <int> indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, Color color) { Vector3 direction = endPos - startPos; Vector3 directionNormal = direction.GetNormal(); Vector3 startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal(); int[] tubeStartIndices = new int[steps]; for (int i = 0; i < steps; i++) { // create tube ends verts Vector3 tubeNormal = Vector3Ex.Transform(startSweepDirection, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); Vector3 offset = Vector3Ex.Transform(startSweepDirection * radius, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); Vector3 tubeStart = startPos + offset; tubeStartIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color)); Vector3 tubeEnd = endPos + offset; } int tipEndIndex = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(endPos, directionNormal, color)); for (int i = 0; i < steps; i++) { // create tube polys indexData.Add(tubeStartIndices[i]); indexData.Add(tubeStartIndices[(i + 1) % steps]); indexData.Add(tipEndIndex); } }
public override void OnMouseMove(MouseEventArgs mouseEvent) { mouseOver = false; // find the ray for this control // check what face it hits // mark that face to draw a highlight base.OnMouseMove(mouseEvent); // rotate the view if (MouseDownOnWidget) { var movePosition = mouseEvent.Position; Quaternion activeRotationQuaternion = TrackBallController.GetRotationForMove(new Vector2(Width / 2, Height / 2), world, Width, lastMovePosition, movePosition, false); if (activeRotationQuaternion != Quaternion.Identity) { lastMovePosition = movePosition; interactionLayer.World.RotationMatrix = interactionLayer.World.RotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion); interactionLayer.Invalidate(); } } else if (world != null && cubeTraceData != null) // Make sure we don't use the trace data before it is ready { Ray ray = world.GetRayForLocalBounds(mouseEvent.Position); IntersectInfo info = cubeTraceData.GetClosestIntersection(ray); if (info != null) { mouseOver = true; DrawMouseHover(GetHitData(info.HitPosition)); } else { ResetTextures(); } } }
public static void MakeLowestFaceFlat(this InteractiveScene scene, IObject3D objectToLayFlatGroup) { bool firstVertex = true; IObject3D objectToLayFlat = objectToLayFlatGroup; IVertex lowestVertex = null; Vector3 lowestVertexPosition = Vector3.Zero; IObject3D itemToLayFlat = null; // Process each child, checking for the lowest vertex foreach (var itemToCheck in objectToLayFlat.VisibleMeshes()) { var meshToCheck = itemToCheck.Mesh.GetConvexHull(false); if (meshToCheck == null && meshToCheck.Vertices.Count < 3) { continue; } // find the lowest point on the model for (int testIndex = 0; testIndex < meshToCheck.Vertices.Count; testIndex++) { var vertex = meshToCheck.Vertices[testIndex]; Vector3 vertexPosition = Vector3.Transform(vertex.Position, itemToCheck.WorldMatrix()); if (firstVertex) { lowestVertex = meshToCheck.Vertices[testIndex]; lowestVertexPosition = vertexPosition; itemToLayFlat = itemToCheck; firstVertex = false; } else if (vertexPosition.Z < lowestVertexPosition.Z) { lowestVertex = meshToCheck.Vertices[testIndex]; lowestVertexPosition = vertexPosition; itemToLayFlat = itemToCheck; } } } if (lowestVertex == null) { // didn't find any selected mesh return; } PolygonMesh.Face faceToLayFlat = null; double lowestAngleOfAnyFace = double.MaxValue; // Check all the faces that are connected to the lowest point to find out which one to lay flat. foreach (var face in lowestVertex.ConnectedFaces()) { double biggestAngleToFaceVertex = double.MinValue; foreach (IVertex faceVertex in face.Vertices()) { if (faceVertex != lowestVertex) { Vector3 faceVertexPosition = Vector3.Transform(faceVertex.Position, itemToLayFlat.WorldMatrix()); Vector3 pointRelLowest = faceVertexPosition - lowestVertexPosition; double xLeg = new Vector2(pointRelLowest.X, pointRelLowest.Y).Length; double yLeg = pointRelLowest.Z; double angle = Math.Atan2(yLeg, xLeg); if (angle > biggestAngleToFaceVertex) { biggestAngleToFaceVertex = angle; } } } if (biggestAngleToFaceVertex < lowestAngleOfAnyFace) { lowestAngleOfAnyFace = biggestAngleToFaceVertex; faceToLayFlat = face; } } double maxDistFromLowestZ = 0; List <Vector3> faceVertices = new List <Vector3>(); foreach (IVertex vertex in faceToLayFlat.Vertices()) { Vector3 vertexPosition = Vector3.Transform(vertex.Position, itemToLayFlat.WorldMatrix()); faceVertices.Add(vertexPosition); maxDistFromLowestZ = Math.Max(maxDistFromLowestZ, vertexPosition.Z - lowestVertexPosition.Z); } if (maxDistFromLowestZ > .001) { Vector3 xPositive = (faceVertices[1] - faceVertices[0]).GetNormal(); Vector3 yPositive = (faceVertices[2] - faceVertices[0]).GetNormal(); Vector3 planeNormal = Vector3.Cross(xPositive, yPositive).GetNormal(); // this code takes the minimum rotation required and looks much better. Quaternion rotation = new Quaternion(planeNormal, new Vector3(0, 0, -1)); Matrix4X4 partLevelMatrix = Matrix4X4.CreateRotation(rotation); // rotate it objectToLayFlat.Matrix = objectToLayFlatGroup.ApplyAtBoundsCenter(partLevelMatrix); } PlatingHelper.PlaceOnBed(objectToLayFlatGroup); }
public void Rotate(Quaternion rotation) { currentRotationMatrix = currentRotationMatrix * Matrix4X4.CreateRotation(rotation); }
public void Rotate(Quaternion rotation) { currentRotationMatrix = currentRotationMatrix * Matrix4X4.CreateRotation(rotation); OnTransformChanged(null); }
static public void CreateCylinder(VectorPOD <ColorVertexData> colorVertexData, VectorPOD <int> indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, RGBA_Bytes color, double layerHeight) { Vector3 direction = endPos - startPos; Vector3 directionNormal = direction.GetNormal(); Vector3 startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal(); int[] tubeStartIndices = new int[steps]; int[] tubeEndIndices = new int[steps]; int[] capStartIndices = new int[steps]; int[] capEndIndices = new int[steps]; double halfHeight = layerHeight / 2 + (layerHeight * .1); double halfWidth = (radius * radius) / halfHeight; double zScale = halfHeight / radius; double xScale = halfWidth / radius; Vector3 scale = new Vector3(xScale, xScale, zScale); for (int i = 0; i < steps; i++) { // create tube ends verts Vector3 tubeNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); Vector3 offset = Vector3.Transform(startSweepDirection * radius, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); offset *= scale; Vector3 tubeStart = startPos + offset; tubeStartIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color)); Vector3 tubeEnd = endPos + offset; tubeEndIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(tubeEnd, tubeNormal, color)); // create cap verts Vector3 rotateAngle = Vector3.Cross(direction, startSweepDirection); Vector3 capStartNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(rotateAngle, MathHelper.Tau / 8)); capStartNormal = Vector3.Transform(capStartNormal, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); capStartNormal = (capStartNormal * scale).GetNormal(); Vector3 capStartOffset = capStartNormal * radius; capStartOffset *= scale; Vector3 capStart = startPos + capStartOffset; capStartIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(capStart, capStartNormal, color)); Vector3 capEndNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(-rotateAngle, MathHelper.Tau / 8)); capEndNormal = Vector3.Transform(capEndNormal, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); capEndNormal = (capEndNormal * scale).GetNormal(); Vector3 capEndOffset = capEndNormal * radius; capEndOffset *= scale; Vector3 capEnd = endPos + capEndOffset; capEndIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(capEnd, capEndNormal, color)); } int tipStartIndex = colorVertexData.Count; Vector3 tipOffset = directionNormal * radius; tipOffset *= scale; colorVertexData.Add(new ColorVertexData(startPos - tipOffset, -directionNormal, color)); int tipEndIndex = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(endPos + tipOffset, directionNormal, color)); for (int i = 0; i < steps; i++) { // create tube polys indexData.Add(tubeStartIndices[i]); indexData.Add(tubeEndIndices[i]); indexData.Add(tubeEndIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[i]); indexData.Add(tubeEndIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[(i + 1) % steps]); // create start cap polys indexData.Add(tubeStartIndices[i]); indexData.Add(capStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[(i + 1) % steps]); // create end cap polys indexData.Add(tubeEndIndices[i]); indexData.Add(capEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); indexData.Add(tubeEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); indexData.Add(tubeEndIndices[(i + 1) % steps]); // create start tip polys indexData.Add(tipStartIndex); indexData.Add(capStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); // create end tip polys indexData.Add(tipEndIndex); indexData.Add(capEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); } }
public static void Render3DLineNoPrep(this WorldView world, Frustum clippingFrustum, Vector3 start, Vector3 end, Color color, double width = 1) { if (clippingFrustum.ClipLine(ref start, ref end)) { double unitsPerPixelStart = world.GetWorldUnitsPerScreenPixelAtPosition(start); double unitsPerPixelEnd = world.GetWorldUnitsPerScreenPixelAtPosition(end); Vector3 delta = start - end; var deltaLength = delta.Length; Matrix4X4 rotateTransform = Matrix4X4.CreateRotation(new Quaternion(Vector3.UnitX + new Vector3(.0001, -.00001, .00002), -delta / deltaLength)); Matrix4X4 scaleTransform = Matrix4X4.CreateScale(deltaLength, 1, 1); Vector3 lineCenter = (start + end) / 2; Matrix4X4 lineTransform = scaleTransform * rotateTransform * Matrix4X4.CreateTranslation(lineCenter); var startScale = unitsPerPixelStart * width; var endScale = unitsPerPixelEnd * width; for (int i = 0; i < unscaledLineMesh.Vertices.Count; i++) { var vertexPosition = unscaledLineMesh.Vertices[i]; if (vertexPosition.X < 0) { scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * startScale, vertexPosition.Z * startScale); } else { scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * endScale, vertexPosition.Z * endScale); } } if (true) { GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); if (color.Alpha0To1 < 1) { GL.Enable(EnableCap.Blend); } else { //GL.Disable(EnableCap.Blend); } GL.MatrixMode(MatrixMode.Modelview); GL.PushMatrix(); GL.MultMatrix(lineTransform.GetAsFloatArray()); GL.Begin(BeginMode.Triangles); for (int faceIndex = 0; faceIndex < scaledLineMesh.Faces.Count; faceIndex++) { var face = scaledLineMesh.Faces[faceIndex]; var vertices = scaledLineMesh.Vertices; var position = vertices[face.v0]; GL.Vertex3(position.X, position.Y, position.Z); position = vertices[face.v1]; GL.Vertex3(position.X, position.Y, position.Z); position = vertices[face.v2]; GL.Vertex3(position.X, position.Y, position.Z); } GL.End(); GL.PopMatrix(); } else { scaledLineMesh.MarkAsChanged(); GLHelper.Render(scaledLineMesh, color, lineTransform, RenderTypes.Shaded); } } }
static public void CreateCylinder(VectorPOD <ColorVertexData> colorVertexData, VectorPOD <int> indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, Color color, double layerHeight) { var direction = endPos - startPos; var directionNormal = direction.GetNormal(); var startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal(); int[] tubeStartIndices = new int[steps]; int[] tubeEndIndices = new int[steps]; int[] capStartIndices = new int[steps]; int[] capEndIndices = new int[steps]; double halfHeight = layerHeight / 2 + (layerHeight * .1); double halfWidth = radius; double zScale = halfHeight / radius; double xScale = halfWidth / radius; // Adjust start/end positions to be centered on Z for the given layer height startPos.Z -= halfHeight; endPos.Z -= halfHeight; var scale = new Vector3(xScale, xScale, zScale); var rotateAngle = Vector3Ex.Cross(startSweepDirection, direction); var startCapStartNormal = Vector3Ex.Transform(startSweepDirection, Matrix4X4.CreateRotation(rotateAngle, MathHelper.Tau / 8)); var startCapEndNormal = Vector3Ex.Transform(startSweepDirection, Matrix4X4.CreateRotation(-rotateAngle, MathHelper.Tau / 8)); for (int i = 0; i < steps; i++) { var rotationMatrix = Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i); // create tube ends verts var tubeNormal = Vector3Ex.Transform(startSweepDirection, rotationMatrix); var offset = Vector3Ex.Transform(startSweepDirection * radius, rotationMatrix) * scale; var tubeStart = startPos + offset; tubeStartIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color)); var tubeEnd = endPos + offset; tubeEndIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(tubeEnd, tubeNormal, color)); // create cap verts var capStartNormal = Vector3Ex.Transform(startCapStartNormal, rotationMatrix); capStartNormal = (capStartNormal * scale).GetNormal(); var capStartOffset = capStartNormal * radius * scale; var capStart = startPos + capStartOffset; capStartIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(capStart, capStartNormal, color)); var capEndNormal = Vector3Ex.Transform(startCapEndNormal, rotationMatrix); capEndNormal = (capEndNormal * scale).GetNormal(); var capEndOffset = capEndNormal * radius * scale; var capEnd = endPos + capEndOffset; capEndIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(capEnd, capEndNormal, color)); } int tipStartIndex = colorVertexData.Count; var tipOffset = directionNormal * radius; tipOffset *= scale; colorVertexData.Add(new ColorVertexData(startPos - tipOffset, -directionNormal, color)); int tipEndIndex = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(endPos + tipOffset, directionNormal, color)); for (int i = 0; i < steps; i++) { // create tube polys indexData.Add(tubeStartIndices[i]); indexData.Add(tubeEndIndices[i]); indexData.Add(tubeEndIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[i]); indexData.Add(tubeEndIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[(i + 1) % steps]); // create start cap polys indexData.Add(tubeStartIndices[i]); indexData.Add(capStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[(i + 1) % steps]); // create end cap polys indexData.Add(tubeEndIndices[i]); indexData.Add(capEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); indexData.Add(tubeEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); indexData.Add(tubeEndIndices[(i + 1) % steps]); // create start tip polys indexData.Add(tipStartIndex); indexData.Add(capStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); // create end tip polys indexData.Add(tipEndIndex); indexData.Add(capEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); } }
public Rotate(CsgObject objectToRotate, Vector3 radians, string name = "") : base(objectToRotate, name) { rotateCache = radians; transform = Matrix4X4.CreateRotation(radians); }
public static void MakeLowestFaceFlat(this InteractiveScene scene, IObject3D objectToLayFlatGroup) { var preLayFlatMatrix = objectToLayFlatGroup.Matrix; bool firstVertex = true; IObject3D objectToLayFlat = objectToLayFlatGroup; Vector3Float lowestPosition = Vector3Float.PositiveInfinity; Vector3Float sourceVertexPosition = Vector3Float.NegativeInfinity; IObject3D itemToLayFlat = null; Mesh meshWithLowest = null; var items = objectToLayFlat.VisibleMeshes().Where(i => i.OutputType != PrintOutputTypes.Support); if (!items.Any()) { items = objectToLayFlat.VisibleMeshes(); } // Process each child, checking for the lowest vertex foreach (var itemToCheck in items) { var meshToCheck = itemToCheck.Mesh.GetConvexHull(false); if (meshToCheck == null && meshToCheck.Vertices.Count < 3) { continue; } // find the lowest point on the model for (int testIndex = 0; testIndex < meshToCheck.Vertices.Count; testIndex++) { var vertex = meshToCheck.Vertices[testIndex]; var vertexPosition = vertex.Transform(itemToCheck.WorldMatrix()); if (firstVertex) { meshWithLowest = meshToCheck; lowestPosition = vertexPosition; sourceVertexPosition = vertex; itemToLayFlat = itemToCheck; firstVertex = false; } else if (vertexPosition.Z < lowestPosition.Z) { meshWithLowest = meshToCheck; lowestPosition = vertexPosition; sourceVertexPosition = vertex; itemToLayFlat = itemToCheck; } } } if (meshWithLowest == null) { // didn't find any selected mesh return; } int faceToLayFlat = -1; double largestAreaOfAnyFace = 0; var facesSharingLowestVertex = meshWithLowest.Faces .Select((face, i) => new { face, i }) .Where(faceAndIndex => meshWithLowest.Vertices[faceAndIndex.face.v0] == sourceVertexPosition || meshWithLowest.Vertices[faceAndIndex.face.v1] == sourceVertexPosition || meshWithLowest.Vertices[faceAndIndex.face.v2] == sourceVertexPosition) .Select(j => j.i); var lowestFacesByAngle = facesSharingLowestVertex.OrderBy(i => { var face = meshWithLowest.Faces[i]; var worldNormal = face.normal.TransformNormal(itemToLayFlat.WorldMatrix()); return(worldNormal.CalculateAngle(-Vector3Float.UnitZ)); }); // Check all the faces that are connected to the lowest point to find out which one to lay flat. foreach (var faceIndex in lowestFacesByAngle) { var face = meshWithLowest.Faces[faceIndex]; var worldNormal = face.normal.TransformNormal(itemToLayFlat.WorldMatrix()); var worldAngleDegrees = MathHelper.RadiansToDegrees(worldNormal.CalculateAngle(-Vector3Float.UnitZ)); double largestAreaFound = 0; var faceVeretexIndices = new int[] { face.v0, face.v1, face.v2 }; foreach (var vi in faceVeretexIndices) { if (meshWithLowest.Vertices[vi] != lowestPosition) { var planSurfaceArea = 0.0; foreach (var coPlanarFace in meshWithLowest.GetCoplanerFaces(faceIndex)) { planSurfaceArea += meshWithLowest.GetSurfaceArea(coPlanarFace); } if (largestAreaOfAnyFace == 0 || (planSurfaceArea > largestAreaFound && worldAngleDegrees < 45)) { largestAreaFound = planSurfaceArea; } } } if (largestAreaFound > largestAreaOfAnyFace) { largestAreaOfAnyFace = largestAreaFound; faceToLayFlat = faceIndex; } } double maxDistFromLowestZ = 0; var lowestFace = meshWithLowest.Faces[faceToLayFlat]; var lowestFaceIndices = new int[] { lowestFace.v0, lowestFace.v1, lowestFace.v2 }; var faceVertices = new List <Vector3Float>(); foreach (var vertex in lowestFaceIndices) { var vertexPosition = meshWithLowest.Vertices[vertex].Transform(itemToLayFlat.WorldMatrix()); faceVertices.Add(vertexPosition); maxDistFromLowestZ = Math.Max(maxDistFromLowestZ, vertexPosition.Z - lowestPosition.Z); } if (maxDistFromLowestZ > .001) { var xPositive = (faceVertices[1] - faceVertices[0]).GetNormal(); var yPositive = (faceVertices[2] - faceVertices[0]).GetNormal(); var planeNormal = xPositive.Cross(yPositive).GetNormal(); // this code takes the minimum rotation required and looks much better. Quaternion rotation = new Quaternion(planeNormal, new Vector3Float(0, 0, -1)); Matrix4X4 partLevelMatrix = Matrix4X4.CreateRotation(rotation); // rotate it objectToLayFlat.Matrix = objectToLayFlatGroup.ApplyAtBoundsCenter(partLevelMatrix); } if (objectToLayFlatGroup is Object3D object3D) { AxisAlignedBoundingBox bounds = object3D.GetAxisAlignedBoundingBox(Matrix4X4.Identity, (item) => { return(item.OutputType != PrintOutputTypes.Support); }); Vector3 boundsCenter = (bounds.MaxXYZ + bounds.MinXYZ) / 2; object3D.Matrix *= Matrix4X4.CreateTranslation(new Vector3(0, 0, -boundsCenter.Z + bounds.ZSize / 2)); } else { PlatingHelper.PlaceOnBed(objectToLayFlatGroup); } scene.UndoBuffer.Add(new TransformCommand(objectToLayFlatGroup, preLayFlatMatrix, objectToLayFlatGroup.Matrix)); }
public static void Render3DLineNoPrep(this WorldView world, Frustum clippingFrustum, Vector3 start, Vector3 end, Color color, double width = 1, bool startArrow = false, bool endArrow = false) { if (clippingFrustum.ClipLine(ref start, ref end)) { double startScale = world.GetWorldUnitsPerScreenPixelAtPosition(start); double endScale = world.GetWorldUnitsPerScreenPixelAtPosition(end); Vector3 delta = start - end; var normal = delta.GetNormal(); var arrowWidth = 3 * width; var arrowLength = 6 * width; if (startArrow || endArrow) { // move the start and end points if (startArrow) { start -= normal * startScale * arrowLength; } if (endArrow) { end += normal * endScale * arrowLength; } delta = start - end; } var deltaLength = delta.Length; var rotateTransform = Matrix4X4.CreateRotation(new Quaternion(Vector3.UnitX + new Vector3(.0001, -.00001, .00002), -delta / deltaLength)); var scaleTransform = Matrix4X4.CreateScale(deltaLength, 1, 1); Vector3 lineCenter = (start + end) / 2; Matrix4X4 lineTransform = scaleTransform * rotateTransform * Matrix4X4.CreateTranslation(lineCenter); var startWidth = startScale * width; var endWidth = endScale * width; for (int i = 0; i < unscaledLineMesh.Vertices.Count; i++) { var vertexPosition = unscaledLineMesh.Vertices[i]; if (vertexPosition.X < 0) { scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * startWidth, vertexPosition.Z * startWidth); } else { scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * endWidth, vertexPosition.Z * endWidth); } } // render the line mesh directly GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); GL.Enable(EnableCap.Blend); GL.MatrixMode(MatrixMode.Modelview); GL.PushMatrix(); GL.MultMatrix(lineTransform.GetAsFloatArray()); GL.Begin(BeginMode.Triangles); for (int faceIndex = 0; faceIndex < scaledLineMesh.Faces.Count; faceIndex++) { var face = scaledLineMesh.Faces[faceIndex]; var vertices = scaledLineMesh.Vertices; var position = vertices[face.v0]; GL.Vertex3(position.X, position.Y, position.Z); position = vertices[face.v1]; GL.Vertex3(position.X, position.Y, position.Z); position = vertices[face.v2]; GL.Vertex3(position.X, position.Y, position.Z); } GL.End(); GL.PopMatrix(); // render the arrows if any if (startArrow) { RenderHead(start, startScale, normal, arrowWidth, arrowLength, false); } if (endArrow) { RenderHead(end, startScale, normal, arrowWidth, arrowLength, true); } } }