using System;
using System.Drawing;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using System.Windows.Forms;
using System.Drawing.Imaging;
using OpenTK.Mathematics;
using OpenTK.GLControl;
using System.Drawing.Text;
using System.Security.Policy;
namespace Yw.WinFrmUI.Test.Core
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.glControl1.Load += GlControl1_Load;
this.glControl1.Paint += GlControl1_Paint;
this.glControl1.Resize += GlControl1_Resize;
this.glControl1.MouseDown += GlControl1_MouseDown;
this.glControl1.MouseUp += GlControl1_MouseUp;
this.glControl1.MouseWheel += GlControl1_MouseWheel;
this.glControl1.MouseMove += GlControl1_MouseMove;
}
private void GlControl1_MouseMove(object sender, MouseEventArgs e)
{
HandleMouseMove(e);
}
private void GlControl1_MouseWheel(object sender, MouseEventArgs e)
{
HandleMouseWheel(e);
}
private void GlControl1_MouseUp(object sender, MouseEventArgs e)
{
HandleMouseUp(e);
}
private void GlControl1_MouseDown(object sender, MouseEventArgs e)
{
HandleMouseDown(e);
}
private void GlControl1_Resize(object sender, EventArgs e)
{
OnResize();
}
private void GlControl1_Paint(object sender, PaintEventArgs e)
{
OnRender();
}
private void GlControl1_Load(object sender, EventArgs e)
{
InitialGLState();
}
#region 字段声明
private const float _size = 100f;
private Matrix4 _modelMatrix = Matrix4.Identity;
private Matrix4 _viewMatrix = Matrix4.Identity;
private Matrix4 _projMatrix = Matrix4.Identity;
private float _rotationX, _rotationY;
private float _translationX, _translationY;
private float _zoom = 1f;
private bool _isDragging, _isRotating;
private Point _lastMousePos;
private float _radius = 100f;
private float _pw, _ph;
private Vector3 _mc = new Vector3(0f);
private Vector3 _eye, _target, _up;
private readonly int[] _textures = new int[6];
private float _screenWidth, _screenHeight;
public float Zoom
{
get => _zoom;
set => _zoom = MathHelper.Clamp(value, 0.01f, 100.0f); // 限制缩放范围
}
#endregion
#region OpenGL初始化
//初始化GL状态
private void InitialGLState()
{
GL.ClearColor(Color.Transparent);
GL.Enable(EnableCap.DepthTest);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
// 生成六个面的文字纹理
_textures[0] = CreateTextTexture("前", 128, Color.White, Color.Red);
_textures[1] = CreateTextTexture("后", 128, Color.White, Color.Green);
_textures[2] = CreateTextTexture("左", 128, Color.White, Color.Blue);
_textures[3] = CreateTextTexture("右", 128, Color.White, Color.Yellow);
_textures[4] = CreateTextTexture("上", 128, Color.White, Color.Magenta);
_textures[5] = CreateTextTexture("下", 128, Color.White, Color.Cyan);
}
#endregion
#region 矩阵更新
private void UpdateMatrices()
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
UpdateViewport();
UpdateProjection();
UpdateView();
UpdateModel();
}
//更新视口
private void UpdateViewport()
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
_screenWidth = this.glControl1.Width;
_screenHeight = this.glControl1.Height;
GL.Viewport(0, 0, this.glControl1.Width, this.glControl1.Height);
}
private void UpdateProjection()
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
// 获取窗口尺寸
int screenWidth = ClientSize.Width;
int screenHeight = ClientSize.Height;
// 设置中心对齐的正交投影
Matrix4 projection = Matrix4.CreateOrthographicOffCenter(
left: -screenWidth / 2f,
right: screenWidth / 2f,
bottom: -screenHeight / 2f,
top: screenHeight / 2f,
depthNear: -1,
depthFar: 1
);
_projMatrix = projection;
//var aspect = this.glControl1.AspectRatio;
//var size = _radius * 2;
//_pw = size;
//_ph = size;
//if (aspect > 1)
//{
// _pw *= aspect;
//}
//else
//{
// _ph /= aspect;
//}
//_projMatrix = Matrix4.CreateOrthographic(_pw * this.Zoom, _ph * this.Zoom, _radius, _radius * 3);
}
private void UpdateView()
{
_eye = new Vector3(_mc.X, _mc.Y, _mc.Z + 2 * _radius);
_target = _mc;
_up = Vector3.UnitY;
//var rotation = Quaternion.Identity;
//var rotationX = Quaternion.FromAxisAngle(Vector3.UnitX, _rotationX);
//var rotationY = Quaternion.FromAxisAngle(Vector3.UnitY, _rotationY);
// rotation = rotationY * rotationX * rotation;
//rotation.Normalize();
// var matrix = Matrix4.CreateFromQuaternion(rotation);
// matrix.Invert();
// eye = Vector3.TransformPosition(eye, matrix);
// up = Vector3.TransformPosition(up, matrix);
_viewMatrix = Matrix4.LookAt(_eye, _target, _up);
}
private void UpdateModel()
{
_modelMatrix = Matrix4.CreateTranslation(new Vector3(_pw / 2f * this.Zoom, _ph / 2f * this.Zoom, 0)) *
Matrix4.CreateFromAxisAngle(Vector3.UnitX, _rotationX) *
Matrix4.CreateFromAxisAngle(Vector3.UnitY, _rotationY) *
Matrix4.CreateTranslation(-new Vector3(_pw / 2f * this.Zoom, _ph / 2f * this.Zoom, 0)) *
Matrix4.CreateTranslation(_translationX * this.Zoom, _translationY * this.Zoom, 0f);
}
#endregion
#region 渲染逻辑
private void OnRender()
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
DrawReferenceLines();
DrawRotatingObject();
//SwapBuffers();
//this.glControl1.MakeCurrent();
//GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
//// 设置矩阵
//GL.MatrixMode(MatrixMode.Projection);
//GL.LoadMatrix(ref _projMatrix);
//GL.MatrixMode(MatrixMode.Modelview);
//Matrix4 modelView = _viewMatrix * _modelMatrix;
//GL.LoadMatrix(ref modelView);
//// 绘制立方体
////DrawCube();
//// 绘制立方体
//DrawTexturedCube();
//var error = GL.GetError();
//// 绘制标签
////DrawLabels();
//this.glControl1.SwapBuffers();
}
float rotationAngle = 45; // 测试角度
void DrawRotatingObject()
{
GL.PushMatrix();
// 关键变换顺序
GL.Translate(100, 50, 0); // 将物体移动到屏幕坐标系中的(100,50)
GL.Rotate(rotationAngle, Vector3.UnitZ); // 绕当前原点(屏幕中心)旋转
// 绘制以原点为中心的物体
GL.Color3(Color.Red);
GL.Begin(PrimitiveType.Quads);
GL.Vertex2(-25, -25);
GL.Vertex2(25, -25);
GL.Vertex2(25, 25);
GL.Vertex2(-25, 25);
GL.End();
GL.PopMatrix();
}
void DrawReferenceLines()
{
//// 绘制屏幕中心十字
//GL.Color3(Color.White);
//GL.LineWidth(2);
//GL.Begin(PrimitiveType.Lines);
//// 水平线
//GL.Vertex2(-1000, 0); GL.Vertex2(1000, 0);
//// 垂直线
//GL.Vertex2(0, -1000); GL.Vertex2(0, 1000);
//GL.End();
//// 绘制屏幕边框
//GL.Color3(Color.Gray);
//GL.Begin(PrimitiveType.LineLoop);
//GL.Vertex2(-screenWidth / 2, -screenHeight / 2);
//GL.Vertex2(screenWidth / 2, -screenHeight / 2);
//GL.Vertex2(screenWidth / 2, screenHeight / 2);
//GL.Vertex2(-screenWidth / 2, screenHeight / 2);
//GL.End();
}
//改变
private void OnResize()
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
this.glControl1.MakeCurrent();
// 防止OpenGL错误
if (this.glControl1.Width == 0)
{
this.glControl1.Width = 1;
}
if (this.glControl1.Height == 0)
{
this.glControl1.Height = 1;
}
// 更新所有矩阵
UpdateMatrices();
// 请求重绘
this.glControl1.Invalidate();
}
private void DrawCube()
{
GL.Begin(PrimitiveType.Quads);
// 前面(Z+)
//GL.Color3(Color.Purple);
GL.Color4((float)Color.Purple.R, (float)Color.Purple.G, (float)Color.Purple.B, 1f);
GL.Vertex3(-50f, -50f, 50f);
GL.Vertex3(50f, -50f, 50f);
GL.Vertex3(50f, 50f, 50f);
//GL.Vertex3(50f, 50f, 50f);
GL.Vertex3(-50f, 50f, 50f);
// GL.Vertex3(-50f, -50f, 50f);
// 后面(Z-)
//GL.Color3(Color.Cyan);
GL.Color4((float)Color.Cyan.R, (float)Color.Cyan.G, (float)Color.Cyan.B, 1f);
GL.Vertex3(-50f, -50f, -50f);
GL.Vertex3(-50f, 50f, -50f);
GL.Vertex3(50f, 50f, -50f);
//GL.Vertex3(50f, 50f, -50f);
GL.Vertex3(50f, -50f, -50f);
//GL.Vertex3(-50f, -50f, -50f);
// 顶面(Y+)
//GL.Color3(Color.Lime);
GL.Color4((float)Color.Lime.R, (float)Color.Lime.G, (float)Color.Lime.B, 1f);
GL.Vertex3(-50f, 50f, -50f);
GL.Vertex3(50f, 50f, -50f);
GL.Vertex3(50f, 50f, 50f);
//GL.Vertex3(50f, 50f, 50f);
GL.Vertex3(-50f, 50f, 50f);
//GL.Vertex3(-50f, 50f, -50f);
DrawTextOnFace("上", new Vector3(0, 50f, 0), new Vector3(0, 0, 100f));
// 底面(Y-)
//GL.Color3(Color.Yellow);
GL.Color4((float)Color.Yellow.R, (float)Color.Yellow.G, (float)Color.Yellow.B, 1f);
GL.Vertex3(-50f, -50f, -50f);
GL.Vertex3(50f, -50f, -50f);
GL.Vertex3(50f, -50f, 50f);
//GL.Vertex3(50f, -50f, 50f);
GL.Vertex3(-50f, -50f, 50f);
//GL.Vertex3(-50f, -50f, -50f);
// 右面(X+)
//GL.Color3(Color.Red);
GL.Color4((float)Color.Red.R, (float)Color.Red.G, (float)Color.Red.B, 1f);
GL.Vertex3(50f, -50f, -50f);
GL.Vertex3(50f, 50f, -50f);
GL.Vertex3(50f, 50f, 50f);
// GL.Vertex3(50f, 50f, 50f);
GL.Vertex3(50f, -50f, 50f);
//GL.Vertex3(50f, -50f, -50f);
// 左面(X-)
GL.Color4((float)Color.Blue.R, (float)Color.Blue.G, (float)Color.Blue.B, 1f);
//GL.Color3(Color.Blue);
GL.Vertex3(-50f, -50f, -50f);
GL.Vertex3(-50f, 50f, -50f);
GL.Vertex3(-50f, 50f, 50f);
//GL.Vertex3(-50f, 50f, 50f);
GL.Vertex3(-50f, -50f, 50f);
//GL.Vertex3(-50f, -50f, -50f);
GL.End();
GL.End();
}
private void DrawFace(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Color4 color, string text)
{
GL.Begin(PrimitiveType.Quads);
GL.Color4(color);
GL.Vertex3(v1);
GL.Vertex3(v2);
GL.Vertex3(v3);
GL.Vertex3(v4);
GL.End();
Vector3 center = (v1 + v2 + v3 + v4) / 4;
Vector3 normal = Vector3.Cross(v2 - v1, v3 - v1);
DrawTextOnFace(text, center, normal);
}
private void DrawTextOnFace(string text, Vector3 position, Vector3 normal)
{
//GL.PushMatrix();
//GL.Translate(position);
//Vector3 up = Vector3.UnitY;
//Vector3 right = Vector3.Cross(normal, up);
// up = Vector3.Cross(right, normal);
// Matrix4 rotation = new Matrix4(
// new Vector4(right, 0),
// new Vector4(up, 0),
// new Vector4(-normal, 0),
// new Vector4(0, 0, 0, 1));
//GL.MultMatrix(ref rotation);
using (Bitmap bmp = new Bitmap(100, 100))
using (Graphics g = Graphics.FromImage(bmp))
{
g.TextRenderingHint = TextRenderingHint.AntiAlias;
Font font = new Font("Arial", 12);
SizeF textSize = g.MeasureString(text, font);
float xOffset = (bmp.Width - textSize.Width) / 2;
float yOffset = (bmp.Height - textSize.Height) / 2;
g.DrawString(text, font, Brushes.Black, new PointF(xOffset, yOffset));
int textureId = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, textureId);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height,
0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.Enable(EnableCap.Texture2D);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0, 0); GL.Vertex3(-10, -10, 0);
GL.TexCoord2(100, 0); GL.Vertex3(10, -10, 0);
GL.TexCoord2(100, 100); GL.Vertex3(10, 10, 0);
GL.TexCoord2(0, 100); GL.Vertex3(-10, 10, 0);
GL.End();
GL.Disable(EnableCap.Texture2D);
GL.DeleteTexture(textureId);
}
// GL.PopMatrix();
}
private int CreateTextTexture(string text, int size, Color textColor, Color bgColor)
{
// 创建位图并绘制文字
using (var bmp = new Bitmap(size, size))
using (var gfx = Graphics.FromImage(bmp))
{
gfx.Clear(bgColor);
var font = new Font("宋体", 32, FontStyle.Bold);
var format = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
gfx.DrawString(text, font, new SolidBrush(textColor),
new RectangleF(0, 0, size, size), format);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
// 转换位图为OpenGL纹理
var data = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
int texId = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, texId);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.Rgba,
data.Width, data.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Bgra,
PixelType.UnsignedByte, data.Scan0);
// 设置纹理参数
GL.TexParameter(TextureTarget.Texture2D,
TextureParameterName.TextureMinFilter,
(int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D,
TextureParameterName.TextureMagFilter,
(int)TextureMagFilter.Linear);
bmp.UnlockBits(data);
return texId;
}
}
private void DrawTexturedCube()
{
// 前面 (Z+)
DrawFace(_textures[0], new[]
{
new Vector3(-50, -50, 50),
new Vector3(50, -50, 50),
new Vector3(50, 50, 50),
new Vector3(-50, 50, 50)
});
// 后面 (Z-)
DrawFace(_textures[1], new[]
{
new Vector3(50, -50, -50),
new Vector3(-50, -50, -50),
new Vector3(-50, 50, -50),
new Vector3(50, 50, -50)
});
// 左面 (X-)
DrawFace(_textures[2], new[]
{
new Vector3(-50, -50, -50),
new Vector3(-50, -50, 50),
new Vector3(-50, 50, 50),
new Vector3(-50, 50, -50)
});
// 右面 (X+)
DrawFace(_textures[3], new[]
{
new Vector3(50, -50, 50),
new Vector3(50, -50, -50),
new Vector3(50, 50, -50),
new Vector3(50, 50, 50)
});
// 顶面 (Y+)
DrawFace(_textures[4], new[]
{
new Vector3(-50, 50, 50),
new Vector3(50, 50, 50),
new Vector3(50, 50, -50),
new Vector3(-50, 50, -50)
});
// 底面 (Y-)
DrawFace(_textures[5], new[]
{
new Vector3(-50, -50, -50),
new Vector3(50, -50, -50),
new Vector3(50, -50, 50),
new Vector3(-50, -50, 50)
});
}
private void DrawFace(int textureId, Vector3[] vertices)
{
GL.BindTexture(TextureTarget.Texture2D, textureId);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0, 0); GL.Vertex3(vertices[0]);
GL.TexCoord2(1, 0); GL.Vertex3(vertices[1]);
GL.TexCoord2(1, 1); GL.Vertex3(vertices[2]);
GL.TexCoord2(0, 1); GL.Vertex3(vertices[3]);
// 修正后的纹理坐标(翻转Y方向)
//GL.TexCoord2(0, 1); GL.Vertex3(vertices[0]); // 左上→左下
//GL.TexCoord2(1, 1); GL.Vertex3(vertices[1]); // 右上→右下
//GL.TexCoord2(1, 0); GL.Vertex3(vertices[2]); // 右下→右上
//GL.TexCoord2(0, 0); GL.Vertex3(vertices[3]); // 左下→左上
GL.End();
}
#endregion
#region 交互处理
///
/// 处理鼠标滚轮
///
public void HandleMouseWheel(MouseEventArgs e)
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
this.Zoom *= e.Delta > 0 ? 0.9f : 1.1f;
UpdateMatrices();
this.glControl1.Invalidate();
}
///
/// 处理鼠标按下
///
public void HandleMouseDown(MouseEventArgs e)
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
_lastMousePos = e.Location;
if (e.Button == MouseButtons.Right)
{
_isDragging = true;
this.glControl1.Cursor = Cursors.SizeAll; // 修改光标样式
}
else if (e.Button == MouseButtons.Left)
{
_isRotating = true;
this.glControl1.Cursor = Cursors.SizeAll; // 修改光标样式
}
}
///
/// 处理鼠标弹起
///
public void HandleMouseUp(MouseEventArgs e)
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
_lastMousePos = e.Location;
if (e.Button == MouseButtons.Right)
{
_isDragging = false;
this.glControl1.Cursor = Cursors.Default;
}
else if (e.Button == MouseButtons.Left)
{
_isRotating = false;
this.glControl1.Cursor = Cursors.Default;
}
this.glControl1.Invalidate();
}
///
/// 处理鼠标移动
///
public void HandleMouseMove(MouseEventArgs e)
{
if (!this.glControl1.IsHandleCreated)
{
return;
}
float dx = e.X - _lastMousePos.X;
float dy = e.Y - _lastMousePos.Y;
if (_isRotating)
{
// 根据鼠标移动量计算旋转角度
var roation_dx = dy / _radius;
var rotaion_dy = dx / _radius;
_rotationX += roation_dx;
_rotationY += rotaion_dy;
UpdateMatrices();
}
else if (_isDragging)
{
float xratio = _pw / this.glControl1.Width;
float yratio = _ph / this.glControl1.Height;
_translationX += dx * xratio;
_translationY -= dy * yratio;
UpdateMatrices();
}
_lastMousePos = e.Location;
this.glControl1.Invalidate();
}
#endregion
}
}