/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2011-2013. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* %CopyrightEnd%
*/
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#endif
#include "egl_impl.h"
#define WX_DEF_EXTS
#include "gen/gl_fdefs.h"
#include "gen/gl_finit.h"
#include "gen/glu_finit.h"
void init_tess();
void exit_tess();
int load_gl_functions();
/* ****************************************************************************
* OPENGL INITIALIZATION
*****************************************************************************/
int egl_initiated = 0;
#ifdef _WIN32
#define RTLD_LAZY 0
#define OPENGL_LIB L"opengl32.dll"
#define OPENGLU_LIB L"glu32.dll"
typedef HMODULE DL_LIB_P;
typedef WCHAR DL_CHAR;
void * dlsym(HMODULE Lib, const char *func) {
void * funcp;
if((funcp = (void *) GetProcAddress(Lib, func)))
return funcp;
else
return (void *) wglGetProcAddress(func);
}
HMODULE dlopen(const WCHAR *DLL, int unused) {
return LoadLibrary(DLL);
}
void dlclose(HMODULE Lib) {
FreeLibrary(Lib);
}
#else
typedef void * DL_LIB_P;
typedef char DL_CHAR;
# ifdef _MACOSX
# define OPENGL_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"
# define OPENGLU_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib"
# else
# define OPENGL_LIB "libGL.so.1"
# define OPENGLU_LIB "libGLU.so.1"
# endif
#endif
extern "C" {
DRIVER_INIT(EGL_DRIVER) {
return NULL;
}
}
int egl_init_opengl(void *erlCallbacks)
{
#ifdef _WIN32
driver_init((TWinDynDriverCallbacks *) erlCallbacks);
#endif
if(egl_initiated == 0) {
if(load_gl_functions()) {
init_tess();
egl_initiated = 1;
}
}
return 1;
}
int load_gl_functions() {
DL_CHAR * DLName = (DL_CHAR *) OPENGL_LIB;
DL_LIB_P LIBhandle = dlopen(DLName, RTLD_LAZY);
//fprintf(stderr, "Loading GL: %s\r\n", (const char*)DLName);
void * func = NULL;
int i;
if(LIBhandle) {
for(i=0; gl_fns[i].name != NULL; i++) {
if((func = dlsym(LIBhandle, gl_fns[i].name))) {
* (void **) (gl_fns[i].func) = func;
// fprintf(stderr, "GL LOADED %s \r\n", gl_fns[i].name);
} else {
if(gl_fns[i].alt != NULL) {
if((func = dlsym(LIBhandle, gl_fns[i].alt))) {
* (void **) (gl_fns[i].func) = func;
// fprintf(stderr, "GL LOADED %s \r\n", gl_fns[i].alt);
} else {
* (void **) (gl_fns[i].func) = (void *) &gl_error;
// fprintf(stderr, "GL Skipped %s and %s \r\n", gl_fns[i].name, gl_fns[i].alt);
};
} else {
* (void **) (gl_fns[i].func) = (void *) &gl_error;
// fprintf(stderr, "GL Skipped %s \r\n", gl_fns[i].name);
}
}
}
// dlclose(LIBhandle);
// fprintf(stderr, "OPENGL library is loaded\r\n");
} else {
fprintf(stderr, "Could NOT load OpenGL library: %s\r\n", DLName);
};
DLName = (DL_CHAR *) OPENGLU_LIB;
LIBhandle = dlopen(DLName, RTLD_LAZY);
// fprintf(stderr, "Loading GLU: %s\r\n", (const char*)DLName);
func = NULL;
if(LIBhandle) {
for(i=0; glu_fns[i].name != NULL; i++) {
if((func = dlsym(LIBhandle, glu_fns[i].name))) {
* (void **) (glu_fns[i].func) = func;
} else {
if(glu_fns[i].alt != NULL) {
if((func = dlsym(LIBhandle, glu_fns[i].alt))) {
* (void **) (glu_fns[i].func) = func;
} else {
* (void **) (glu_fns[i].func) = (void *) &gl_error;
// fprintf(stderr, "GLU Skipped %s\r\n", glu_fns[i].alt);
};
} else {
* (void **) (glu_fns[i].func) = (void *) &gl_error;
// fprintf(stderr, "GLU Skipped %s\r\n", glu_fns[i].name);
}
}
}
// dlclose(LIBhandle);
// fprintf(stderr, "GLU library is loaded\r\n");
} else {
fprintf(stderr, "Could NOT load OpenGL GLU library: %s\r\n", DLName);
};
return 1;
}
void gl_error() {
// fprintf(stderr, "OpenGL Extension not available \r\n");
throw "undef_extension";
}
/* *******************************************************************************
* GLU Tesselation special
* ******************************************************************************/
static GLUtesselator* tess;
typedef struct {
GLdouble * tess_coords;
int alloc_n;
int alloc_max;
int * tess_index_list;
int index_n;
int index_max;
int error;
} egl_tess_data;
#define NEED_MORE_ALLOC 1
#define NEED_MORE_INDEX 2
static egl_tess_data egl_tess;
void CALLBACK
egl_ogla_vertex(GLdouble* coords)
{
/* fprintf(stderr, "%d\r\n", (int) (coords - tess_coords) / 3); */
if(egl_tess.index_n < egl_tess.index_max) {
egl_tess.tess_index_list[egl_tess.index_n] = (int) (coords - egl_tess.tess_coords) / 3;
egl_tess.index_n++;
}
else
egl_tess.error = NEED_MORE_INDEX;
}
void CALLBACK
egl_ogla_combine(GLdouble coords[3],
void* vertex_data[4],
GLfloat w[4],
void **dataOut)
{
GLdouble* vertex = &egl_tess.tess_coords[egl_tess.alloc_n];
if(egl_tess.alloc_n < egl_tess.alloc_max) {
egl_tess.alloc_n += 3;
vertex[0] = coords[0];
vertex[1] = coords[1];
vertex[2] = coords[2];
*dataOut = vertex;
#if 0
fprintf(stderr, "combine: ");
int i;
for (i = 0; i < 4; i++) {
if (w[i] > 0.0) {
fprintf(stderr, "%d(%g) ", (int) vertex_data[i], w[i]);
}
}
fprintf(stderr, "\r\n");
fprintf(stderr, "%g %g %g\r\n", vertex[0], vertex[1], vertex[2]);
#endif
} else {
egl_tess.error = NEED_MORE_ALLOC;
*dataOut = NULL;
}
}
void CALLBACK
egl_ogla_edge_flag(GLboolean flag)
{
}
void CALLBACK
egl_ogla_error(GLenum errorCode)
{
// const GLubyte *err;
// err = gluErrorString(errorCode);
// fprintf(stderr, "Tesselation error: %d: %s\r\n", (int) errorCode, err);
}
void init_tess()
{
tess = gluNewTess();
gluTessCallback(tess, GLU_TESS_VERTEX, (GLUfuncptr) egl_ogla_vertex);
gluTessCallback(tess, GLU_TESS_EDGE_FLAG, (GLUfuncptr) egl_ogla_edge_flag);
gluTessCallback(tess, GLU_TESS_COMBINE, (GLUfuncptr) egl_ogla_combine);
gluTessCallback(tess, GLU_TESS_ERROR, (GLUfuncptr) egl_ogla_error);
}
void exit_tess()
{
gluDeleteTess(tess);
}
int erl_tess_impl(char* buff, ErlDrvPort port, ErlDrvTermData caller)
{
ErlDrvBinary* bin;
int i;
int num_vertices;
GLdouble *n;
int AP;
int a_max = 2;
int i_max = 6;
num_vertices = * (int *) buff; buff += 8; /* Align */
n = (double *) buff; buff += 8*3;
egl_tess.alloc_max = a_max*num_vertices*3;
bin = driver_alloc_binary(egl_tess.alloc_max*sizeof(GLdouble));
egl_tess.error = 0;
egl_tess.tess_coords = (double *) bin->orig_bytes;
memcpy(egl_tess.tess_coords,buff,num_vertices*3*sizeof(GLdouble));
egl_tess.index_max = i_max*3*num_vertices;
egl_tess.tess_index_list = (int *) driver_alloc(sizeof(int) * egl_tess.index_max);
egl_tess.tess_coords = (double *) bin->orig_bytes;
egl_tess.index_n = 0;
egl_tess.alloc_n = num_vertices*3;
gluTessNormal(tess, n[0], n[1], n[2]);
gluTessBeginPolygon(tess, 0);
gluTessBeginContour(tess);
for (i = 0; i < num_vertices; i++) {
gluTessVertex(tess, egl_tess.tess_coords+3*i, egl_tess.tess_coords+3*i);
}
gluTessEndContour(tess);
gluTessEndPolygon(tess);
AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData) * (13+egl_tess.index_n*2));
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(i=0; i < egl_tess.index_n; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) egl_tess.tess_index_list[i];
};
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = egl_tess.index_n+1;
rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) bin;
rt[AP++] = egl_tess.alloc_n*sizeof(GLdouble); rt[AP++] = 0;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Return tuple {list, Bin}
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Result tuple
driver_send_term(port,caller,rt,AP);
/* fprintf(stderr, "List %d: %d %d %d \r\n", */
/* res, */
/* n_pos, */
/* (tess_alloc_vertex-new_vertices)*sizeof(GLdouble), */
/* num_vertices*6*sizeof(GLdouble)); */
driver_free_binary(bin);
driver_free(egl_tess.tess_index_list);
driver_free(rt);
return 0;
}