using OpenTK.GLControl; using OpenTK.Graphics.OpenGL; using OpenTK.Mathematics; namespace Yw.WinFrmUI.Hydro { /// /// 正交相机 /// internal class Ortho2dCamera { // 基础参数 private List _pts = null; //所有坐标 private BoundingBox3 _wbx;//世界包围盒 private BoundingBox3 _vbx;//视图包围盒 private float _baseSize = 10f;//基础尺寸 private float _orthoWidth = 10f;//正交宽度 private float _orthoHeight = 10f;//正交高度 private float _orthoNear = 0.1f;//正交近截面 private float _orthoFar = 10f;//正交远截面 private float _aspect = 1f;//视口宽高比 private Vector3 _rotation = Vector3.Zero;//旋转量 private Vector3 _translation = Vector3.Zero;//平移量 /// /// 投影矩阵 /// public Matrix4 ProjectionMatrix => _projectionMatrix; private Matrix4 _projectionMatrix; /// /// 视图模型 /// public Matrix4 ViewMatrix { get { return _viewMatrix; } } private Matrix4 _viewMatrix; /// /// 模型矩阵 /// public Matrix4 ModelMatrix => _modelMatrix; private Matrix4 _modelMatrix; public Vector3 Translation => _translation; public Vector3 Rotation => _rotation; /// /// 缩放 /// public float Zoom { get => _zoom; set => _zoom = MathHelper.Clamp(value, 0.01f, 100.0f); // 限制缩放范围 } private float _zoom = 1.0f; // 当前缩放系数(1表示原始大小) /// /// 初始化 /// public void Initial(List pts) { _pts = pts; if (pts != null && pts.Count > 0) { _wbx = new BoundingBox3(pts); _vbx = TransformBoundsToViewSpace(_wbx); _baseSize = CalcuBaseSize(_vbx); } } //世界包围盒=>视图包围盒 private BoundingBox3 TransformBoundsToViewSpace(BoundingBox3 worldBounds) { var corners = worldBounds.GetCorners(); Vector3 min = new(float.MaxValue); Vector3 max = new(float.MinValue); var qua = new Quaternion(_rotation); foreach (var corner in corners) { Vector3 viewPos = Vector3.Transform(corner, qua); min = Vector3.ComponentMin(min, viewPos); max = Vector3.ComponentMax(max, viewPos); } return new BoundingBox3(min, max); } //计算基础尺寸 private float CalcuBaseSize(BoundingBox3 vbx) { var halfX = Math.Max(Math.Abs(vbx.Max.X), Math.Abs(vbx.Min.X)); var halfY = Math.Max(Math.Abs(vbx.Max.Y), Math.Abs(vbx.Min.Y)); var halfZ = Math.Max(Math.Abs(vbx.Max.Z), Math.Abs(vbx.Min.Z)); var baseSize = Math.Max(halfX, Math.Max(halfY, halfZ)); return baseSize * 2f; } //更新视口 public void UpdateViewport(int width, int height) { var w = width < 1 ? 1 : width; var h = height < 1 ? 1 : height; _aspect = w / (float)h; GL.Viewport(0, 0, w, h); } /// /// 更新模型矩阵 /// public void UpdateModelMatrix() { var center = (_vbx.Min + _vbx.Max) * 0.5f; _modelMatrix = Matrix4.CreateTranslation(-center); } /// /// 更新视图矩阵 /// public void UpdateViewMatrix() { var center = (_vbx.Min + _vbx.Max) * 0.5f; var eye = center + new Vector3(0, 0, _baseSize + 0.1f); // 视图矩阵:将场景中心对齐到视口中心 _viewMatrix = Matrix4.LookAt( eye, // 摄像机位置 Vector3.Zero,// 观察目标 Vector3.UnitY); // 上方向 } //更新投影矩阵 public void UpdateProjectionMatrix() { float width = _baseSize; float height = _baseSize; if (_aspect > 1) { width = _baseSize * _aspect; } else { height = _baseSize / _aspect; } width *= this.Zoom; height *= this.Zoom; // 设置正交投影矩阵 _projectionMatrix = Matrix4.CreateOrthographic( width, height, 0.1f, // zNear _baseSize + 0.1f // zFar (包含模型高度) ); } #region 鼠标交互 private Vector2 _lastMousePos;//最后一次鼠标位置 private bool _isDragging = false;//是否增在拖动 private bool _isRotating = false;//是否正在旋转 /// /// 处理鼠标滚轮 /// public void HandleMouseWheel(GLControl gl, MouseEventArgs e) { this.Zoom *= e.Delta > 0 ? 0.9f : 1.1f; UpdateProjectionMatrix(); gl.Invalidate(); } /// /// 处理鼠标按下 /// public void HandleMouseDown(GLControl gl, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { _isDragging = true; _lastMousePos = new Vector2(e.X, e.Y); gl.Cursor = Cursors.SizeAll; // 修改光标样式 } else if (e.Button == MouseButtons.Left) { _isRotating = true; _lastMousePos = new Vector2(e.X, e.Y); gl.Cursor = Cursors.SizeAll; // 修改光标样式 } } /// /// 处理鼠标弹起 /// public void HandleMouseUp(GLControl gl, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { _isDragging = false; gl.Cursor = Cursors.Default; } else if (e.Button == MouseButtons.Left) { _isRotating = false; gl.Cursor = Cursors.Default; } } /// /// 处理鼠标移动 /// public void HandleMouseMove(GLControl gl, MouseEventArgs e) { if (_isDragging) { Vector2 currentPos = new Vector2(e.X, e.Y); Vector2 delta = currentPos - _lastMousePos; _lastMousePos = currentPos; // 计算平移量(Y轴方向需要反转) _translation.X += delta.X / this.Zoom; _translation.Y -= delta.Y / this.Zoom; gl.Invalidate(); } else if (_isRotating) { Vector2 currentPos = new Vector2(e.X, e.Y); Vector2 delta = currentPos - _lastMousePos; _lastMousePos = currentPos; _rotation.X -= delta.Y; _rotation.Y += delta.X; _vbx = TransformBoundsToViewSpace(_wbx); _baseSize = CalcuBaseSize(_vbx); // UpdateModelMatrix(); //UpdateViewMatrix(); //UpdateProjectionMatrix(); gl.Invalidate(); } } #endregion } }