CSharpGL(20)用unProject和Project实现鼠标拖拽图元
CSharpGL(20)用unProject和Project实现鼠标拖拽图元
效果图

例如,你可以把Big Dipper这个模型拽成下面这个样子。

配合旋转,还可以继续拖拽成这样。

当然,能拖拽的不只是线段。还可以拖拽三角形(如下图)、四边形。

另外,还可以单点拖拽。

2016-04-28
现在实现了高亮显示拾取、拖拽的图元的功能。




下面演示了鼠标移动到图元上时显示图元的索引值的功能。

起初会出现stitching和z-fighting的现象。例如下面选中一个三角形时,由于stitching问题,没高亮其斜边。

于是我添加了PolygonOffsetSwtich开关,解决了这个问题。

下载
CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
unProject/Project
这个两个函数的执行结果完全相反。拖拽功能全靠他们了。
Project把模型坐标系上的点转换为窗口坐标系上的点。这可以通过其实现代码来验证。
/// <summary>
/// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates.
/// </summary>
/// <param name="obj">The object.</param>
/// <param name="model">The model.</param>
/// <param name="proj">The proj.</param>
/// <param name="viewport">The viewport.</param>
/// <returns></returns>
public static vec3 project(vec3 obj, mat4 model, mat4 proj, vec4 viewport)
{
vec4 tmp = new vec4(obj, (1f));
tmp = model * tmp;
tmp = proj * tmp; tmp /= tmp.w;
tmp = tmp * 0.5f + new vec4(0.5f, 0.5f, 0.5f, 0.5f);
tmp[] = tmp[] * viewport[] + viewport[];
tmp[] = tmp[] * viewport[] + viewport[]; return new vec3(tmp.x, tmp.y, tmp.z);
}
通过试验发现,一个vec3经过Project后再经过unProject,会变回原来的值。这就是说,unProject把窗口坐标系上的点转换为模型坐标系上的点。
OpenGL是以窗口左下角为原点(0, 0)的。而Windows窗口是以左上角为原点的。所以用的时候要注意转换一下。
弄清楚了这两个函数,才能实现鼠标拖拽的功能。
拖拽原理
既然可以把模型空间的点转换为平面坐标系上的点,并且可以逆向操作。那么只需将要拖拽的点A通过project函数投影到屏幕上(变成a);根据鼠标在屏幕上的移动,相应的移动a,变成a',最后把a'通过unProject反射回模型空间,就是拖拽后的A'了。在VBO里,把A改为A'即可。
/// <summary>
/// 根据<paramref name="differenceOnScreen"/>来修改指定索引处的顶点位置。
/// </summary>
/// <param name="differenceOnScreen"></param>
/// <param name="viewMatrix"></param>
/// <param name="projectionMatrix"></param>
/// <param name="viewport"></param>
/// <param name="positionIndexes"></param>
public void MovePositions(Point differenceOnScreen,
mat4 viewMatrix, mat4 projectionMatrix, vec4 viewport, uint[] positionIndexes)
{
if (positionIndexes == null) { return; }
if (positionIndexes.Length == ) { return; } GL.BindBuffer(BufferTarget.ArrayBuffer, this.positionBufferPtr.BufferId);
IntPtr pointer = GL.MapBuffer(BufferTarget.ArrayBuffer, MapBufferAccess.ReadWrite);
unsafe
{
var array = (vec3*)pointer.ToPointer();
for (int i = ; i < positionIndexes.Length; i++)
{
vec3 projected = glm.project(array[positionIndexes[i]],
viewMatrix, projectionMatrix, viewport);
vec3 newProjected = new vec3(projected.x + differenceOnScreen.X,
projected.y + differenceOnScreen.Y, projected.z);
array[positionIndexes[i]]=glm.unProject(newProjected,
viewMatrix, projectionMatrix, viewport);
}
}
GL.UnmapBuffer(BufferTarget.ArrayBuffer);
GL.BindBuffer(BufferTarget.ArrayBuffer, );
}
MouseDown
鼠标按下时,如果拾取到图元,就要为拖拽做准备。(如果想了解拾取的原理,可参考CSharpGL(18)分别处理glDrawArrays()和glDrawElements()两种方式下的拾取(ColorCodedPicking))
private void glCanvas1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// move vertex
PickedGeometry pickedGeometry = RunPicking(e.X, e.Y);
if (pickedGeometry != null)
{
var dragParam = new DragParam(pickedGeometry,
camera.GetProjectionMat4(),
camera.GetViewMat4(),
new Point(e.X, glCanvas1.Height - e.Y - ));
this.dragParam = dragParam;
}
}
}
其中的RunPicking就是执行一次拾取操作。
private PickedGeometry RunPicking(int x, int y)
{
this.glCanvas1_OpenGLDraw(selectedModel, null);
IColorCodedPicking pickable = this.rendererDict[this.SelectedModel];
pickable.MVP = this.camera.GetProjectionMat4() * this.camera.GetViewMat4();
PickedGeometry pickedGeometry = ColorCodedPicking.Pick(
this.camera, x, y, this.glCanvas1.Width, this.glCanvas1.Height, pickable); return pickedGeometry;
}
这里有个dragParam类型,记录了按下后的一些数据。
class DragParam
{ public PickedGeometry pickedGeometry;
public mat4 projectionMatrix;
public mat4 viewMatrix;
public Point lastMousePositionOnScreen;
public vec4 viewport; public DragParam(PickedGeometry pickedGeometry, mat4 projectionMatrix, mat4 viewMatrix, Point lastMousePositionOnScreen)
{
this.pickedGeometry = pickedGeometry;
this.projectionMatrix = projectionMatrix;
this.viewMatrix = viewMatrix;
this.lastMousePositionOnScreen = lastMousePositionOnScreen;
var viewport = new int[]; GL.GetInteger(GetTarget.Viewport, viewport);
this.viewport = new vec4(viewport[], viewport[], viewport[], viewport[]);
}
}
MouseMove
鼠标开始移动后,就要实时更新模型顶点的位置了。
private void glCanvas1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// move vertex
DragParam dragParam = this.dragParam;
if (dragParam != null)
{
var current = new Point(e.X, glCanvas1.Height - e.Y - );
Point differenceOnScreen = new Point(
current.X - dragParam.lastMousePositionOnScreen.X,
current.Y - dragParam.lastMousePositionOnScreen.Y);
dragParam.lastMousePositionOnScreen = current;
this.rendererDict[this.selectedModel].MovePositions(
differenceOnScreen,
dragParam.viewMatrix, dragParam.projectionMatrix,
dragParam.viewport,
dragParam.pickedGeometry.Indexes);
}
}
}
MouseUp
鼠标抬起,清空数据,恢复状态。
private void glCanvas1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// move vertex
this.dragParam = null;
}
}
总结
本文虽然简单,但是我却花了好几天才解决拖拽的问题。过程中想过试过种种奇葩的方案。最后,在弄明白了project和unProject的功能后,立即想到了现在这个方案,既简单又实用。
所以说必须戒除浮躁和急切的心理,慢慢地搞清楚每一个小问题。这才是磨刀不误砍柴工。
原CSharpGL的其他功能(UI、3ds解析器、TTF2Bmp、CSSL等),我将逐步加入新CSharpGL。
欢迎对OpenGL有兴趣的同学关注(https://github.com/bitzhuwei/CSharpGL)
CSharpGL(20)用unProject和Project实现鼠标拖拽图元的更多相关文章
- 【狼】unity 鼠标拖拽物体实现任意角度自旋转
主要涉及函数 Input.GetAxis(“Mouse x”) 可取得鼠标横向(x轴)移动增量 Input.GetAxis(“Mouse y”) 可取得鼠标竖向(y轴)移动增量 通过勾股定理获取拖拽长 ...
- JavaScript鼠标拖拽特效及相关问题总结
#div1{width:200px;height:200px;background:red;position:absolute;} #div2{width:200px;height:200px;bac ...
- 一款基于jQuery的支持鼠标拖拽滑动焦点图
记得之前我们分享过一款jQuery全屏广告图片焦点图,图片切换效果还不错.今天我们要分享另外一款jQuery焦点图插件,它的特点是支持鼠标拖拽滑动,所以在移动设备上使用更加方便,你只要用手指滑动屏幕即 ...
- NGUI对象跟随鼠标拖拽移动
public Camera WNGUICamera; Vector3 _WoldPosition;//指针的初始位置 // Vector3 _WoldAng; Vector3 WscreenSpace ...
- 鼠标拖拽定位和DOM各种尺寸详解
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Arcgis for qml - 鼠标拖拽移动
以实现鼠标拖拽文本图层为例 GitHub:ArcGIS拖拽文本 作者:狐狸家的鱼 目的是利用鼠标进行拖拽. 实现两种模式,一种是屏幕上的拖拽,第二种是地图上图层的挪动. 屏幕上的拖拽其实跟ArcGIS ...
- html5的鼠标拖拽
鼠标拖拽 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...
- javascript鼠标拖拽的那些事情
<html> <head> <title>javascript鼠标拖拽的那些事情</title> <meta http-equiv="C ...
- 设置zedgraph鼠标拖拽和局部放大属性(转帖)
说一下几个属性的意义和具体应用: (1)鼠标拖拽显示区域 PanModifierKeys ->> Gets or sets a value that determines which mo ...
随机推荐
- Mono为何能跨平台?聊聊CIL(MSIL)
前言: 其实小匹夫在U3D的开发中一直对U3D的跨平台能力很好奇.到底是什么原理使得U3D可以跨平台呢?后来发现了Mono的作用,并进一步了解到了CIL的存在.所以,作为一个对Unity3D跨平台能力 ...
- 基于DDD的现代ASP.NET开发框架--ABP系列文章总目录
ABP相关岗位招聘:给热爱.NET新技术和ABP框架的朋友带来一个高薪的工作机会 ABP交流会录像视频:ABP架构设计交流群-7月18日上海线下交流会的内容分享(有高清录像视频的链接) 代码自动生成: ...
- Redis/HBase/Tair比较
KV系统对比表 对比维度 Redis Redis Cluster Medis Hbase Tair 访问模式 支持Value大小 理论上不超过1GB(建议不超过1MB) 理论上可配置(默认配置1 ...
- 23种设计模式--工厂模式-Factory Pattern
一.工厂模式的介绍 工厂模式让我们相到的就是工厂,那么生活中的工厂是生产产品的,在代码中的工厂是生产实例的,在直白一点就是生产实例的类,代码中我们常用new关键字,那么这个new出来的实例 ...
- 23种设计模式--中介者模式-Mediator Pattern
一.中介者模式的介绍 中介者模式第一下想到的就是中介,房子中介,婚姻中介啊等等,当然笔者也希望来个婚姻中介给我介绍一个哈哈哈,,回归正题中介者模式分成中介者类和用户类,根据接口编程的方式我们再 ...
- 开源一个跨平台运行的服务插件 - TaskCore.MainForm
本次将要很大家分享的是一个跨平台运行的服务插件 - TaskCore.MainForm,此框架是使用.netcore来写的,现在netcore已经支持很多系统平台运行了,所以将以前的Task.Main ...
- 自定义搭建PHP开发环境
学习了一段时间php了,因为之前是刚接触php,所以用的是集成安装包(wamp).现在想进一步了解apache.mysql.php之间的关系以及提升自己所以进行自定义搭建PHP开发环境.废话不多说,请 ...
- 微信网页开发之获取用户unionID的两种方法--基于微信的多点登录用户识别
假设网站A有以下功能需求:1,pc端微信扫码登录:2,微信浏览器中的静默登录功能需求,这两种需求就需要用到用户的unionID,这样才能在多个登录点(终端)识别用户.那么这两种需求下用户的unionI ...
- 显示本地openssl支持的加密算法
参考页面: http://www.yuanjiaocheng.net/webapi/parameter-binding.html http://www.yuanjiaocheng.net/webapi ...
- 解决“chrome提示adobe flash player 已经过期”的小问题
这个小问题也确实困扰我许久,后来看到chrome吧里面有人给出了解决方案: 安装install_flash_player_ppapi, 该软件下载地址:http://labs.adobe.com/do ...