65.9K
CodeProject 正在变化。 阅读更多。
Home

CsGL 中的“Paint.exe”

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.91/5 (9投票s)

2003年12月17日

7分钟阅读

viewsIcon

138145

downloadIcon

4331

使用 C# 和 CsGL 模仿 Windows Paint.exe

屏幕截图显示矩形绘图对象被选中

引言

许多 OpenGL 示例都以显示图形为导向,但由于这个项目模仿了 Windows 画图应用程序,因此与许多其他示例相比,用户交互更多,并且让 opengl 对象选择工作起来可能很痛苦。我的目的是更好地学习 OpenGL,C# 的 CsGL 库非常好,示例也非常有帮助。现在看来 CsGL 已经被 Tao 取代了,所以这篇文章已经过时了!我绝不是 OpenGL 大师,所以如果你是游戏开发者,这篇文章对你来说太低级了,但如果你在如何让贝塞尔曲线工作等方面遇到了困难,那么这可能会对你有所帮助。

使用代码

我一直在关注 Sourceforge 上 Randy Ridge 的一些示例,并根据他的表单/视图模式设置了许多小的 Windows 演示应用程序,这样我就列出了一个每次都会遵循的小清单

我们目前正在遵循的设置 Windows CS GL 应用程序的步骤

  1. 开始一个 Winforms 应用程序
  2. 添加对 winnt/system32/csgl.dll 的项目引用
  3. 添加一个类 – myView,并从 OpenGLControl 继承
  4. 添加所需的“#using”行
  5. 从点(或此)示例中复制必要的子例程

在 Windows 应用程序中显示 CS OpenGL 可能有更好的方法,但一旦它看起来工作良好,我就一直懒于尝试改进它。欢迎任何其他想法。

参考:我相信我从示例集中的点演示示例中获得了 GL 视图。

此外,看起来引用 dll 会导致 csgl.dll 被复制到 bin/release 目录。目前,如果 dll 不在 exe 所在的目录中,我不知道如何运行程序。

OpenGL 对象集合的集合:对于我想做的事情,开发一些共享一些方法但在其特定几何形状(线、矩形、椭圆、自由曲线)上有所不同的绘图对象是有意义的。但是 DrawingObjects 不是纯几何类,也不是 OpenGL 类;它们也继承自 Windows.Forms。例如,一旦某个矩形成为焦点,它处理所有与其绘制的左上角和右下角点相关的鼠标事件似乎最有意义。一旦构建了一个绘图对象,它就会被插入到一个哈希表中。

速度/结构 - 我不是游戏开发者,我想知道一些游戏是否像这样使用存储在哈希表中的自绘对象集合。我对这种结构有些疑虑,因为目前我正在每个绘制周期中遍历所有绘图对象,无论它们是否被修改。我感觉这种结构在一百个绘图对象之后会变得笨拙,我正在考虑将 OpenGL 绘图列表作为解决方案。

视图与表单通信的“虫洞” - 我想在表单类和视图类之间保持清晰的边界,但由于我模仿了 Windows 画图程序,并且它们在表单上显示当前的鼠标位置,这让我感到困惑,因为只有视图知道鼠标在哪里,而视图是表单的子级。在“正常”Win32 中,我可能会定义一个 Windows 消息并在消息中将鼠标位置发送到顶层窗口。我不确定我使用的方法是否是 C# 特性 - 我认为你不能在 C++/MFC 中做这种事情。也许它之所以有效是因为我的所有类都在同一个命名空间中,这让我很感兴趣,因为在 MFC Windows 中,我经常从顶层窗口开始,然后向下到我需要的类或控件,但在这里我能够从较低级别向上工作到较高级别。

private void SendMousePositionToParent(System.Windows.Forms.MouseEventArgs e)
{
    int X = e.X - this.iLeftOffset; 
       // the left position needs to print as 0
    int Y = e.Y;
    ((drawForm)this.Parent).ReceiveViewInfo(X , Y , strType);
       // worm hole to parent
}

关注点

我们在代码中学到但未在本文中包含的一些内容包括:更改光标、光标热点、图标。

贝塞尔曲线 - 我只想绘制由矩形限定的椭圆,但我最初几次尝试贝塞尔曲线让我有点困惑。一旦我让它们工作,我就把所有贝塞尔曲线的东西都放到了 Curves 类中,在那里我再也不用考虑它们了。也许这个类可以帮助那些正在为基本语法而苦恼的人。

特别是,对 glMap1d 的调用其中一个参数是指向数组的指针,在 C# 中,你不能在“不安全代码”之外使用指针,你可以查看子例程以了解它是如何完成的。你还必须转到“项目/属性/配置属性/生成/允许不安全代码块”并将其设置为“true”。如果你打算使用它的话。

private unsafe void BezierDrawLine( double [,] inPoints ) 

错误解释 - 当我们几乎放弃使用 CSGL 选择对象时,我们退后一步,深吸一口气,决定尝试收集一些更好的数据。当你遇到一个真正的、活生生的、复杂的 openGL 错误时,你不会得到太多的消息 - 它只会崩溃。幸运的是,它会发​​出一个可以捕获和解释的整数。此外,所有的错误总是属于我 - 而不是 csgl 库。将来我可能会考虑一个用于跟踪的库的调试版本,但目前,我正在使用以下子例程来解释错误

