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
}
}