选择显示字体大小

做一个opengl控件

p>   opengl是一个独立于窗口的图形库,而图形最终是在窗口系统里绘制出来的,那么opengl的绘图命令是怎么在窗口里生成输出的呢?
  这就是各个系统上的opengl实现者需要做的工作了。在windows里是通过wgl库完成的,在x-windows里是通过glx服务器来完成的,至于这些opengl实现具体是怎么工作的,请参考sgi发布的sample implement源码,不过那个代码是用c写的。
  在ms-windows里,wgl库负责将opengl的绘制设备rendercontext与gdi的devicecontext联系起来,使得发到opengl的rc里的命令生成的位图能够在gdi dc里绘制出来,你可以把它想象成opengl在rc里有一个framebuffer,记录着生成的图案,而wgl则负责把framebuffer的内容bitblt到dc上。当然,这并不是它实际的工作方法,如果想了解更多请参考sgi发布的sdk资料或联系ms公司。
  为了使gdi dc能够接受opengl rc的输出,必须为dc选定特别的像素格式,然后建立rc,再用wglmakecurrent把当前要使用的rc和dc联系起来。此后我们就可以用opengl命令正常工作了。在一个程序里可以创建多个rc和多个dc,程序中的opengl命令会发到被wglmakecurrent指定为当前的那一组合中。
  我并不认为这个初始化过程是个很有意思的工作,这个世界上有很多聪明的程序员也这么想,所以他们发明了glaux库和glut库。glaux是在著名的opengl programmer guide里提出的,这本书是opengl编程的官方文档,因为它的封皮是红色的,所以通常简称为redbook。故名思意,glaux是一套输助库,它使得你无须关心在具体窗口系统里初始化、消息响应的细节,而是使用传统的c/dos程序风格编制opengl程序。



int main(int argc, char** argv)
{ auxinitdisplaymode( aux_singleaux_rgbaux_depth16);//使用单缓冲、rgb彩色模式、16位浓度
auxinitposition(0,0,250,250);
auxinitwindow("title");//以上两行在(0,0)片建立了一个大小为250x250的窗口,其标题为"title"。
myinit();//建立opengl透视投影环境
auxreshapefunc(myreshape);//指定窗口大小变化的响应函数
auxmainloop(display);//指定绘制函数
return 0; }


  由于glaux是为教学目的开发的,所以实用价值很限,所以又有程序员开发了glut,这套库被广泛使用,它的工作方式与glaux极为类似,但功能完善得多,特别是对交互、全屏等的支持要理想得多,所以许多的opengl演示程序使用它,比如sgi网站 峁┑亩嗍菔境绦蚨夹枰褂盟m闭馓卓庖丫灰浦驳蕉嘀制教ㄉ希砸窍胗眉虻サ姆椒ǹ⒃趙indows/macos/os2/xwindows等系统上都能使用的程序,那么应该选择这套库。
  我并不认为一个delphi程序员会喜欢glaux或glut,因为那意味着你不能利用delphi的可视开发能力,另外任何真正实用的delphi程序想直接在其它操作系统上编译运行好象也不现实,即glut的跨平台能力也没有什么吸引力。我们应该开发一个vcl控件,把初始化工作封装起来。
  我认为从tcustompanel派生一个子类比较方便,让我们称它为tglpanel吧。初始化过程要在wmcreate里完成,之所以要放在这里是因为这个时候window handle已经建立,但还没启用。
  在wmcreate中会
  ①调用initdc完成dc调整工作,initdc会以本窗口使用的dc调用preparepixelformat,而pixelformat则真正完成像素格式调整。
  ②然后wmcreate会调用initgl完成opengl透视投影环境的设定。
  ③最后调用oninit给用户一个调整透视投影环境的机会。
注意,如果要在mdi环境中的子窗体中使用opengl,还有些附加工作要做,这就是给窗口类的params.style加上ws_clipchildren和ws_clipsiblings属性,这得在window handle建立之前就完成,因此要写在createparams里。由于sdi应用并不需要该代码,所以应该定义onpreinit事件,让用户在需要的时候自己加上,在create里调用onpreinit。以下代码定义了onpreinit,但并没有定义createparams,如果需要自己加上吧。
  在tglpanel类中实际所做工作的详细说明(按成员可见性组织):
