Delphi openGL and 3ds files….
Ambiente di sviluppo : Microsoft Visual C++ , Delphi 7.0, GNU GCC
Come tutti ormai credo sappiano Borland include una libreria per la gestione dell’OpenGl che appunto si chiama OpenGl.pas che a sua volta mappa per windows buona parte dei metodi contenuti nella dll di sistema ‘opengl32.dll’. Questa libreria funziona molto bene, ed in passato la vecchia Borland aveva anche fatto il corrispondente .so per l’ormai defuntu Kylix.
Tutto ciò è molto carino ed esaltante, però a meno di volersi disegnare gli scenari da codice , una cosa sicuramente necessaria è la possibilità di importare oggetti e scenari da un programma apposito. Quale programma se non il mitico 3d Studio ?
A questo punto ci si imbatte in una ricerca frenetica su internet incappando in diverse solozioni , tra cui una libreria interamente scritta in Delphi ed open source da un sito SULACO , a prima vista sembra funzioni tutto correttamente ma manipolando texture e punti luce affiorano dei piccoli problemi. Ovviamente è un buon lavoro che per chi è interessato permette di comprendere un po la struttura dei files 3ds e magari da una base per farne di conseguenza delle correzioni.
Provando e riprovando librerie si incappa nalla libreria “lib3ds” al sito www.lib3ds.org, questa libreria oltre ad essere molto veloce è anche semplicissima da utilizzare. A parte qualche prova iniziale ricompilandola come dll usando Microsoft Visual C++ ci è stato semplice utilizzarla con Delphi.
Su internet avevamo trovato già dei sorgenti in C++ che permettevano di compilarne una dll facilmente utilizzabile, e la dll creata dal progetto si chiama lib3dsloader.dll , c’era però qualche problema nel richiamo delle procedure, perchè la definizione in C++ non era stdcall. Allora abbiamo apportato al sorgente questa piccola modifica :
1 – nel file lib3dsloader.h abbiamo definito il tipo di riferimento per l’esportazione dei metodi (LIB3DSLOADERAPI ) come __stdcall
2 – la funzione “ivan” dato un pathfile e un “filename” ritorna l’id “hadle” della memoria dove gli oggetti 3d studio sono caricati , i parametri da Delphi dovranno essere dei pChar che corrispondono in C a char *
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <stdio.h> #include <stdlib.h> #include <stddef.h> #include "stdafx.h" #include <gl/gl.h> #define LIB3DSLOADERAPI __declspec(dllexport) __stdcall typedef char * pchar; #ifdef __cplusplus extern "C" { #endif struct DisplayList { int id; char name[64]; }; struct MaterialList { GLuint textureId; float diffuseColor[4]; }; int LIB3DSLOADERAPI Load3ds(char* pathname,char* filename,int & numDisplayLists,DisplayList dLists[],int &numMaterials,MaterialList mList[]); int LIB3DSLOADERAPI LoadWhole3ds(char* pathname,char* filename); int LIB3DSLOADERAPI ivan(pchar pathname,pchar filename); #ifdef __cplusplus } #endif |
nell’implementazione, cioè nel file lib3dsloader.cpp abbiamo modificato un po le definizioni in modo da renderle compliant, definendo il gruppo di funzioni esportate come “Extrnal C”
Download :
src libreria 3ds compreso di binario
Per usare questa libreria :
1) dovete copiare le due dll (lib3dsloader.dll e lib3ds-2_0.dll ) nella directory del progetto.
2) abbiamo creato una “unit” (libreria per i non delphini) che mappi i metodi della stessa (per il momento ci serve esclusivamente caricare gli oggetti 3dstudio), il codice della libreria per il richiamo :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
unit ACTS_Ivan; interface uses classes,sysutils; function IvanLoadWhole3ds(_Loc_File_Path, _Loc_File_name : String ) : Integer; implementation function I3dsLoader(_Loc_File_Path, _Loc_File_name : PChar ) : Integer; stdcall; external 'lib3dsloader.dll' name '_ivan@8'; function IvanLoadWhole3ds(_Loc_File_Path, _Loc_File_name : String ) : Integer; Begin result := I3dsLoader(Pchar(_Loc_File_Path),PChar(_Loc_File_name)); End; end. |
ora creiamo un progetto… dove nel form main usiamo la unit dove abbiamo definito il map con la DLL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
unit UnitOpenGl; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, dglOpenGL, ExtCtrls, StdCtrls, OpenGl; type TForm1 = class(TForm) Panel1: TPanel; Panel2: TPanel; CloseButton: TButton; procedure FormCreate(Sender: TObject); procedure Panel1Resize(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: Char); procedure CloseButtonClick(Sender: TObject); private { Private declarations } rc : HGLRC; // Rendering Context dc : HDC; // Device Context ElapsedTime, AppStart, LastTime : DWord; // Timing variables ObjectLoaded : Integer; procedure glDraw; procedure Idle(Sender: TObject; var Done: Boolean); procedure CaricaTutto; public { Public declarations } end; var Form1: TForm1; implementation uses ACTS_Ivan; {$R *.DFM} {------------------------------------------------------------------} { Function to draw the actual scene } {------------------------------------------------------------------} procedure TForm1.glDraw(); Var x:Integer; begin glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer glLoadIdentity(); // Reset The View glTranslatef(0, 0, -4); glRotatef(ElapsedTime/10, 0, 1, 0); glBegin(GL_TRIANGLES); glColor3f(1, 0, 0); glVertex3f(-1, -1, 0); glColor3f(0, 1, 0); glVertex3f( 1, -1, 0); glColor3f(0, 0, 1); glVertex3f( 0, 1, 0); glEnd(); glPushMatrix(); glPopMatrix(); end; {------------------------------------------------------------------} { Initialise OpenGL } {------------------------------------------------------------------} procedure glInit(); begin glClearColor(0.0, 0.0, 0.0, 0.0); // Black Background glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading glClearDepth(1.0); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); // Enable Depth Buffer glDepthFunc(GL_LESS); // The Type Of Depth Test To Do glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //Realy Nice perspective calculations end; {------------------------------------------------------------------} { Create the form and initialist openGL } {------------------------------------------------------------------} procedure TForm1.FormCreate(Sender: TObject); var pfd : TPIXELFORMATDESCRIPTOR; pf : Integer; begin InitOpenGL; // New call to initialize and bind the OpenGL dll // OpenGL initialisieren dc:=GetDC(Panel1.Handle); // PixelFormat pfd.nSize:=sizeof(pfd); pfd.nVersion:=1; pfd.dwFlags:=PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;// or 0; pfd.iPixelType:=PFD_TYPE_RGBA; // PFD_TYPE_RGBA or PFD_TYPEINDEX pfd.cColorBits:=32; pf := ChoosePixelFormat(dc, @pfd); // Returns format that most closely matches above pixel format SetPixelFormat(dc, pf, @pfd); rc :=wglCreateContext(dc); // Rendering Context = window-glCreateContext wglMakeCurrent(dc,rc); // Make the DC (Form1) the rendering Context ReadExtensions; // Read And Bind The Standard OpenGL Functions ReadImplementationProperties; // Read And Bind All // Initialise GL environment variables glInit; Panel1Resize(sender); // sets up the perspective AppStart :=GetTickCount(); // when the app has spare time, render the GL scene Application.OnIdle := Idle; // CaricaTutto(); end; {------------------------------------------------------------------} { Release rendering context when form gets detroyed } {------------------------------------------------------------------} procedure TForm1.FormDestroy(Sender: TObject); begin wglMakeCurrent(0,0); wglDeleteContext(rc); end; {------------------------------------------------------------------} { Application onIdle event } {------------------------------------------------------------------} procedure TForm1.Idle(Sender: TObject; var Done: Boolean); begin Done := FALSE; LastTime :=ElapsedTime; ElapsedTime :=GetTickCount() - AppStart; // Calculate Elapsed Time ElapsedTime :=(LastTime + ElapsedTime) DIV 2; // Average it out for smoother movement glDraw(); // Draw the scene SwapBuffers(DC); // Display the scene end; {------------------------------------------------------------------} { If the panel resizes, reset the GL scene } {------------------------------------------------------------------} procedure TForm1.Panel1Resize(Sender: TObject); begin if not (ExtensionsRead and ImplementationRead) then // Only call the resize if the OpenGL dll was bound to the functions exit; glViewport(0, 0, Panel1.Width, Panel1.Height); // Set the viewport for the OpenGL window glMatrixMode(GL_PROJECTION); // Change Matrix Mode to Projection glLoadIdentity(); // Reset View gluPerspective(45.0, Panel1.Width/Panel1.Height, 1.0, 45500.0); // Do the perspective calculations. Last value = max clipping depth glMatrixMode(GL_MODELVIEW); // Return to the modelview matrix end; {------------------------------------------------------------------} { Monitors all keypress events for the app } {------------------------------------------------------------------} procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); begin if Key = #27 then Close; end; procedure TForm1.CloseButtonClick(Sender: TObject); begin Close; end; procedure TForm1.CaricaTutto; var FileName: String; begin // In file name inserite il nome file del file 3d studio ObjectLoaded := IvanLoadWhole3ds(ExtractFilePath(FileName),ExtractFileName(FileName)); end; end. |
a questo punto avete caricato e state vedendo il vs modello 3DStudio all’interno del vostro form Delphi, ovviamente non ditemi quant’è veloce perchè ci son rimasto di stucco, viaggia alla stragrande anche dentro una virtual machine con le OpenGL in emulazione…. (da paura)
se avete bisogno di chiarimenti in merito lasciate pure un commento mi arriva la mail ….
a presto
ivan