// **************************************************
// pretty print the error
// http://www.opengl.org/documentation/blue_book_1.0/book/ch05.html#id5488181
void PrintGLError( uint err, String from)
{
    if ( err == GL.GL_NO_ERROR ) return;    // don't print
    if ( from != "" ) System.Console.WriteLine("GL Error: " + from);
    PrintGLError( err);
}

    void PrintGLError( uint err)
{
    String pretty = "";

    switch ( err )
    {
        case GL.GL_NO_ERROR:
            pretty = "GL_NO_ERROR ";
            pretty = pretty + "No error has been recorded."+
               " The value of this symbolic constant is "+
               "guaranteed to be zero. ";
            break;
        case GL.GL_INVALID_ENUM:
            pretty = "GL_INVALID_ENUM ";
            pretty = pretty + "An unacceptable value is specified for "+
                   "an enumerated argument. The offending command"+
                   " is ignored, having no side effect other than"+
                   " to set the error flag ";
            break;
        case GL.GL_INVALID_VALUE:
            pretty = "GL_INVALID_VALUE ";
            pretty = pretty + "A numeric argument is out of range. "+
                "The offending command is ignored, having no side "+
                "effect other than to set the error flag.  ";
            break;
        case GL.GL_INVALID_OPERATION:
            pretty = "GL_INVALID_OPERATION ";
            pretty = pretty + "The specified operation is not "+
                   "allowed in the current state. The offending command "+
                   "is ignored, having no side effect other than "+
                   "to set the error flag.  ";
            break;
        case GL.GL_STACK_OVERFLOW:
            pretty = "GL_STACK_OVERFLOW ";
            pretty = pretty + "This command would cause a stack overflow."+
                " The offending command is ignored, having no side"+
                " effect other than to set the error flag.  ";
            break;
        case GL.GL_STACK_UNDERFLOW:
            pretty = "GL_STACK_UNDERFLOW ";
            pretty = pretty + "This command would cause a stack "+
                   "underflow. The offending command is ignored, "+
                   "having no side effect other than to set "+
                   "the error flag.  ";
            break;
        case GL.GL_OUT_OF_MEMORY:
            pretty = "GL_OUT_OF_MEMORY ";
            pretty = pretty + "There is not enough memory left to "+
                    "execute the command. The state of the GL is "+
                    "undefined, except for the state of the error "+
                    "flags, after this error is recorded.  ";
            break;
    }
System.Console.WriteLine("GL Error: " + pretty);
}

// how to call it:
PrintGLError( GL.glGetError() , "Render");

OnMouseHover 正常工作 - 我们希望悬停事件对视图中显示的每个绘图对象都有效。通常,悬停事件与控件或视图本身相关联,并且当鼠标悬停在视图的某个位置时只触发一次。一旦触发,鼠标必须离开视图才能再次触发悬停事件。为了模拟每个绘图对象的悬停行为,我们在视图中使用 TrackMouseEvent 触发一次后将其重新打开。这似乎是一个如此模糊的函数,可能我试图实现的效果不是 Windows 的方式,但这里有一个 MSDN 描述它的链接。http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/mouseinput/ mouseinputreference/mouseinputfunctions/trackmouseevent.asp

正确选择 - 视图中“选择”子例程的目的是将 x,y 鼠标坐标转换为 OpenGL 对象。我的 DrawingObjects 都是 OpenGL 线的集合,每条线都由一个 uint ID 标识。因此,例如,在自由形式的 PencilDrawingObject 中,可能存在数百条单独的线,但由于它们都具有相同的 ID,因此当鼠标悬停在任何一条线上时,我们可以使用 OpenGL 选择过程来检索 ID。然后我们想更改所选对象的颜色。我必须说,尽管我怀着这个意图编写了代码,但当其中一条弯曲的线第一次以其高亮颜色出现时,感觉几乎是神奇的。

关于选择过程如何工作的超级资源由“cornflake”提供:(点击此处)

在让选择功能工作的过程中,我几天都只遇到一些模糊的错误。我不得不让一些更简单的代码工作,以重新获得我对 CSGL 的信心。我正在包含我重做为 Windows 应用程序的 NeHe 矩形拾取示例。(Randy Ridge 的 NeHe 示例生成 Dos 应用程序,并且不是用 Visual Studio 完成的)。一旦我恢复了信心,并对所有可能产生错误的代码段进行了检测,我发现我最严重的错误是在“绘图”模式下使用整数绘制线条,而在“选择”模式下调用渲染子例程时使用浮点数。OpenGL 不喜欢那样。

图标 - 我只想更改系统托盘图标,但我遇到了不必要的困难。以下代码很丑陋,但它奏效了。致谢在注释中。

// how we got the icon to work, finally
//1) 16x16 bitmap
//2) in correct folder, or specify full path
// this helped - http://www.csharphelp.com/archives2/archive427.html
//System.IO.Path.d
// Convert the bitmap into an icon and use it for the system tray icon
Bitmap bitmap = new Bitmap( "..\\..\\Graphics\\pencil16.bmp");
IntPtr hIcon = bitmap.GetHicon();
Icon icon = Icon.FromHandle(hIcon);
//badIcon icon = new Icon("app.ico");
//notifyIcon1.Icon = icon;

this.Icon = icon;        // set form icon

回想起来,更改图标的最佳位置似乎是项目属性,而不是上面提到的方法。我当时只是没看到。

csgl.dll - 必须从SourceForge下载。

历史

  • 2003年12月16日首次上传
© . All rights reserved.