私有
1、加入dc/rc/pal私有变量
2、定义初始化dc/rc的私有方法

保护:
3、加入fonpaint,fonresize,foninit,fonpreinit四个事件响应变量。
4、继承/重载虚方法createparams,paint,resize
5、响应以下消息
wm_create, twmcreate, wmcreate
wm_destroy, twmdestroy, wmdestroy
wm_palettechanged, twmpalettechanged, wmpalettechanged
wm_querynewpalette, twmquerynewpalette, wmquerynewpalette
wm_erasebkgnd, twmerasebkgnd, wmerasebkgnd

公开:
6、定义建构与析构方法
7、定义必要的其它方法以提供各种特性

发布:
8、以下继承来的属性
__property alignment;
__property align;
__property dragcursor;
__property dragmode;
__property enabled;
__property parentfont;
__property parentshowhint;
__property popupmenu;
__property showhint;
__property taborder;
__property tabstop;
__property visible;
9、以下继承来的方法
__property onclick;
__property ondblclick;
__property ondragdrop;
__property ondragover;
__property onenddrag;
__property onenter;
__property onexit;
__property onmousedown;
__property onmousemove;
__property onmouseup;
__property onstartdrag;
10、加入以下事件
//初始化opengl状态
__property tnotifyevent oninit = {read=foninit,write=foninit};
//专用于修改显示bpp模式
__property tnotifyevent onpreinit = {read=fonpreinit,write=fonpreinit};
11、重载以下事件
__property tnotifyevent onresize = {read=fonresize,write=fonresize};
__property tnotifyevent onpaint = {read=fonpaint,write=fonpaint};
12、将消息与其响应函数连接起来(delphi中这一步是在定义函数时指定的)
源代码
unit glpanel;

interface

uses
windows, messages, sysutils, classes, graphics, controls, forms, dialogs,
extctrls,opengl;

type
tglpanel = class(tcustompanel)
private
{ private declarations }
dc: hdc;
rc: hglrc;
procedure initdc;
procedure initgl;
procedure preparepixelformat(var dc: hdc);

protected
{ protected declarations }
fonpaint:tnotifyevent;
foninit:tnotifyevent;
fonpreinit:tnotifyevent;
fonresize:tnotifyevent;

procedure paint;override;
procedure resize;override;
procedure wmdestroy(var msg: twmdestroy);message wm_destroy;
procedure wmcreate(var msg:twmcreate); message wm_create;


public
{ public declarations }
constructor create(owner:tcomponent);override;

published
{ published declarations }

property alignment;
property align;
property dragcursor;
property dragmode;
property enabled;
property parentfont;
property parentshowhint;
property popupmenu;
property showhint;
property taborder;
property tabstop;
property visible;

property onclick;
property ondblclick;
property ondragdrop;
property ondragover;
property onenddrag;
property onenter;
property onexit;
property onmousedown;
property onmousemove;
property onmouseup;
property onstartdrag;

property oninit:tnotifyevent read foninit write foninit;
property onpreinit:tnotifyevent read fonpreinit write fonpreinit;

property onresize:tnotifyevent read fonresize write fonresize;
property onpaint:tnotifyevent read fonpaint write fonpaint;

end;

procedure register;

implementation

