Delphi openGL and 3ds files….

opengl.png

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. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <stddef.h>
  5. #include "stdafx.h"
  6. #include <gl/gl.h>
  7.  
  8.  
  9. #define LIB3DSLOADERAPI __declspec(dllexport) __stdcall
  10. typedef char * pchar;
  11.  
  12. #ifdef __cplusplus
  13. extern "C" {
  14. #endif
  15.  
  16. struct DisplayList
  17. {
  18.         int      id;
  19.         char name[64];
  20. };
  21.  
  22. struct MaterialList
  23. {
  24.         GLuint textureId;
  25.         float  diffuseColor[4];
  26. };
  27.  
  28. int LIB3DSLOADERAPI Load3ds(char* pathname,char* filename,int & numDisplayLists,DisplayList dLists[],int &numMaterials,MaterialList mList[]);
  29. int LIB3DSLOADERAPI LoadWhole3ds(char* pathname,char* filename);
  30. int LIB3DSLOADERAPI ivan(pchar pathname,pchar filename);
  31.  
  32. #ifdef __cplusplus
  33.  }
  34. #endif
  35.  

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. unit ACTS_Ivan;
  3.  
  4. interface
  5. uses classes,sysutils;
  6.  
  7. function IvanLoadWhole3ds(_Loc_File_Path, _Loc_File_name : String ) : Integer;
  8.  
  9. implementation
  10.  
  11.  
  12. function I3dsLoader(_Loc_File_Path, _Loc_File_name : PChar ) : Integer; stdcall; external ‘lib3dsloader.dll’ name ‘_ivan@8′;
  13.  
  14.  
  15. function IvanLoadWhole3ds(_Loc_File_Path, _Loc_File_name : String ) : Integer;
  16. Begin
  17.  result := I3dsLoader(Pchar(_Loc_File_Path),PChar(_Loc_File_name));
  18. End;
  19.  
  20.  
  21. end.
  22.  
  23.  

