扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
int main(int argc, char** argv)
{ auxInitDisplayMode( AUX_SINGLE|AUX_RGB|AUX_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系统下调试成功。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者