procedure register;
begin
registercomponents(’samples’, [tglpanel]);
end;
//---------------------------------------------
constructor tglpanel.create;
begin
inherited;
end;
//---------------------------------------------
procedure tglpanel.wmdestroy(var msg: twmdestroy);
begin
wglmakecurrent(0, 0);
wgldeletecontext(rc);
releasedc(handle, dc);
end;
//---------------------------------------------------
procedure tglpanel.initdc;
begin
dc := getdc(handle);
preparepixelformat(dc);
end;
//---------------------------------------------------
procedure tglpanel.initgl;
begin
glclear(gl_color_buffer_bit or gl_depth_buffer_bit);
glmatrixmode(gl_projection);
glloadidentity;
glenable(gl_lighting);
glenable(gl_light0);
glortho(-1, 1, -1, 1, -1, 50);
glmatrixmode(gl_modelview);
glloadidentity;
glenable(gl_depth_test);
//注意下面这一行是为了做练习程序时可以直接用glcolor指定材质而加的,
// 可能使得光照或表面材质发生意想不到的变化,
// 如果不需要可以去掉或在程序中用gldisable(gl_color_material);关闭
glenable(gl_color_material);
glshademodel(gl_smooth);
glulookat(2, 4, 6, 0, 0, 0, 0, 1, 0);
swapbuffers(dc);
end;
//---------------------------------------------
procedure tglpanel.preparepixelformat(var dc: hdc);
var
pfd : tpixelformatdescriptor;
chosenpixelformat : integer;
begin
  fillchar(pfd, sizeof(tpixelformatdescriptor), 0);

with pfd do
begin
  nsize := sizeof(tpixelformatdescriptor);
  nversion := 1;
  dwflags := pfd_draw_to_window or
  pfd_support_opengl or
  pfd_doublebuffer;
  ipixeltype := pfd_type_rgba;
  ccolorbits := 16; // 16位颜色
  cdepthbits := 32; // 32位深度缓冲
  ilayertype := pfd_main_plane;
{ should be 24, but we must allow for the clunky wku boxes }
end;

chosenpixelformat := choosepixelformat(dc, @pfd);
if chosenpixelformat = 0 then
  raise exception.create(’choosepixelformat failed!’);
  setpixelformat(dc, chosenpixelformat, @pfd);
end;

procedure tglpanel.wmcreate(var msg:twmcreate);
begin
//在这里做初始化工作
//修改dc的象素格式,使之支持opengl绘制
initdc;
rc := wglcreatecontext(dc);
wglmakecurrent(dc, rc);
//初始化gl绘制系统
initgl;
if assigned(foninit) then
begin
 if (wglmakecurrent(dc,rc)=false) then
  showmessage(’wglmakecurrent:’ + inttostr(getlasterror));
  foninit(self);
 end;
end;
//
procedure tglpanel.paint;
begin
//tcustompanel::paint();
if assigned(fonpaint) then
 begin
  wglmakecurrent(dc,rc);
  fonpaint(self);
  swapbuffers(dc);
 end;
end;
//
procedure tglpanel.resize;
begin
  inherited;
if assigned(fonresize) then
 begin
  wglmakecurrent(dc,rc);
  glviewport(0,0,clientwidth,clientheight);
  fonresize(self);
  end;
 end;
