{this unit has a group of useful routines for use with OpenGL} { TsceneGL: a 3D scene, has a group of entities and a group of lights. Tlight: a light, has color, position and orientation. by now only the color attributes have any effect. T3dMouse: enables a normal mouse to interact easily with one 3D object By: Ricardo Sarmiento delphiman@hotmail.com } unit UrickGL; interface Uses Windows, Messages, SysUtils, Classes, GL, GLU, U3DPolys; type TsceneGL=class(Tobject) DC:HDC; {somewhat private} HRC: HGLRC; {somewhat private} WindowHandle:Thandle; {handle to the host window, a Tpanel most times} Entities:Tlist; {Availiable entities in the scene} Lights:Tlist; {Availiable lights in the scene} Active:boolean; {true: InitRC has been called already} Fperspective:boolean; {true: use perspective projection, false: use orthogonal projection} Angle,DistNear,DistFar:single; {values for Field-of-view angle, distance to near clipping plane, distance to far clipping plane} texturing:boolean; {true: use textures and texture information, false: donīt use textures} Constructor create; {creates a new scene} Destructor destroy; override; {donīt call this directly, call free instead} {initialization of the Rendering Context, receives the handle of the display control, frecuently it is a Tpanel} Procedure InitRC(iHandle:THandle); Procedure Redraw; {redraw scene} {set values for the pixelīs format. donīt call this Function directly, call instead InitRC.} Procedure SetDCPixelFormat; {Free all resources taken by InitRC. donīt call this Function directly} Procedure ReleaseRC; {reflects changes in the width and height (ancho,alto) of the display control} Procedure UpdateArea(width,height:integer); {Set the perspective values for Field-of-view angle, distance to near clipping plane, distance to far clipping plane} Procedure SetPerspective(iAngle,iDistNear,iDistFar:single); end; {TsceneGL} Tlight=class(Tobject) Source:Tvertex; {position and orientation of the light, not yet implemented} Fambient, FdIffuse, Fspecular:array[0..3] of GLfloat; {ambient, dIffuse and specular components} Number:LongInt; {number of the light, from 1 to 8} Constructor Create(num:Byte); {create a new, white Ambient light. num goes from 1 to 8} Destructor Destroy; override; {donīt call directly, call instead free} Procedure Ambient(r,g,b,a:GLfloat); {change the Ambient component of the light} Procedure DIffuse(r,g,b,a:GLfloat); {change the dIffuse component of the light} Procedure Specular(r,g,b,a:GLfloat); {change the specular component of the light} Procedure Redraw; {executes the OpenGL code for this light} end; {Tlight} T3dMouse=class(Tobject) {ease manipulation of 3D objects with the mouse} Button1, {left button on mouse} Button2:boolean; {right button on mouse} Mode:integer; {movement mode: 1-move x,y-z, 2-turn rx,ry-rz 3-turn rx,ry-rz+move z.} Entity:Tentity; {points to the entity to be modIfied, just one at a time} Start:array[1..6] of single; {x,y,z - rx,ry,rz saves the initial position when dragging mouse} Scaling:array[1..6] of single; {x,y,z - rx,ry,rz stores the speed relations between mouse and 3D} BlockStatus:array[1..6] of boolean; {x,y,z - rx,ry,rz which movements are blocked} Constructor Create(iEntity:Tentity); {iEntity is the pointer to one entity in the scene} Procedure Move(x,y:single;Botones: TShIftState); {move or turn the entity according to the mode} Procedure Scale(x,y,z,rx,ry,rz:single); {controls the relative speed between the mouse and the object} Procedure Block(num:integer;valor:boolean); {blocks movement and/or rotation on any of 6 axis.} Procedure FindVertex(x,y:integer;Scene:TsceneGL;var pt:Tvertex); {return a pointer to the vertex which was nearer to the mouse} {Scene is the scene to be evaluated, pt is the chosen vertex, can be nil If none was found under the mouse} {x,y are the coordinates of the mouse} end; {num goes from 1 to 6, valor=true:block, valor=false: donīt block movement} Const MaxHits=200; var xxx,yyy,nnn:integer; numFound:integer; VertexHits:Array[1..MaxHits] of integer; {This function can convert a BMP file into a RGBA file} {parameters: BMPname: name of the BMP file, RGBAname: name of the new file, transparent: color that will be treated as transparent} Function ConvertBMP(BMPname:string;RGBAname:string;Transparent:longint):integer; {this is to avoid certain problems with borland tools. donīt call them yourself} Function MaskExceptions: Pointer; Procedure UnmaskExceptions(OldMask: Pointer); implementation Constructor TsceneGL.create; begin inherited create; Entities:=Tlist.create; Lights:=Tlist.create; Active:=false; Fperspective:=true; Angle:=30; {degrees for Field-of-view angle} DistNear:=1; {1 unit of distance to near clipping plane} DistFar:=100; {distance to far clipping plane} texturing:=true; {use textures} end; Destructor TsceneGL.destroy; begin If Active then ReleaseRC; Lights.free; Entities.free; inherited destroy; end; Procedure TsceneGL.InitRC; begin {set the area where the OpenGL rendering will take place} WindowHandle:=iHandle; DC := GetDC(WindowHandle); SetDCPixelFormat; HRC := wglCreateContext(DC); wglMakeCurrent(DC, HRC); { enable depht testing and back face rendering} glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); {enable Lights} Active:=True; end; Procedure TsceneGL.SetDCPixelFormat; var nPixelFormat: Integer; pfd: TPixelFormatDescriptor; begin FillChar(pfd, SizeOf(pfd),0); with pfd do begin nSize := sizeof(pfd); // Size of this structure nVersion := 1; // Version number dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER; // Flags iPixelType:= PFD_TYPE_RGBA; // RGBA pixel values cColorBits:= 24; // 24-bit color cDepthBits:= 32; // 32-bit depth buffer iLayerType:= PFD_MAIN_PLANE; // Layer type end; nPixelFormat := ChoosePixelFormat(DC, @pfd); SetPixelFormat(DC, nPixelFormat, @pfd); DescribePixelFormat(DC, nPixelFormat, sizeof(TPixelFormatDescriptor), pfd); end; Procedure TsceneGL.ReleaseRC; begin wglMakeCurrent(0, 0); wglDeleteContext(HRC); ReleaseDC(WindowHandle, DC); Active:=False; end; Procedure TsceneGL.UpdateArea; var gldAspect : GLdouble; ratio,range:GlFloat; begin { redefine the visible volume and the viewport when the windowīs size is modIfied} glMatrixMode(GL_PROJECTION); glLoadIdentity; If Fperspective then begin gldAspect := width/height; gluPerspective(Angle, // Field-of-view angle gldAspect, // Aspect ratio of viewing volume DistNear, // Distance to near clipping plane DistFar); // Distance to far clipping plane end else begin { Orthogonal projection} range:=6; If width<=height then begin ratio:=height/width; GlOrtho(-range,range,-range*ratio,range*ratio,-range*4,range*4); end else begin ratio:=width/height; GlOrtho(-range*ratio,range*ratio,-range,range,-range*4,range*4); end; end; glViewport(0, 0, width, height); end; Procedure TsceneGL.Redraw; const glfMaterialColor: Array[0..3] of GLfloat = (0.5, 1.0, 0.5, 1.0); var i,num:integer; ps : TPaintStruct; pp:Pointer; begin If Active then begin {initialization} pp:=MaskExceptions; BeginPaint(WindowHandle, ps); {place Lights} i:=0; num:=Lights.count; while ix) and (nx-radioy) and (ny-radio