ora creiamo un progetto… dove nel form main usiamo la unit dove abbiamo definito il map con la DLL

  1.  
  2. unit UnitOpenGl;
  3.  
  4. interface
  5.  
  6. uses
  7.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, dglOpenGL,
  8.   ExtCtrls, StdCtrls, OpenGl;
  9.  
  10.  
  11. type
  12.   TForm1 = class(TForm)
  13.     Panel1: TPanel;
  14.     Panel2: TPanel;
  15.     CloseButton: TButton;
  16.     procedure FormCreate(Sender: TObject);
  17.     procedure Panel1Resize(Sender: TObject);
  18.     procedure FormDestroy(Sender: TObject);
  19.     procedure FormKeyPress(Sender: TObject; var Key: Char);
  20.     procedure CloseButtonClick(Sender: TObject);
  21.   private
  22.     { Private declarations }
  23.     rc : HGLRC;    // Rendering Context
  24.     dc  : HDC;     // Device Context
  25.     ElapsedTime, AppStart, LastTime : DWord;  // Timing variables
  26.     ObjectLoaded : Integer;
  27.     procedure glDraw;
  28.     procedure Idle(Sender: TObject; var Done: Boolean);
  29.     procedure CaricaTutto;
  30.  
  31.   public
  32.     { Public declarations }
  33.   end;
  34.  
  35. var
  36.   Form1: TForm1;
  37.  
  38.  
  39. implementation
  40.  
  41. uses ACTS_Ivan;
  42.  
  43. {$R *.DFM}
  44.  
  45. {——————————————————————}
  46. {  Function to draw the actual scene                               }
  47. {——————————————————————}
  48. procedure TForm1.glDraw();
  49. Var x:Integer;
  50. begin
  51.   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);    // Clear The Screen And The Depth Buffer
  52.   glLoadIdentity();                                       // Reset The View
  53.  
  54.  
  55.   glTranslatef(0, 0, -4);
  56.  
  57.   glRotatef(ElapsedTime/10, 0, 1, 0);
  58.  
  59.   glBegin(GL_TRIANGLES);
  60.     glColor3f(1, 0, 0);  glVertex3f(-1, -1, 0);
  61.     glColor3f(0, 1, 0);  glVertex3f( 1, -1, 0);
  62.     glColor3f(0, 0, 1);  glVertex3f( 0,  1, 0);
  63.   glEnd();
  64.   glPushMatrix();
  65.   glPopMatrix();
  66. end;
  67.  
  68.  
  69. {——————————————————————}
  70. {  Initialise OpenGL                                               }
  71. {——————————————————————}
  72. procedure glInit();
  73. begin
  74.   glClearColor(0.0, 0.0, 0.0, 0.0);        // Black Background
  75.   glShadeModel(GL_SMOOTH);                 // Enables Smooth Color Shading
  76.   glClearDepth(1.0);                       // Depth Buffer Setup
  77.   glEnable(GL_DEPTH_TEST);                 // Enable Depth Buffer
  78.   glDepthFunc(GL_LESS);                    // The Type Of Depth Test To Do
  79.  
  80.   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);   //Realy Nice perspective calculations
  81. end;
  82.  
  83.  
  84. {——————————————————————}
  85. {  Create the form and initialist openGL                           }
  86. {——————————————————————}
  87. procedure TForm1.FormCreate(Sender: TObject);
  88. var pfd : TPIXELFORMATDESCRIPTOR;
  89.     pf  : Integer;
  90. begin
  91.   InitOpenGL;                               // New call to initialize and bind the OpenGL dll
  92.  
  93.   // OpenGL initialisieren
  94.   dc:=GetDC(Panel1.Handle);
  95.  
  96.   // PixelFormat
  97.   pfd.nSize:=sizeof(pfd);
  98.   pfd.nVersion:=1;
  99.   pfd.dwFlags:=PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;// or 0;
  100.   pfd.iPixelType:=PFD_TYPE_RGBA;      // PFD_TYPE_RGBA or PFD_TYPEINDEX
  101.   pfd.cColorBits:=32;
  102.  
  103.  
  104.   pf := ChoosePixelFormat(dc, @pfd);   // Returns format that most closely matches above pixel format
  105.   SetPixelFormat(dc, pf, @pfd);
  106.  
  107.   rc :=wglCreateContext(dc);    // Rendering Context = window-glCreateContext
  108.   wglMakeCurrent(dc,rc);        // Make the DC (Form1) the rendering Context
  109.  
  110.  
  111.   ReadExtensions;               // Read And Bind The Standard OpenGL Functions
  112.   ReadImplementationProperties; // Read And Bind All
  113.  
  114.   // Initialise GL environment variables
  115.   glInit;
  116.   Panel1Resize(sender);    // sets up the perspective
  117.   AppStart :=GetTickCount();
  118.  
  119.   // when the app has spare time, render the GL scene
  120.   Application.OnIdle := Idle;
  121.   //
  122.   CaricaTutto();
  123. end;
  124.  
  125.  
  126. {——————————————————————}
  127. {  Release rendering context when form gets detroyed               }
  128. {——————————————————————}
  129. procedure TForm1.FormDestroy(Sender: TObject);
  130. begin
  131.   wglMakeCurrent(0,0);
  132.   wglDeleteContext(rc);
  133. end;
  134.  
  135.  
  136. {——————————————————————}
  137. {  Application onIdle event                                        }
  138. {——————————————————————}
  139. procedure TForm1.Idle(Sender: TObject; var Done: Boolean);
  140. begin
  141.   Done := FALSE;
  142.  
  143.   LastTime :=ElapsedTime;
  144.   ElapsedTime :=GetTickCount() - AppStart;      // Calculate Elapsed Time
  145.   ElapsedTime :=(LastTime + ElapsedTime) DIV 2; // Average it out for smoother movement
  146.  
  147.   glDraw();                         // Draw the scene
  148.   SwapBuffers(DC);                  // Display the scene
  149. end;
  150.  
  151.  
  152. {——————————————————————}
  153. {  If the panel resizes, reset the GL scene                        }
  154. {——————————————————————}
  155. procedure TForm1.Panel1Resize(Sender: TObject);
  156. begin
  157.   if not (ExtensionsRead and ImplementationRead) then // Only call the resize if the OpenGL dll was bound to the functions
  158.     exit;
  159.  
  160.   glViewport(0, 0, Panel1.Width, Panel1.Height);    // Set the viewport for the OpenGL window
  161.   glMatrixMode(GL_PROJECTION);        // Change Matrix Mode to Projection
  162.   glLoadIdentity();                   // Reset View
  163.   gluPerspective(45.0, Panel1.Width/Panel1.Height, 1.0, 45500.0);  // Do the perspective calculations. Last value = max clipping depth
  164.  
  165.   glMatrixMode(GL_MODELVIEW);         // Return to the modelview matrix
  166. end;
  167.  
  168.  
  169. {——————————————————————}
  170. {  Monitors all keypress events for the app                        }
  171. {——————————————————————}
  172. procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
  173. begin
  174.   if Key = #27 then
  175.     Close;
  176. end;
  177.  
  178. procedure TForm1.CloseButtonClick(Sender: TObject);
  179. begin
  180.   Close;
  181. end;
  182.  
  183.  
  184. procedure TForm1.CaricaTutto;
  185. var FileName: String;
  186.  
  187. begin
  188.   // In file name inserite il nome file del file 3d studio
  189.   ObjectLoaded := IvanLoadWhole3ds(ExtractFilePath(FileName),ExtractFileName(FileName));
  190.  
  191.  
  192. end;
  193.  
  194. end.
  195.  

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

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>