end.

  以上代码仅用来说明原理及建立一个基本的练习环境,您可以自由使用,转载请注明出处。如果使用从本人主页下载的tglpanel请遵守内附使用说明的版权申明。如果实际做东西,建议使用mike lischke的glscene控件组(http://www.lischke-online.de/)。

  end

  else

  删除注册表项....................... end;初始化扩展是通过ishellextinit实现的,当外壳调用ishellextinit.initialize时,它传递一个数据对象包含来文件对应的目录的pidl标识符。initialize方法需要从数据对象中提取文件名,并把文件名和pidl标识符保存起来为了以后使用。

  

  function tcxpropsheet.seiinitialize(pidlfolder: pitemidlist;

    lpdobj: idataobject; hkeyprogid: hkey): hresult;

  var

    stgmedium: tstgmedium;

    formatetc: tformatetc;

    szfile: array[0..max_path+1]of char;

    filecount: integer;begin

    result:=e_fail;

  if(lpdobj=nil)then

  begin

    result:=e_invalidarg;

    messagebox(0, ’1’, ’错误’, mb_ok);

    exit;

  end;

  with formatetc do

  begin

    cfformat:=cf_hdrop;

    ptd:=nil;

    dwaspect:=dvaspect_content;

    lindex:=-1;

    tymed:=tymed_hglobal;

  end;

  result:=lpdobj.getdata(formatetc, stgmedium);

  if failed(result)then

  exit;

  //如果只有一个文件被选中,获得文件名并保存。

  filecount:=dragqueryfile(stgmedium.hglobal, $ffffffff, nil, 0);

  if filecount=1 then

  begin

    result:=noerror;

    dragqueryfile(stgmedium.hglobal, 0, szfile, sizeof(szfile));

    ffilename:=strpas(szfile);

  end;

  releasestgmedium(stgmedium);end;添加页面的操作是通过ishellpropsheetext接口来实现的。如果属性页是和文件相关联,外壳会调用ishellpropsheetext.addpages给属性页添加一个页面。如果属性页同控制面板程序相关联,外壳调用ishellpropsheetext.replacepage来替换页面。

  ishellpropsheetext.addpages方法有两个参数,lpfnaddpage是一个指向addpropsheetpageproc回调函数的指针,回调函数用来提供要添加的页面信息给外壳。lparam是一个用户自定义的值,这里我们用它来返回给回调函数对象。

  一般的ishellpropsheetext.addpages方法实现步骤是:

  给propsheetpage结构设定正确的值,特别是:

  把扩展的对象引用记数变量付值给pcrefparent成员,这可以防止页面还在显示时,扩展对象被卸载。

  实现propsheetpageproc回调函数来处理页面创建和销毁的情况。

  调用createpropertysheetpage函数来创建页面。

  调用lpfnaddpage指向的函数来来添加创建好的页面。

  function tcxpropsheet.addpages(lpfnaddpage: tfnaddpropsheetpage;

  lparam: lparam): hresult;var

  psp: tpropsheetpage;

  hpsp: hpropsheetpage;begin

  result:=e_fail;

  try

  psp.dwsize:=sizeof(psp);

  psp.dwflags:=psp_userefparent or psp_usetitle or psp_usecallback;

  psp.hinstance:=hinstance;

  //这里我们使用了事先储存在wave.res中的对话框模板,模板是用delphi5自带的

  //resource workshop编辑的,使用delphi5\bin\brcc32.exe编译的。

  psp.psztemplate:=makeintresource(100);

  //标题名

  psp.psztitle:=’波文件信息’;

  //设定回调函数

  psp.pfndlgproc:=@dialogproc;

  psp.pfncallback:=@propcallback;

  //设定对象引用记数变量

  psp.pcrefparent:=@comserver.objectcount;

  //用lparam向回调函数传递对象

  psp.lparam:=integer(self);

  hpsp:=createpropertysheetpage(psp);

  if hpsp$#@60;$#@62;nil then begin

  if not lpfnaddpage(hpsp, lparam)then begin

  destroypropertysheetpage(hpsp);

  end else begin

  _addref;//增加引用记数,否则一脱离这个方法的作用域,delphi自动释放对象。

  result:=s_ok;

  end

  end

  except

  on e: exception do begin

  e.message:=’添加页面’+e.message;

  messagebox(0, pchar(e.message), ’错误’, mb_ok);

  end;

  end;end;

  function tcxpropsheet.replacepage(upageid: uint;

  lpfnreplacewith: tfnaddpropsheetpage; lparam: lparam): hresult;begin

  result:=e_notimpl;//同文件关联时,外壳不调用replacepage,所以不用实现end;回调函数处理属性页的消息,主要要响应wm_initdialog消息来初始化页面显示信息,响应wm_command消息来处理用户交互,响应wm_notify消息来处理页面切换或关闭后处理操作结果。

  

  function dialogproc(hwnddlg: hwnd; msg: uint; wparam: wparam;

  lparam: lparam): bool; stdcall;

  var

    pageobj: tcxpropsheet;

    filename: string;

    displayname : string;

    sheethwnd: hwnd;

  begin

    result:=false;

    try

    if msg=wm_initdialog then begin//初始化界面

  //获得lparam传递过来的对象

    pageobj:=tcxpropsheet(ppropsheetpage(lparam)^.lparam);

  //保存对象信息

    setwindowlong(hwnddlg, dwl_user, integer(pageobj));

  //设置界面显示波文件信息

    setdlgitemtext(hwnddlg, 100, pchar(extractfilename(pageobj.ffilename)));

    openmedia(pageobj.ffilename);

  setdlgitemtext(hwnddlg, 101, pchar(inttostr(getwavstatus(mci_wave_status_avgbytespersec))));

  setdlgitemtext(hwnddlg, 102, pchar(inttostr(getwavstatus(mci_wave_status_bitspersample))));

  setdlgitemtext(hwnddlg, 103, pchar(inttostr(getwavstatus(mci_wave_status_channels))));

  closemedia;

    setwindowlong(hwnddlg, dwl_msgresult, 0);

    result:=true;

  end

  else if(msg=wm_command)then begin

  if lo(wparam)=110 then//用户点击了关于按钮(id=110)

    messagebox(0,’作者:hubdog’+#13#10+’email:hubdog@263.net’,’关于...’,mb_ok);

  end else if(msg=wm_notify)then begin

    sheethwnd:=getparent(hwnddlg);//获得属性页的窗口句柄

    case pnmhdr(lparam)^.code of

  //页面失去焦点

    psn_killactive:

  begin

    setwindowlong(hwnddlg, dwl_msgresult, 0);

    result:=true;

  end;

  end;

  end;

    except

    on e: exception do begin

    e.message:=’回调处理’+e.message;

    messagebox(0, pchar(e.message), ’错误’, mb_ok);

  end;

  end;

  end;

  

  建立同驱动器相关联的属性页扩展用

  同上面讲的有两点不同:

  ishellextinit.initialize方法传递过来的数据对象包含的驱动器路径可能是cfstr_mountedvolume格式而不是cf_hdrop格式的。标准驱动器是cf_hdrop格式的,而在ntfs文件系统中映射的远程设备则是cfstr_mountedvolume格式的。

  注册表项是hkey_classes_root\drive\shellex\propertysheethandlers子键。

  建立控制面板属性页扩展

  同上面讲的有两点不同:

  控制面板程序调用ishellpropsheetext.replacepage方法来替换页面,它不调用ishellpropsheetext。addpages方法。

  注册方式:子键可以在不同位置创建,这依赖于扩展是针对用户还是针对机器的。对用户方式子键是hkey_current_user\regstr_path_controlpanel,否则子键是hkey_local_machine\regstr_path_controlsfolder。

  本程序在delphi5,win nt 4.0,k6-233系统下调试成功。例子程序可以到http://chaozhi.com/lgc去下载


 


关键字 本文所属关键字

相关 与本文相关文章

分类 所有文章关键字导航

源码编程相关

Java   Asp   PHP   .Net   XML   C/C++   CGI   VB   Jsp   J2ee   J2se   J2me   EJB   Servlet   Tomcat   Resin   Struts   Weblogic   Eclipse   ANT   GUI   JMS   Web servise   IDEA   Webphere   Hibernate   Spring   Jboss   Applet   Swing   Socket   Javamail   Perl   Ajax   P2P   安全   模式   框架   测试   开源   游戏

SQL数据库相关

My-SQL   Ms-SQL   Access   DB2   Oracle   Sybase   SQLserver   索引   存储过程   加密   数据库   分页   视图  

手机无线相关

3G   Wap   CDMA   GRPS   GSM   IVR   彩信   短信   无线   增值业务

网页设计制作相关

HTML   CSS   网页配色   网页特效   Javascript   VBscript   Dreamweaver   Frontpage   JS   Web   网站设计

网站建设推广相关

建站经验   网站优化   网站排名   推广   Alexa

操作系统/服务器相关

Windows XP   Windows 2000   Windows 2003   Windows Me   Windows 9.x   Linux   UNIX   注册表   操作系统   服务器   应用服务器

图形图像多媒体相关

Photoshop   Fireworks   Flash   Coreldraw   Illustrator   Freehand   Photoimpact   多媒体   图形图像

标准 网站致力的规范

Valid CSS!

无不良内容,无不良广告,无恶意代码

Valid XHTML 1.0 Transitional

creativecommons