1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2011-2016. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 #ifdef _WIN32
25 #include <windows.h>
26 #endif
27 
28 #include "egl_impl.h"
29 
30 #define WX_DEF_EXTS
31 #include "gen/gl_fdefs.h"
32 #include "gen/gl_finit.h"
33 #include "gen/glu_finit.h"
34 
35 void init_tess();
36 void exit_tess();
37 int load_gl_functions();
38 
39 /* ****************************************************************************
40  * OPENGL INITIALIZATION
41  *****************************************************************************/
42 
43 int egl_initiated = 0;
44 
45 #ifdef _WIN32
46 #define RTLD_LAZY 0
47 #define OPENGL_LIB L"opengl32.dll"
48 #define OPENGLU_LIB L"glu32.dll"
49 typedef HMODULE DL_LIB_P;
50 typedef WCHAR DL_CHAR;
51 #define DL_STR_FMT "%S"
dlsym(HMODULE Lib,const char * func)52 void * dlsym(HMODULE Lib, const char *func) {
53   void * funcp;
54   if((funcp = (void *) GetProcAddress(Lib, func)))
55     return funcp;
56   else
57     return (void *) wglGetProcAddress(func);
58 }
59 
dlopen(const WCHAR * DLL,int unused)60 HMODULE dlopen(const WCHAR *DLL, int unused) {
61   return LoadLibrary(DLL);
62 }
63 
dlclose(HMODULE Lib)64 void dlclose(HMODULE Lib) {
65   FreeLibrary(Lib);
66 }
67 
68 #else
69 typedef void * DL_LIB_P;
70 typedef char DL_CHAR;
71 # define DL_STR_FMT "%s"
72 # ifdef _MACOSX
73 #  define OPENGL_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"
74 #  define OPENGLU_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib"
75 # else
76 #  define OPENGL_LIB "libGL.so.1"
77 #  define OPENGLU_LIB "libGLU.so.1"
78 # endif
79 #endif
80 extern "C" {
DRIVER_INIT(EGL_DRIVER)81 DRIVER_INIT(EGL_DRIVER) {
82   return NULL;
83 }
84 }
85 
egl_init_opengl(void * erlCallbacks)86 int egl_init_opengl(void *erlCallbacks)
87 {
88 #ifdef _WIN32
89   driver_init((TWinDynDriverCallbacks *) erlCallbacks);
90 #endif
91   if(egl_initiated == 0) {
92     if(load_gl_functions()) {
93       init_tess();
94       egl_initiated = 1;
95     }
96   }
97   return 1;
98 }
99 
load_gl_functions()100 int load_gl_functions() {
101   DL_CHAR * DLName = (DL_CHAR *) OPENGL_LIB;
102   DL_LIB_P LIBhandle = dlopen(DLName, RTLD_LAZY);
103   //fprintf(stderr, "Loading GL: %s\r\n", (const char*)DLName);
104   void * func = NULL;
105   int i;
106 
107   if(LIBhandle) {
108     for(i=0; gl_fns[i].name != NULL; i++) {
109       if((func = dlsym(LIBhandle, gl_fns[i].name))) {
110 	* (void **) (gl_fns[i].func) = func;
111 	// fprintf(stderr, "GL LOADED %s \r\n", gl_fns[i].name);
112       } else {
113 	if(gl_fns[i].alt != NULL) {
114 	  if((func = dlsym(LIBhandle, gl_fns[i].alt))) {
115 	    * (void **) (gl_fns[i].func) = func;
116 	    // fprintf(stderr, "GL LOADED %s \r\n", gl_fns[i].alt);
117 	  } else {
118 	    * (void **) (gl_fns[i].func) = (void *) &gl_error;
119 	    // fprintf(stderr, "GL Skipped %s and %s \r\n", gl_fns[i].name, gl_fns[i].alt);
120 	  };
121 	} else {
122 	  * (void **) (gl_fns[i].func) = (void *) &gl_error;
123 	  // fprintf(stderr, "GL Skipped %s \r\n", gl_fns[i].name);
124 	}
125       }
126     }
127     // dlclose(LIBhandle);
128     // fprintf(stderr, "OPENGL library is loaded\r\n");
129   } else {
130     fprintf(stderr, "Could NOT load OpenGL library: " DL_STR_FMT "\r\n", DLName);
131   };
132 
133   DLName = (DL_CHAR *) OPENGLU_LIB;
134   LIBhandle = dlopen(DLName, RTLD_LAZY);
135   // fprintf(stderr, "Loading GLU: %s\r\n", (const char*)DLName);
136   func = NULL;
137 
138   if(LIBhandle) {
139     for(i=0; glu_fns[i].name != NULL; i++) {
140       if((func = dlsym(LIBhandle, glu_fns[i].name))) {
141 	* (void **) (glu_fns[i].func) = func;
142       } else {
143 	if(glu_fns[i].alt != NULL) {
144 	  if((func = dlsym(LIBhandle, glu_fns[i].alt))) {
145 	    * (void **) (glu_fns[i].func) = func;
146 	  } else {
147 	    * (void **) (glu_fns[i].func) = (void *) &gl_error;
148 	    // fprintf(stderr, "GLU Skipped %s\r\n", glu_fns[i].alt);
149 	  };
150 	} else {
151 	  * (void **) (glu_fns[i].func) = (void *) &gl_error;
152 	  // fprintf(stderr, "GLU Skipped %s\r\n", glu_fns[i].name);
153 	}
154       }
155     }
156     // dlclose(LIBhandle);
157     // fprintf(stderr, "GLU library is loaded\r\n");
158   } else {
159     fprintf(stderr, "Could NOT load OpenGL GLU library: " DL_STR_FMT "\r\n", DLName);
160   };
161 
162   return 1;
163 }
164 
gl_error()165 void gl_error() {
166   // fprintf(stderr, "OpenGL Extension not available \r\n");
167   throw "undef_extension";
168 }
169 
170 /* *******************************************************************************
171  * GLU Tesselation special
172  * ******************************************************************************/
173 
174 static GLUtesselator* tess;
175 
176 typedef struct {
177   GLdouble * tess_coords;
178   int alloc_n;
179   int alloc_max;
180 
181   int * tess_index_list;
182   int index_n;
183   int index_max;
184 
185   int error;
186 } egl_tess_data;
187 
188 #define NEED_MORE_ALLOC 1
189 #define NEED_MORE_INDEX 2
190 
191 static egl_tess_data egl_tess;
192 
193 void CALLBACK
egl_ogla_vertex(GLdouble * coords)194 egl_ogla_vertex(GLdouble* coords)
195 {
196   /* fprintf(stderr, "%d\r\n", (int) (coords - tess_coords) / 3); */
197   if(egl_tess.index_n < egl_tess.index_max) {
198     egl_tess.tess_index_list[egl_tess.index_n] = (int) (coords - egl_tess.tess_coords) / 3;
199     egl_tess.index_n++;
200   }
201   else
202     egl_tess.error = NEED_MORE_INDEX;
203 }
204 
205 void CALLBACK
egl_ogla_combine(GLdouble coords[3],void * vertex_data[4],GLfloat w[4],void ** dataOut)206 egl_ogla_combine(GLdouble coords[3],
207 		 void* vertex_data[4],
208 		 GLfloat w[4],
209 		 void **dataOut)
210 {
211   GLdouble* vertex = &egl_tess.tess_coords[egl_tess.alloc_n];
212   if(egl_tess.alloc_n < egl_tess.alloc_max) {
213     egl_tess.alloc_n += 3;
214     vertex[0] = coords[0];
215     vertex[1] = coords[1];
216     vertex[2] = coords[2];
217     *dataOut = vertex;
218 
219 #if 0
220     fprintf(stderr, "combine: ");
221     int i;
222     for (i = 0; i < 4; i++) {
223       if (w[i] > 0.0) {
224 	fprintf(stderr, "%d(%g) ", (int) vertex_data[i], w[i]);
225       }
226     }
227     fprintf(stderr, "\r\n");
228     fprintf(stderr, "%g %g %g\r\n", vertex[0], vertex[1], vertex[2]);
229 #endif
230 
231   } else {
232     egl_tess.error = NEED_MORE_ALLOC;
233     *dataOut = NULL;
234   }
235 }
236 
237 void CALLBACK
egl_ogla_edge_flag(GLboolean flag)238 egl_ogla_edge_flag(GLboolean flag)
239 {
240 }
241 
242 void CALLBACK
egl_ogla_error(GLenum errorCode)243 egl_ogla_error(GLenum errorCode)
244 {
245   // const GLubyte *err;
246   // err = gluErrorString(errorCode);
247   // fprintf(stderr, "Tesselation error: %d: %s\r\n", (int) errorCode, err);
248 }
249 
init_tess()250 void init_tess()
251 {
252   tess = gluNewTess();
253 
254   gluTessCallback(tess, GLU_TESS_VERTEX,     (GLUfuncptr) egl_ogla_vertex);
255   gluTessCallback(tess, GLU_TESS_EDGE_FLAG,  (GLUfuncptr) egl_ogla_edge_flag);
256   gluTessCallback(tess, GLU_TESS_COMBINE,    (GLUfuncptr) egl_ogla_combine);
257   gluTessCallback(tess, GLU_TESS_ERROR,      (GLUfuncptr) egl_ogla_error);
258 
259 }
260 
exit_tess()261 void exit_tess()
262 {
263   gluDeleteTess(tess);
264 }
265 
erl_tess_impl(char * buff,ErlDrvPort port,ErlDrvTermData caller)266 int erl_tess_impl(char* buff, ErlDrvPort port, ErlDrvTermData caller)
267 {
268   ErlDrvBinary* bin;
269   int i;
270   int num_vertices;
271   GLdouble *n;
272   int AP;
273   int a_max = 2;
274   int i_max = 6;
275   num_vertices = * (int *) buff; buff += 8; /* Align */
276   n = (double *) buff; buff += 8*3;
277 
278   egl_tess.alloc_max = a_max*num_vertices*3;
279   bin = driver_alloc_binary(egl_tess.alloc_max*sizeof(GLdouble));
280   egl_tess.error = 0;
281   egl_tess.tess_coords = (double *) bin->orig_bytes;
282   memcpy(egl_tess.tess_coords,buff,num_vertices*3*sizeof(GLdouble));
283   egl_tess.index_max = i_max*3*num_vertices;
284   egl_tess.tess_index_list = (int *) driver_alloc(sizeof(int) * egl_tess.index_max);
285 
286   egl_tess.tess_coords = (double *) bin->orig_bytes;
287   egl_tess.index_n = 0;
288   egl_tess.alloc_n = num_vertices*3;
289 
290   gluTessNormal(tess, n[0], n[1], n[2]);
291   gluTessBeginPolygon(tess, 0);
292   gluTessBeginContour(tess);
293   for (i = 0; i < num_vertices; i++) {
294     gluTessVertex(tess, egl_tess.tess_coords+3*i, egl_tess.tess_coords+3*i);
295   }
296   gluTessEndContour(tess);
297   gluTessEndPolygon(tess);
298 
299   AP = 0; ErlDrvTermData *rt;
300   rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData) * (13+egl_tess.index_n*2));
301   rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
302 
303   for(i=0; i < egl_tess.index_n; i++) {
304     rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) egl_tess.tess_index_list[i];
305   };
306   rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = egl_tess.index_n+1;
307 
308   rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) bin;
309   rt[AP++] = egl_tess.alloc_n*sizeof(GLdouble); rt[AP++] = 0;
310 
311   rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Return tuple {list, Bin}
312   rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Result tuple
313 
314   driver_send_term(port,caller,rt,AP);
315   /* fprintf(stderr, "List %d: %d %d %d \r\n",  */
316   /* 	  res, */
317   /* 	  n_pos,  */
318   /* 	  (tess_alloc_vertex-new_vertices)*sizeof(GLdouble),  */
319   /* 	  num_vertices*6*sizeof(GLdouble)); */
320   driver_free_binary(bin);
321   driver_free(egl_tess.tess_index_list);
322   driver_free(rt);
323   return 0;
324 }
325