[PD-cvs] externals/pdp/opengl/system Makefile, 1.2, 1.3 pdp_3Dcontext_common.c, 1.2, 1.3 pdp_3Dcontext_glx.c, 1.2, 1.3 pdp_3dp_base.c, 1.2, 1.3 pdp_mesh.c, 1.2, 1.3 pdp_opengl.c, 1.2, 1.3 pdp_texture.c, 1.2, 1.3 setup.c, 1.2, 1.3

Hans-Christoph Steiner eighthave at users.sourceforge.net
Fri Dec 16 02:05:38 CET 2005


Update of /cvsroot/pure-data/externals/pdp/opengl/system
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6756/opengl/system

Added Files:
	Makefile pdp_3Dcontext_common.c pdp_3Dcontext_glx.c 
	pdp_3dp_base.c pdp_mesh.c pdp_opengl.c pdp_texture.c setup.c 
Log Message:
checking in pdp 0.12.4 from http://zwizwa.fartit.com/pd/pdp/pdp-0.12.4.tar.gz

--- NEW FILE: pdp_3Dcontext_common.c ---

/*
 *   OpenGL Extension Module for pdp - pbuffer packet implementation
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
  this code uses glx. i don't know if it is worth to take into
  account portabiliy. since it will take a while until pdp runs
  on anything else than linux. but in any case, providing a windows/osx
  implementation here should not be too difficult..
*/

#include "pdp_3Dcontext.h"
#include <GL/gl.h>
//#include <GL/glx.h>
#include <GL/glu.h>
//#include <GL/glut.h>

#define D if (0)

/* constructor */

/* pbuf operators */

u32 pdp_packet_3Dcontext_width(int packet)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (c) return c->width;
    else return 0;
}

u32 pdp_packet_3Dcontext_height(int packet)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (c) return c->height;
    else return 0;
}

u32 pdp_packet_3Dcontext_subwidth(int packet)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (c) return c->sub_width;
    else return 0;
}


u32 pdp_packet_3Dcontext_subheight(int packet)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (c) return c->sub_height;
    else return 0;
}


void  pdp_packet_3Dcontext_set_subwidth(int packet, u32 w)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (c) c->sub_width = w;
}


void pdp_packet_3Dcontext_set_subheight(int packet, u32 h)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (c)  c->sub_height = h;
}


float pdp_packet_3Dcontext_subaspect(int packet)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (c) return (float)c->sub_width/c->sub_height;
    else return 0;
}

int pdp_packet_3Dcontext_isvalid(int packet)
{
    t_pdp *header = pdp_packet_header(packet);
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);

    if (!header) return 0;
    if (!c) return 0;
    if (PDP_3DCONTEXT != header->type) return 0;
    return 1;
}

t_3Dcontext *pdp_packet_3Dcontext_info(int packet)
{
    t_pdp *header = pdp_packet_header(packet);
    if (!header) return 0;
    if (PDP_3DCONTEXT != header->type) return 0;
    return (t_3Dcontext *)&header->info.raw;
}



void pdp_llconv_flip_top_bottom(char *data, int width, int height, int pixelsize);

int pdp_packet_3Dcontext_snap_to_bitmap(int packet, int w, int h)
{
    int x, y, new_p, i;
    char *data = 0;
    // char r;
    // int extra = 5;

    if (!pdp_packet_3Dcontext_isvalid(packet)) goto error;

    x = pdp_packet_3Dcontext_subwidth(packet);
    y = pdp_packet_3Dcontext_subheight(packet);

    x = (x - w) >> 1;
    y = (y - h) >> 1;
    x = (x < 0 ) ? 0 : x;
    y = (y < 0 ) ? 0 : y;

    new_p = pdp_packet_new_bitmap_rgb(w, h);
    data = (char *)pdp_packet_data(new_p);
    if (-1 == new_p || !data) goto error;
    pdp_packet_3Dcontext_set_rendering_context(packet);

    // D post("BEGIN READPIXELS %d %d %d %d %x", w, h, x, y, data);
    
    //for (i=0; i<w*h; i++){
    //	data[3*i] = 255;
    //	data[3*i+1] = 255;
    //	data[3*i+2] = 0;
    //}
    // r = random();
    // data[w*h*3] = r;

    /* seems nvidia drivers 4191 have a bug
       when w % 4 is not zero */
    glReadPixels(x,y, w ,h,GL_RGB,GL_UNSIGNED_BYTE, data);

    /* inplace swap top to bottom (textures and buffers have
       another coordinate system than standard images)
       instead of fixing this by using a different texture coordinate
       system, a memory swap is performed. this is more expensive
       but eliminates hassle when converting between buffers, textures
       and bitmaps */

    pdp_llconv_flip_top_bottom(data, w, h, 3);

    // if (r != data[w*h*3]) post("PANIC");

    // post("END READPIXELS %d %d", w, h);


    return new_p;

 error:
    return -1;
    
}




/* move these to the pdp_3d_context object: they're too specific */

/* setup for 2d operation from pbuf dimensions */
void pdp_packet_3Dcontext_setup_2d_context(int p)
{
    u32 w;
    u32 h;
    float asp;
    if (!pdp_packet_3Dcontext_isvalid(p)) return;
    w = pdp_packet_3Dcontext_subwidth(p);
    h = pdp_packet_3Dcontext_subheight(p);
    asp = pdp_packet_3Dcontext_subaspect(p);


    /* set the viewport to the size of the sub frame */
    glViewport(0, 0, w, h);

    /* set orthogonal projection, with a relative frame size of (2asp x 2) */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, 2*asp, 0, 2);

    /* set the center of view */
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(asp, 1, 0);
    glScalef(1,-1,1);


}

/* setup for 3d operation from pbuf dimensions */
void pdp_packet_3Dcontext_setup_3d_context(int p)
{
    u32 w;
    u32 h;
    int i;
    float asp;
    float m_perspect[] = {-1.f, /* left */
			  1.f,  /* right */
			  -1.f, /* bottom */
			  1.f,  /* top */
			  1.f,  /* front */
			  20.f};/* back */

    if (!pdp_packet_3Dcontext_isvalid(p)) return;
    w = pdp_packet_3Dcontext_subwidth(p);
    h = pdp_packet_3Dcontext_subheight(p);
    asp = pdp_packet_3Dcontext_subaspect(p);


    /* set the viewport to the size of the sub frame */
    glViewport(0, 0, w, h);

    /* set orthogonal projection, with a relative frame size of (2asp x 2) */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(m_perspect[0] * asp, m_perspect[1] * asp,       // left, right
	      m_perspect[2], m_perspect[3],                   // bottom, top
	      m_perspect[4], m_perspect[5]);                  // front, back
    
    /* reset texture matrix */
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();


    /* set the center of view */
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0, 0, 4, 0, 0, 0, 0, 1, 0);
    //glTranslatef(asp, 1, 0);


    glEnable(GL_DEPTH_TEST);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);
    glShadeModel(GL_SMOOTH);
    //glShadeModel(GL_FLAT);


    /* disable everything that is enabled in other modules
     this resets the ogl state to its initial conditions */
    glDisable(GL_LIGHTING);
    for (i=0; i<8; i++) glDisable(GL_LIGHT0 + i);
    glDisable(GL_COLOR_MATERIAL);


}


void pdp_3Dcontext_common_setup(void)
{
}

--- NEW FILE: pdp_mesh.c ---
/*
 *   Pure Data Packet module. mesh implementation
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* a very naive approach to triangular meshes */


// $$TODO: some serious memory corruption in this file our the list implementation


#include <math.h>

#include "pdp.h"
#include "pdp_mesh.h"


/* VERTEX methods */
void vertex_add_triangle(t_vertex *v, t_triangle *t)
{
    pdp_list_add_pointer(v->trilist, t);
}
void vertex_remove_triangle(t_vertex *v, t_triangle *t)
{
    pdp_list_remove_pointer(v->trilist, t);
}
void vertex_add_neighbour(t_vertex *v, t_vertex *neighbour)
{
    pdp_list_add_pointer_to_set(v->vertlist, neighbour);
};


/* constructor/destructors are "private"
   they may only be called by the mesh object to ensure
   the vector list stays sound (i.e. without duplicates) */
void _vertex_free(t_vertex *v)
{
    if (!v->trilist) post("WARNING: vertex %x has empty trilist", v);
    else{
        pdp_list_free(v->trilist);
	v->trilist = 0;
    }
    if (!v->vertlist) post("WARNING: vertex %x has empty vertlist", v);
    {
	pdp_list_free(v->vertlist);
	v->vertlist = 0;
    }
    pdp_dealloc(v);
}

t_vertex *_vertex_new(float *c, float *n)
{
    int k;
    t_vertex *v = (t_vertex *)  pdp_alloc(sizeof(t_vertex));
    I3(k) v->c[k] = c[k];
    I3(k) v->n[k] = n[k];
    v->trilist = pdp_list_new(0);
    v->vertlist = pdp_list_new(0);
    return v;
}


void vertex_compute_normal_random(t_vertex *v){int k; I3(k) v->n[k] = _rand();}
void vertex_compute_normal_sphere(t_vertex *v){int k; I3(k) v->n[k] = v->c[k];}
void vertex_compute_normal_prism(t_vertex *v)
{
    float scale = 0.0f;
    float sum[] = {0.0f, 0.0f, 0.0f};
    int k;
    t_pdp_atom* i;
    t_pdp_list *vl = v->vertlist;
    t_vertex *vtx;

    PDP_POINTER_IN(vl, i, vtx) {
	I3(k) sum[k] += vtx->c[k];
	scale = scale + 1.0f;
    }
    scale = 1.0f / scale;
    I3(k) sum[k] *= scale;
    I3(k) v->n[k] = v->c[k] - sum[k];
	
    //post("computed normal (%f, %f, %f) of vertex (%f, %f, %f)", v->n[0], v->n[1], v->n[2], v->c[0], v->c[1], v->c[2]);
};
void vertex_compute_normal_average(t_vertex *v)
{
    int triangles = pdp_list_size(v->trilist);
    float scale = 1.0f / ((float)triangles);
    t_pdp_atom* i;
    int k;
    t_triangle *t;

    I3(k) v->n[k] = 0; //reset normal
    PDP_POINTER_IN(v->trilist, i, t){
	I3(k) v->n[k] += t->n[k];
    }
    _vector3_scale(v->n, scale);
    
    
}


float vertex_normalize(t_vertex *v)
{
    return _vector3_normalize(v->c);
}


/* TRIANGLE methods */

/* create triangle (a connection between 3 vertices): 
   counterclockwize with facing front
   this method is "private"
   you can only create triangles as part of a mesh */
t_triangle *_triangle_new(t_vertex *v0, t_vertex *v1, t_vertex *v2)
{
    int k;

    t_triangle *t = (t_triangle *)pdp_alloc(sizeof(t_triangle));

    /* store vertex references */
    t->v[0] = v0;
    t->v[1] = v1;
    t->v[2] = v2;

    /* reset median vertices */
    I3(k) t->m[k] = 0;

    /* connect triangle to vertices */
    vertex_add_triangle(v0, t);
    vertex_add_triangle(v1, t);
    vertex_add_triangle(v2, t);

    /* connect vertices to vertices */
    vertex_add_neighbour(v0, v1);
    vertex_add_neighbour(v0, v2);
    vertex_add_neighbour(v1, v0);
    vertex_add_neighbour(v1, v2);
    vertex_add_neighbour(v2, v0);
    vertex_add_neighbour(v2, v1);
    
    return t;
}

/* delete a triangle, disconnecting the vertices */
void _triangle_free(t_triangle *t)
{
    int k;

    /* remove the triangle reference of the vertices */
    I3(k) vertex_remove_triangle(t->v[k], t);

    /* set references to zero (bug catcher) */
    I3(k) t->v[k] = 0;
    I3(k) t->m[k] = 0;

    /* free struct */
    pdp_dealloc(t);
    
}

/* get triangle that shares the link between v0 and v1 */
t_triangle *triangle_neighbour(t_triangle *t, t_vertex *v0, t_vertex *v1)
{
    t_pdp_atom* it;
    t_triangle *tri;
    PDP_POINTER_IN(v1->trilist, it, tri){
	if (tri != t && pdp_list_contains_pointer(v0->trilist, tri)) return tri;
    }
    return 0;
}

/* add a median vector to a link in a triangle 
   note: vertices must be in triangle, or behaviour is undefined */
void triangle_add_median(t_triangle *t, t_vertex *v0, t_vertex *v1, t_vertex *median)
{

    /* link 0 1 */
    if (!((v0 == t->v[2]) || (v1 == t->v[2]))) t->m[0] = median;

    /* link 1 2 */
    else if (!((v0 == t->v[0]) || (v1 == t->v[0]))) t->m[1] = median;

    /* link 2 0 */
    else t->m[2] = median;
}

void triangle_compute_normal(t_triangle *t)
{
    int k;
    float v0[3];
    float v1[3];
    I3(k) v0[k] = t->v[1]->c[k] - t->v[0]->c[k];
    I3(k) v1[k] = t->v[2]->c[k] - t->v[0]->c[k];
    _vector3_cross(v0,v1,t->n);
}

void triangle_compute_unit_normal(t_triangle *t)
{
    triangle_compute_normal(t);
    _vector3_normalize(t->n);
}

/* MESH methods */

/* add and remove methods for vertices and triangles */
t_vertex *mesh_vertex_add(t_mesh *m, float *c, float *n)
{
    t_vertex *v = _vertex_new(c, n);
    pdp_list_add_pointer(m->vertices, v);
    return v;
}

void mesh_vertex_remove(t_mesh *m, t_vertex *v)
{
    pdp_list_remove_pointer(m->vertices, v);
    _vertex_free(v);
}

t_triangle *mesh_triangle_add(t_mesh *m, t_vertex *v0, t_vertex *v1, t_vertex *v2)
{
    t_triangle *t = _triangle_new(v0,v1,v2);
    pdp_list_add_pointer(m->triangles, t);
    return t;
}

void mesh_triangle_remove(t_mesh *m, t_triangle *t)
{
    pdp_list_remove_pointer(m->triangles, t);
    _triangle_free(t);
}

/* calculate normals */
void mesh_calculate_normals(t_mesh *m)
{
    t_pdp_atom* it;
    t_pdp_atom* it_tri;
    t_pdp_list *l = m->vertices;
    t_pdp_list *l_tri = m->triangles;
    t_vertex *v;
    t_triangle *t;
    //while (v = pdp_list_getnext_pointer(l, &it)) vertex_compute_normal_sphere(v);
    switch(m->normal_type){
    default:
    case MESH_NORMAL_SPHERE:  PDP_POINTER_IN(l, it, v) vertex_compute_normal_sphere(v); break;
    case MESH_NORMAL_PRISM:   PDP_POINTER_IN(l, it, v) vertex_compute_normal_prism(v); break;
    case MESH_NORMAL_RANDOM:  PDP_POINTER_IN(l, it, v) vertex_compute_normal_random(v); break;
    case MESH_NORMAL_AVERAGE: 
	PDP_POINTER_IN(l_tri, it_tri, t) triangle_compute_unit_normal(t); 
	PDP_POINTER_IN(l, it, v) vertex_compute_normal_average(v); 
	break;
    }
}

/* split a triangle in 4, using the intermedia median vertex storage */
void mesh_split_four(t_mesh *m, t_triangle *old_t)
{
    int k;
    t_vertex *v[6];

    /* some intermediates */
    t_triangle *neighbour;
    t_float newv[] = {0,0,0};
    t_float nullvect[] = {0,0,0};


    /* get main vertices */
    I3(k) v[k] = old_t->v[k];

    /* get median vertices inserted by neighbouring triangles */
    I3(k) v[k+3] = old_t->m[k];

#define GET_MEDIAN(v, v0, v1)					\
    if (!v){							\
	I3(k) newv[k] = 0.5f * (v0->c[k] + v1->c[k]);		\
	v = mesh_vertex_add(m, newv, nullvect);			\
	/*vertex_normalize(v);*/				\
	if (neighbour = triangle_neighbour(old_t, v0, v1)){	\
	    triangle_add_median(neighbour, v0, v1, v);		\
	}							\
    }
    
    GET_MEDIAN(v[3], v[0], v[1])
    GET_MEDIAN(v[4], v[1], v[2])
    GET_MEDIAN(v[5], v[2], v[0])

#undef GET_MEDIAN

    /* remove the old triangle */
    mesh_triangle_remove(m, old_t);

    /* create 4 new triangles */
    mesh_triangle_add(m, v[0], v[3], v[5]);
    mesh_triangle_add(m, v[1], v[4], v[3]);
    mesh_triangle_add(m, v[2], v[5], v[4]);
    mesh_triangle_add(m, v[3], v[4], v[5]);

}

/* split a triangle in 3 */
void mesh_split_three(t_mesh *m, t_triangle *old_t)
{
    int k, l;
    t_vertex *v[4];
    t_float newv[] = {0,0,0};
    t_float nullvect[] = {0,0,0};

    /* get vertices */
    I3(k) v[k] = old_t->v[k];

    /* remove a triangle */
    mesh_triangle_remove(m, old_t);

    /* compute new vertex coordinates */
    I3(k) I3(l) newv[k] += 0.33333f * v[l]->c[k];
    
    /* create new vertex */
    v[3] = mesh_vertex_add(m, newv, nullvect);
    //vertex_normalize(v[3]);

    /* create 3 new triangles */
    mesh_triangle_add(m, v[0], v[1], v[3]);
    mesh_triangle_add(m, v[1], v[2], v[3]);
    mesh_triangle_add(m, v[2], v[0], v[3]);

}



void mesh_split_all_four(t_mesh *m)
{
    t_triangle *t;
    t_pdp_list *l = pdp_list_copy(m->triangles);

    //post("split_all_four: nb triangles %d", pdp_list_size(m->triangles));

    while (l->elements){
	t = pdp_list_pop(l).w_pointer;
	mesh_split_four(m, t);
    }
    mesh_calculate_normals(m);
    pdp_list_free(l);
}


void mesh_split_all_three(t_mesh *m)
{
    t_triangle *t;
    t_pdp_list *l = pdp_list_copy(m->triangles);

    //post("split_all_three: nb triangles %d", pdp_list_size(m->triangles));

    while (l->elements){
	t = pdp_list_pop(l).w_pointer;
	mesh_split_three(m, t);
    }
    mesh_calculate_normals(m);
    pdp_list_free(l);
}

void mesh_split_random_three(t_mesh *m)
{
    int size = pdp_list_size(m->triangles);
    t_triangle *t = pdp_list_index(m->triangles, (random() % size)).w_pointer;
    mesh_split_three(m, t);
    mesh_calculate_normals(m);
}



void mesh_free(t_mesh *m)
{
    t_pdp_list *l;
    t_triangle *t;
    t_vertex *v;

    /* delete all triangles */
    while (m->triangles->elements){
	t = pdp_list_pop(m->triangles).w_pointer;
	//post("freeing triangle %x", t);
	_triangle_free(t);
    }
    pdp_list_free(m->triangles);
    m->triangles = 0;

    /* delete all vertices */
    while (m->vertices->elements){
	v = pdp_list_pop(m->vertices).w_pointer;
	//post("freeing vertex %x", v);
	_vertex_free(v);
    }
    pdp_list_free(m->vertices);
    m->vertices = 0;

    pdp_dealloc(m);

}


t_mesh *_mesh_new(void)
{
    t_mesh *m = (t_mesh *)pdp_alloc(sizeof(t_mesh));

    /* create main vertex and triangle lists */
    m->triangles = pdp_list_new(0);
    m->vertices = pdp_list_new(0);

    /* set normal type */
    m->normal_type = MESH_NORMAL_PRISM;

    return m;
}

/* init tetra */
t_mesh *mesh_new_tetra(void)
{
    int k;
    t_triangle *t[4];
    t_vertex *v[4];
    t_pdp_atom* it;
    t_triangle *tri;
    t_mesh *m = _mesh_new();

    float n[] = {0,0,0};
    float fv[4][3] = {{2,0,0},{0,2,0},{0,0,2}, {-1,-1,-1}};

    /* add vertices */
    I4(k) v[k] = mesh_vertex_add(m, &fv[k][0], n);
    I4(k) vertex_normalize(v[k]);

    /* add triangles */
    mesh_triangle_add(m, v[0], v[1], v[2]);
    mesh_triangle_add(m, v[1], v[0], v[3]);
    mesh_triangle_add(m, v[0], v[2], v[3]);
    mesh_triangle_add(m, v[1], v[3], v[2]);


    /* compute normals */
    mesh_calculate_normals(m);

    return m;
}


void _mesh_relax_compute_resultant_spring(t_mesh *m, float *center, float d0, float r0)
{
    int k;
    t_pdp_atom *i, *j;
    t_vertex *v, *w;
    
    PDP_POINTER_IN(m->vertices, i, v){
	float scale = 0.0f;
	float r;
	
	/* compute contribution of origin link */
	I3(k) v->n[k] = v->c[k] - center[k];
	r = _vector3_normalize(v->n);
	I3(k) v->n[k] *= (r0 - r);

	PDP_POINTER_IN(v->vertlist, j, w){
	    int k;
	    float f[3];
	    float d, l;

	    /* compute force contribution of one link (model: spring with rest length == d0) */
	    I3(k) f[k] = w->c[k] - v->c[k];        // PC: f == distance vector
	    d = _vector3_normalize(f);               // PC: d == distance, vector == unit norm
	    I3(k) v->n[k] += (d - d0) * f[k];      // PC: n == n_prev + fource resultant
	}
    }
}

void _mesh_relax_apply_force(t_mesh *m, float k)
{
    t_pdp_atom* it;
    t_vertex *v;
    
    PDP_POINTER_IN(m->vertices, it, v){
	int i;
	/* apply fource vector with step */
	I3(i) v->c[i] += k * v->n[i];
    }

}

void mesh_compute_center(t_mesh *m, float *c)
{
    t_pdp_atom*(it);
    t_vertex *v;
    float scale;
    int k;

    I3(k) c[k] = 0;
    PDP_POINTER_IN(m->vertices, it, v){
	I3(k) c[k] += v->c[k];
    }
    scale = 1.0f / ((float)pdp_list_size(m->vertices));
    I3(k) c[k] *= scale;

}

void mesh_translate(t_mesh *m, float *c)
{
    t_pdp_atom *it;
    t_vertex *v;
    int k;

    PDP_POINTER_IN(m->vertices, it, v){
	I3(k) v->c[k] += c[k];
    }
}

/* relax a mesh (move toward equal link length) */
void mesh_relax(t_mesh *m, float step, float d0, float r0)
{
    int k;
    float c[3];
    mesh_compute_center(m, c);
    I3(k) c[k] = -c[k];
    mesh_translate(m, c);
    I3(k) c[k] = 0;
    _mesh_relax_compute_resultant_spring(m, c, d0, r0); /* compute force resultant */
    _mesh_relax_apply_force(m, step);    /* apply "time step towards desired distance" */
    mesh_calculate_normals(m);        /* restore normals */
}



/* print some debug information */
void mesh_debug(t_mesh *m)
{
    int k;
    int boundary_edges = 0;
    t_pdp_atom* it;
    t_triangle *t;
    post("mesh info");
    post("\tnumber of vertices = %d", pdp_list_size(m->vertices));
    post("\tnumber of triangles = %d", pdp_list_size(m->triangles));

    PDP_POINTER_IN(m->triangles, it, t){
	I3(k) if (!triangle_neighbour(t, t->v[k], t->v[(k+1)%3])) boundary_edges++;
    }
    post("\tnumber of boundaray edges = %d", boundary_edges);

    
}

--- NEW FILE: Makefile ---
include ../Makefile.config

all:	pdp_texture.o pdp_3Dcontext_glx.o pdp_3Dcontext_common.o \
	pdp_opengl.o pdp_3dp_base.o pdp_mesh.o setup.o

clean:
	rm -rf *~ *.o


--- NEW FILE: setup.c ---
#include "pdp_opengl.h"

/* 3dp overview:

 - texture packets (gl)
 - drawable packets (glX windows and pbufs)

 the 3dp system connects to a display server and creates a common context
 this can be a pbuf context (if supported, glx >= 1.3) or a normal glX context
 textures are standard opengl
 drawable packets are wrappers around glx drawables (windows or pbufs)
 they share the central display connection and rendering context

*/


#ifdef __cplusplus
extern "C"
{
#endif

/* opengl lib kernel setup */
void pdp_opengl_system_setup(void);

/* packet type setup */
void pdp_3Dcontext_glx_setup(void); /* glx specific part of the 3D context packet */
void pdp_3Dcontext_common_setup(void); /* common part of the 3D context packet */
void pdp_texture_setup(void); /* texture packet */


/* module setup */
void pdp_3d_windowcontext_setup(void);
void pdp_3d_draw_setup(void);
void pdp_3d_view_setup(void);
void pdp_3d_light_setup(void);
void pdp_3d_color_setup(void);
void pdp_3d_push_setup(void);
void pdp_3d_snap_setup(void);
void pdp_3d_dlist_setup(void);
void pdp_3d_drawmesh_setup(void);
void pdp_3d_for_setup(void);
void pdp_3d_state_setup(void);
void pdp_3d_subcontext_setup(void);


    //#define D(x) { pdp_post_n( #x ".." ); x; pdp_post("done"); }
#define D(x) x

void pdp_opengl_setup(void)
{
    int i;
    post("PDP: pdp_opengl extension library");

    /* setup system */
    D(pdp_opengl_system_setup());

    /* setup packet types */
    D(pdp_3Dcontext_glx_setup());
    D(pdp_3Dcontext_common_setup());
    D(pdp_texture_setup());


    /* setup modules */
    D(pdp_3d_windowcontext_setup());
    D(pdp_3d_draw_setup());
    D(pdp_3d_view_setup());
    D(pdp_3d_push_setup());
    D(pdp_3d_light_setup());
    D(pdp_3d_dlist_setup());
    D(pdp_3d_color_setup());
    D(pdp_3d_snap_setup());
    D(pdp_3d_drawmesh_setup());
    D(pdp_3d_for_setup());
    D(pdp_3d_state_setup());
    D(pdp_3d_subcontext_setup());


}


#ifdef __cplusplus
}
#endif

--- NEW FILE: pdp_3dp_base.c ---
#include "pdp_opengl.h"
#include "pdp_3dp_base.h"

#define THIS(b) t_pdp_3pd_base *b = (t_pdp_3pd_base *)x

/* destructor */
void pdp_3dp_base_free(void *x)
{
    // free super
    pdp_dpd_base_free(x);
}

/* init method */
void pdp_3dp_base_init(void *x)
{
    // init super
    pdp_dpd_base_init(x);

    // set processing queue to pdp_opengl system queue
    pdp_dpd_base_set_queue(x, pdp_opengl_get_queue());

}

/* class setup method */
void pdp_3dp_base_setup(t_class *class)
{
    // setup super
    pdp_dpd_base_setup(class);
}


--- NEW FILE: pdp_texture.c ---
/*
 *   OpenGL Extension Module for pdp - texture packet implementation
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


/* this modules implemtents the opengl texture packet
   it contains only portable opengl code */

#include <stdio.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "pdp_opengl.h"
#include "pdp_texture.h"
#include "pdp_dpd_command.h"


static t_pdp_class *texture_class;

static t_pdp_dpd_commandfactory _tex_cf;

typedef struct _texture_command
{
    t_pdp_dpd_command base;
    int p_src;
    int p_dst;
} t_texture_command;


/* all symbols are C-style */
#ifdef __cplusplus
extern "C"
{
#endif



/* returns a pointer to the packet subheader given the pdp header */
static t_texture *_pdp_tex_info(t_pdp *x)
{
    return (t_texture *)&(x->info.raw);
}


/* create a pow of 2 texture dimension, ge than 8 */
static int _round_to_pow_2(int n){
    int r = 8;
    while (n > r) r <<= 1;
    return r;
}

t_pdp_symbol *_pdp_get_tex_description_from_params(GLsizei width, GLsizei height, GLint format)
{
    char description[1024];
    char *c = description;
    
    c += sprintf(c, "texture");
    switch(format){
    case GL_LUMINANCE: c += sprintf(c, "/grey"); break;
    case GL_RGB:       c += sprintf(c, "/rgb"); break;
    case GL_RGBA:      c += sprintf(c, "/rgba"); break;
    default:
	c += sprintf(c, "/unknown"); goto exit;
    }
    c += sprintf(c, "/%dx%d", width, height);
	    
 exit:
    return pdp_gensym(description);
}

t_pdp_symbol *_pdp_tex_get_description(t_pdp *header)
{
    t_texture *texture = _pdp_tex_info(header);
    int encoding;

    if (!header) return pdp_gensym("invalid");
    else if (!header->desc){
	if (header->type == PDP_TEXTURE){
	    /* if description is not defined, try to construct it */
	    return _pdp_get_tex_description_from_params(texture->width, texture->height, texture->format);
	}
	else return pdp_gensym("unknown");
    }
    else return header->desc;
}


static int _pdp_packet_texture_old_or_dummy(u32 width, u32 height, s32 format);
static void _pdp_packet_gentexture(int packet);

static void texture_command_convert_bitmap_to_texture(t_texture_command *c)
{
    t_texture *t = (t_texture *)pdp_packet_subheader(c->p_dst);

    /* make sure packet contains a texture, since it is created with _pdp_packet_reuse_texture */
    _pdp_packet_gentexture(c->p_dst);

    /* flip source image before uploading */
    pdp_packet_bitmap_flip_top_bottom(c->p_src);

    /* fill texture */
    pdp_packet_texture_make_current(c->p_dst);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, t->sub_width, t->sub_height, 
		    t->format, GL_UNSIGNED_BYTE, (char *)pdp_packet_data(c->p_src));

    /* decrease refcount */
    pdp_packet_mark_unused(c->p_src);
    pdp_packet_mark_unused(c->p_dst);

    //post("conversion done");
    pdp_dpd_command_suicide(c);
}


/* converters to standard pdp types */
int _pdp_packet_texture_convert_image_to_texture(int packet, t_pdp_symbol *dest_template)
{
    int p_temp, p;

    //post ("converting to bitmap");
    p_temp = pdp_packet_convert_rw(packet, pdp_gensym("bitmap/*/*"));
    if (p_temp == -1) return -1;

    //post ("converting to texture");
    p = pdp_packet_convert_rw(p_temp, pdp_gensym("texture/*/*"));
    pdp_packet_mark_unused(p_temp);
    return p;
}



/* converters to standard pdp types */
int _pdp_packet_texture_convert_bitmap_to_texture(int packet, t_pdp_symbol *dest_template)
{
    t_pdp *header = pdp_packet_header(packet);
    void  *data   = pdp_packet_data  (packet);
    int new_p;
    u32 w;
    u32 h;
    t_texture_command *c;

    if (!pdp_packet_bitmap_isvalid(packet)) return -1;

    w = header->info.image.width;
    h = header->info.image.height;

    switch (header->info.image.encoding){
    case PDP_BITMAP_GREY:
	/* create greyscale texture */
	new_p = _pdp_packet_texture_old_or_dummy(w,h, GL_LUMINANCE);
	break;
    case PDP_BITMAP_RGB:
	/* create rgb texture */
	new_p = _pdp_packet_texture_old_or_dummy(w,h, GL_RGB);
	break;
    case PDP_BITMAP_RGBA:
	/* create greyscale texture */
	new_p = _pdp_packet_texture_old_or_dummy(w,h, GL_RGBA);
	break;
    default:
	new_p = -1;
	break;
    }

    if (new_p != -1){

	/* remark: this is a hack. a texture has to be created
	   when a rendering context is active. this means it has
	   to be created in the correct thread. therefore a dpd
	   command is added to the 3dp queue. this seems to work,
	   but without a dropping mechanism, this can overload the
	   queue. the real solution would be to add a converter
	   object to a 3dp chain, or to accept image or bitmap
	   packets in 3dp objects */


	/* dispatch command */
	c = (t_texture_command *)pdp_dpd_commandfactory_get_new_command(&_tex_cf);
	c->p_src = pdp_packet_copy_rw(packet);
	c->p_dst = pdp_packet_copy_ro(new_p);
	pdp_procqueue_add(pdp_opengl_get_queue(), c, texture_command_convert_bitmap_to_texture, 0, 0);
    }
    return new_p;

}



int _pdp_packet_texture_convert_texture_to_bitmap(int packet, t_pdp_symbol *dest_template0)
{
    post("_pdp_packet_texture_convert_texture_to_bitmap not implemented."); 
    return -1;
}


t_texture *pdp_packet_texture_info(int packet)
{
    t_pdp *header = pdp_packet_header(packet);
    if (pdp_packet_texture_isvalid(packet)) return _pdp_tex_info(header);
    else return 0;
}

/* check if valid texture packet. all other methods assume packet is valid */
int pdp_packet_texture_isvalid(int packet)
{
    t_pdp *header;
    if (!(header = pdp_packet_header(packet))) return 0;
    if (PDP_TEXTURE != header->type) return 0;
    return glIsTexture(_pdp_tex_info(header)->tex_obj);
}



static void _tex_init_obj(t_texture *t)
{
    //u8 *dummydata;
    //int i;

    glBindTexture(GL_TEXTURE_2D, t->tex_obj);
    glTexImage2D(GL_TEXTURE_2D, 0, t->format, t->width, t->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    /* debug
    dummydata = (u8 *)malloc(t->width*t->height*4);
    for (i=0; i<t->width*t->height*4; i++){dummydata[i] = random(); }
    glTexImage2D(GL_TEXTURE_2D, 0, t->format, t->width, t->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummydata);
    free(dummydata);
    */

}


static void _pdp_tex_copy(t_pdp *dst, t_pdp *src);
static void _pdp_tex_clone(t_pdp *dst, t_pdp *src);
static void _pdp_tex_reinit(t_pdp *dst);
static void _pdp_tex_cleanup(t_pdp *dst);

static void _pdp_tex_init_methods(t_pdp *h)
{
    h->theclass = texture_class;
}

static void _pdp_tex_reinit(t_pdp *dst)
{
    /* this does nothing. texture is assumed to be in a valid state */
}
static void _pdp_tex_clone(t_pdp *dst, t_pdp *src)
{
    t_texture *dst_t = _pdp_tex_info(dst);
    t_texture *src_t = _pdp_tex_info(src);

    //post("WARNING: _pdp_tex_clone: should not be called from outside 3d thread");

    /* determine if destination texture is valid */
    if (glIsTexture(dst_t->tex_obj)){
	/* check format */
	if ((dst_t->width >= src_t->width) 
	    && (dst_t->height >= src_t->height) 
	    && (dst_t->format == src_t->format)){
	    dst_t->sub_width = src_t->sub_width;
	    dst_t->sub_height = src_t->sub_height;
	    return;
	}
    }
    /* not initialized, so we need to create a new one */
    else {
	glGenTextures(1, (GLuint*)&dst_t->tex_obj);
    }

    /* setup header */
    dst_t->width = src_t->width;
    dst_t->height = src_t->height;
    dst_t->format = src_t->format;
    dst_t->sub_width = src_t->sub_width;
    dst_t->sub_height = src_t->sub_height;

    /* setup packet methods */
    _pdp_tex_init_methods(dst);

    /* init description */
    dst->desc = _pdp_tex_get_description(dst);

}
static void _pdp_tex_copy(t_pdp *dst, t_pdp *src)
{
    /* texture copying is inefficient. for the tex extensions there is no analogy
       for "efficient in-place processing"
       this means the pdp_packet_register_rw() call should be avoided
       this inconsistency should be tucked away in a texture base class */

    /* todo: use texture combining extensions for this */

    post("WARNING: fanout is not yet implemented correctly for texture packets");

    /* not implemented yet, just a call to the clone method */
    _pdp_tex_clone(dst, src);
}

static void _pdp_tex_cleanup(t_pdp *dst)
{
    t_texture *t = _pdp_tex_info(dst);
    glDeleteTextures(1, (GLuint*)&t->tex_obj);
    t->tex_obj = -1; /* is this value guaranteed to be invalid? */
}


/* texture constructors */

/* reuse a texture, or create a "dummy" == packet with everything except a valid texture object */
static int _pdp_packet_texture_old_or_dummy(u32 width, u32 height, s32 format)
{
    int p = -1;
    t_pdp *h;
    t_texture *t;

    int p2_w = _round_to_pow_2(width);
    int p2_h = _round_to_pow_2(height);


    /* try to reuse a texture packet or get a new one */
    p = pdp_packet_reuse(_pdp_get_tex_description_from_params(p2_w, p2_h, format));
    if (-1 == p) p = pdp_packet_create(PDP_TEXTURE, 0);
    if (-1 == p) return -1;

    h = pdp_packet_header(p);
    t = _pdp_tex_info(h);
    
    /* check if alloc succeded */
    if (!h) return -1;
    
    /* check if tex is already initialized */
    if (pdp_packet_texture_isvalid(p)){
	/* check format */
	if ((t->width >= width) && (t->height >= height) && (t->format == format)){
	    //post("pdp_packet_new_tex: reused");
	    t->sub_width = width;
	    t->sub_height = height;
	    return p;
	}
	post("ERROR: pdp_packet_new_texture: pdp_packet_reuse returned wrong type");
    }

    /* determine the texture dims * setup rest of data struct */
    t->width = 64;
    t->height = 64;
    while (t->width < width) t->width <<= 1;
    while (t->height < height) t->height <<= 1;

    t->format = format;
    t->sub_width = width;
    t->sub_height = height;

    _pdp_tex_init_methods(h);


    /* init the texture */
    //_tex_init_obj(t); 

    /* init description */
    h->desc = _pdp_tex_get_description(h);


    return p;
}

/* don't call this method on a non-texture object! */
static void _pdp_packet_gentexture(int p)
{
    t_texture *t;
    if (!pdp_packet_texture_isvalid(p)){
	/* not initialized, so we need to create a new one */
	// post("generating texture");
	t = (t_texture *)pdp_packet_subheader(p);

	/* create the texture object */
	glGenTextures(1, (GLuint *)&t->tex_obj);

	/* init the texture */
	_tex_init_obj(t);

    }
}

int pdp_packet_new_texture(u32 width, u32 height, s32 format)
{
    t_texture *t;
    int p = _pdp_packet_texture_old_or_dummy(width, height, format);

    //post("WARNING: pdp_packet_new_texture: this method should not be called outside the 3dp thread");

    if (p == -1) return -1;
    _pdp_packet_gentexture(p);
    return p;
}


/* high level texture packet operators */

/* make a texture the current texture context */
void pdp_packet_texture_make_current(int packet)
{
    t_texture *t = pdp_packet_texture_info(packet);
    if (!t) return;
    glBindTexture(GL_TEXTURE_2D, t->tex_obj);
}               

void pdp_packet_texture_make_current_enable(int packet)
{
    glEnable(GL_TEXTURE_2D);
    pdp_packet_texture_make_current(packet);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

float pdp_packet_texture_fracx(int packet)
{
    t_texture *t = pdp_packet_texture_info(packet);
    if (!t) return 0.0;
    return (float)t->sub_width/t->width;
}

float pdp_packet_texture_fracy(int packet)
{
    t_texture *t = pdp_packet_texture_info(packet);
    if (!t) return 0.0;
    return (float)t->sub_height/t->height;
}

u32 pdp_packet_texture_total_width(int packet)
{
     t_texture *t = pdp_packet_texture_info(packet);
    if (!t) return 0;
    return t->width;
   
}
u32 pdp_packet_texture_total_height(int packet)
{
     t_texture *t = pdp_packet_texture_info(packet);
    if (!t) return 0;
    return t->height;
   
}

u32 pdp_packet_texture_sub_width(int packet)
{
     t_texture *t = pdp_packet_texture_info(packet);
    if (!t) return 0;
    return t->sub_width;
   
}
u32 pdp_packet_texture_sub_height(int packet)
{
     t_texture *t = pdp_packet_texture_info(packet);
    if (!t) return 0;
    return t->sub_height;
}

float pdp_packet_texture_sub_aspect(int packet)
{
    t_texture *t = pdp_packet_texture_info(packet);
    if (!t) return 0;
    return (float)t->sub_width/t->sub_height;
}

/* setup for 2d operation from texture dimensions */
void pdp_packet_texture_setup_2d_context(int p)
{
    u32 w;
    u32 h;
    float asp;
    if (!pdp_packet_texture_isvalid(p)) return;
    w = pdp_packet_texture_sub_width(p);
    h = pdp_packet_texture_sub_height(p);
    asp = pdp_packet_texture_sub_aspect(p);

    /* set the viewport to the size of the sub texture */
    glViewport(0, 0, w, h);

    /* set orthogonal projection, with a relative frame size of (2asp x 2) */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 2*asp, 0, 2);

    /* set the center of view */
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(asp, 1, 0);
    glScalef(1,-1,1);
}

void pdp_texture_setup(void)
{
    t_pdp_conversion_program *program;

    /* setup packet class */
    texture_class = pdp_class_new(pdp_gensym("texture/*/*"), 0);
    texture_class->cleanup = _pdp_tex_cleanup;
    texture_class->wakeup = _pdp_tex_reinit;
    //texture_class->clone = _pdp_tex_clone;
    texture_class->copy = _pdp_tex_copy;
    
    /* init command list */
    pdp_dpd_commandfactory_init(&_tex_cf, sizeof(t_texture_command));



    /* setup programs */
    program = pdp_conversion_program_new(_pdp_packet_texture_convert_bitmap_to_texture, 0);
    pdp_type_register_conversion(pdp_gensym("bitmap/*/*"), pdp_gensym("texture/*/*"), program);

    /* this is a hack to use until the type conversion system has a proper search algo */
    program = pdp_conversion_program_new(_pdp_packet_texture_convert_image_to_texture, 0);
    pdp_type_register_conversion(pdp_gensym("image/*/*"), pdp_gensym("texture/*/*"), program);


    program = pdp_conversion_program_new(_pdp_packet_texture_convert_texture_to_bitmap, 0);
    pdp_type_register_conversion(pdp_gensym("texture/*/*"), pdp_gensym("bitmap/*/*"), program);

}

/* all symbols are C-style */
#ifdef __cplusplus
}
#endif

--- NEW FILE: pdp_opengl.c ---

/*
 *   OpenGL Extension Module for pdp - opengl system stuff
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "pdp.h"
#include "pdp_control.h"

#define PDP_3DP_QUEUE_LOGSIZE 16
#define PDP_3DP_QUEUE_DELTIME 1.0f



void pdp_3Dcontext_prepare_for_thread_switch(void *);
static t_pdp_procqueue _3dp_queue;


static void pdp_control_thread(void *x, t_symbol *s, int argc, t_atom *argv)
{
    int t = 0;
    float f;
    if (argc != 1) return;
    if (argv[0].a_type != A_FLOAT) return;
    f = argv[0].a_w.w_float;
    t = (f != 0.0f);
    post("3dp thread switched %s", t ? "on":"off");


    /* when we switch threads, the glx system needs to be notified
       because it has to release the render context. this is done
       in a process method, so it is run in the correct thread. */


    pdp_procqueue_add(&_3dp_queue, 0, pdp_3Dcontext_prepare_for_thread_switch, 0, 0);
    pdp_procqueue_wait(&_3dp_queue);
    

    /* fresh start: enable/disable the thread dispatching */
    pdp_procqueue_use_thread(&_3dp_queue, t);

}

/* kernel setup */
void pdp_opengl_system_setup(void)
{
    /* init the 3dp queue */
    pdp_procqueue_init(&_3dp_queue, PDP_3DP_QUEUE_DELTIME, PDP_3DP_QUEUE_LOGSIZE);

    /* scheduler uses the thread */
    pdp_procqueue_use_thread(&_3dp_queue, 1);
    //pdp_procqueue_use_thread(&_3dp_queue, 0); //DEBUG: disable 3dp thread

    /* add pdp_control method for thread */
    pdp_control_addmethod((t_method)pdp_control_thread, gensym("3dthread"));
}

t_pdp_procqueue* pdp_opengl_get_queue(void){return (&_3dp_queue);}




--- NEW FILE: pdp_3Dcontext_glx.c ---

/*
 *   OpenGL Extension Module for pdp - opengl system stuff
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* this file contains the platform dependent opengl setup routines (glx)
   and pdp_packet_3Dcontext methods */

#include "pdp_opengl.h"
#include "pdp_xwindow.h"
#include "pdp_internals.h"
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
//#include <GL/glut.h>

/* all symbols are C-style */
#ifdef __cplusplus
//extern "C"
//{
#endif

// this is buggy: disabled
#define PRIVATE_CONTEXT 0


/* structure to hold the (platform dependent) gl environment setup */
typedef struct _gl_env
{
    bool initialized;        /* data structure is consistent */

    XVisualInfo *visual;     /* the visual info structure for the context */
    GLXContext context;      /* the rendering context used to render to windows or pbufs */
    GLXFBConfig *config;     /* the framebuffer config object */
    
    t_pdp_xdisplay *xdpy;    /* pdp's x display object */

    //Display *dpy;            /* x display connection */
    //int screen;              /* x screen */
    int last_context_packet; /* the packet that is currently rendered too (for caching) */
} t_gl_env;

static t_gl_env pdp_glx_env;
static t_pdp_class *context_class;

/* PDP_3DCONTEXT packet methods */

/* set/unset ogl rendering context to pbuf */
void pdp_packet_3Dcontext_set_rendering_context(int packet)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);


    if (!c) return;


    /* don't do a glx call if the context is still the same */
    if (pdp_glx_env.last_context_packet == packet) return;

    //post("new current context is %d", packet);


    /* pbuffer */
    switch(c->encoding){
    case PDP_3DCONTEXT_WINDOW:
	//glFinish();
	//glXMakeCurrent(pdp_glx_env.dpy, ((t_pdp_xwindow *)c->drawable)->win, pdp_glx_env.context);
	glXMakeCurrent(pdp_glx_env.xdpy->dpy, ((t_pdp_xwindow *)c->drawable)->win, (GLXContext)c->context);
	pdp_glx_env.last_context_packet = packet;
	break;
    case PDP_3DCONTEXT_PBUFFER:
	//glXMakeCurrent(pdp_glx_env.dpy, (GLXPbuffer)c->drawable, pdp_glx_env.context);
	//glXMakeContextCurrent(c->dpy, c->drawable.pbuf, c->drawable.pbuf, c->context);
	pdp_glx_env.last_context_packet = -1;
	break;
    default:
	pdp_glx_env.last_context_packet = -1;
	break;
    }
    
}

void pdp_packet_3Dcontext_unset_rendering_context(int packet)
{
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (!c) return;

    /* pbuffer */
    switch(c->encoding){
    case PDP_3DCONTEXT_WINDOW:
	glXMakeCurrent(pdp_glx_env.xdpy->dpy, None, NULL);
	pdp_glx_env.last_context_packet = -1;
	break;
    case PDP_3DCONTEXT_PBUFFER:
	//glXMakeCurrent(pdp_glx_env.dpy, None, NULL);
	//glXMakeContextCurrent(c->dpy, c->drawable.pbuf, c->drawable.pbuf, c->context);
	break;
    default:
	break;
    }
}


/* cons/des */
static void _3Dcontext_clone(t_pdp *dst, t_pdp *src)
{
    post("ERROR: clone not supported for 3Dcontext packets");
}

static void _3Dcontext_copy(t_pdp *dst, t_pdp *src)
{
    post("ERROR: copy not supported for 3Dcontext packets");
}

static void _3Dcontext_reinit(t_pdp *dst)
{
    /* leave the packet as is */
}
static void _3Dcontext_cleanup(t_pdp *dst)
{
    t_3Dcontext *c = (t_3Dcontext *)(&dst->info.raw);

    /* reset context packet cache, in case this packet was the current context. */
    pdp_glx_env.last_context_packet = -1;

    switch(c->encoding){
    case PDP_3DCONTEXT_WINDOW:
#if PRIVATE_CONTEXT
	glXDestroyContext (pdp_glx_env.dpy, (GLXContext)c->context);
#endif
	pdp_xwindow_cleanup((t_pdp_xwindow *)c->drawable);
	free(c->drawable);
	break;
	
    case PDP_3DCONTEXT_PBUFFER:
	break;
	//glXDestroyContext(c->dpy, c->context);
	//glXDestroyPbuffer(c->dpy, c->drawable.pbuf);
    default:
	break;
    }
}


/* setup packet methods */
static void _3Dcontext_init_methods(t_pdp *header)
{
    header->theclass = context_class;
    header->flags = PDP_FLAG_DONOTCOPY;
}



/* window specific methods */


void _pdp_3Dcontext_set_window_size(t_3Dcontext *c, t_pdp_xwindow *xwin)
{
    c->width     = xwin->winwidth;
    c->sub_width = xwin->winwidth;
    c->height    = xwin->winheight;
    c->sub_height= xwin->winheight;
}

/* resize the window */
void pdp_packet_3Dcontext_win_resize(int packet, int width, int height)
{
    t_pdp_xwindow *xwin;
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (!c) return;
    if (PDP_3DCONTEXT_WINDOW != c->encoding) return;
    xwin = (t_pdp_xwindow *)c->drawable;
    pdp_xwindow_resize(xwin, width, height);
    _pdp_3Dcontext_set_window_size(c, xwin);
}


t_pdp_list *pdp_packet_3Dcontext_win_get_eventlist(int packet)
{
    t_pdp_list *eventlist;
    t_pdp_xwindow *xwin;
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (!c) return 0;
    if (PDP_3DCONTEXT_WINDOW != c->encoding) return 0;
    xwin = (t_pdp_xwindow *)c->drawable;
    eventlist = pdp_xwindow_get_eventlist(xwin);
    _pdp_3Dcontext_set_window_size(c, xwin);
    return eventlist;
}

void pdp_packet_3Dcontext_win_cursor(int packet, bool toggle)
{
    t_pdp_xwindow *xwin;
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (!c) return;
    if (PDP_3DCONTEXT_WINDOW != c->encoding) return;
    xwin = (t_pdp_xwindow *)c->drawable;
    pdp_xwindow_cursor(xwin, toggle);

}

void pdp_packet_3Dcontext_win_swapbuffers(int packet)
{
    t_pdp_xwindow *xwin;
    t_3Dcontext *c = pdp_packet_3Dcontext_info(packet);
    if (!c) return;
    if (PDP_3DCONTEXT_WINDOW != c->encoding) return;
    xwin = (t_pdp_xwindow *)c->drawable;
    glXSwapBuffers(xwin->xdisplay->dpy,xwin->win);
    //glFinish();

}


/* constructors */

/* construct (or reuse) a window packet */
int pdp_packet_new_3Dcontext_win(void)
{
    /* $$$FIXME: this assumes packet can't be reused */
    int p = pdp_packet_new(PDP_3DCONTEXT, 0);
    t_pdp_xwindow *xwin;
    t_3Dcontext *c;
    t_pdp *header = pdp_packet_header(p);
    if (!header) return -1; /* pool full ? */
    c = (t_3Dcontext *)&header->info.raw;

    if (c->drawable){
	xwin = (t_pdp_xwindow *)c->drawable;
    }
    else{
	xwin = (t_pdp_xwindow *)malloc(sizeof(*xwin));
    }

    pdp_xwindow_init(xwin);
    pdp_xwindow_create_on_display(xwin, pdp_glx_env.xdpy);

    /* init subheader */
#if PRIVATE_CONTEXT
    if (NULL == (c->context = (void *)glXCreateContext(pdp_glx_env.dpy, pdp_glx_env.visual, pdp_glx_env.context, True))){
	post("pdp_packet_new_3Dcontext_wind: ERROR: can't create rendering context");
    }
#else
    c->context = (void *)pdp_glx_env.context;
#endif
    c->drawable = xwin;
    c->encoding  = PDP_3DCONTEXT_WINDOW;
    _pdp_3Dcontext_set_window_size(c, xwin);

    /* init packet methods */
    _3Dcontext_init_methods(header);

    /* init header */
    header->desc = pdp_gensym("3Dcontext/window");
    header->flags = PDP_FLAG_DONOTCOPY;
    
    return p;


}

/* pbuf constructor */
int pdp_packet_new_3Dcontext_pbuf(u32 width, u32 height, u32 depth)
{
    post("ERROR: 3Dcontext/pbuffer packets not implemented");
    return -1;
}


/* this is a notifier sent when the processing thread which
   executes gl commands is changed. we need to release the current context
   before another thread can take it. */
void pdp_3Dcontext_prepare_for_thread_switch(void)
{
    pdp_packet_3Dcontext_unset_rendering_context(pdp_glx_env.last_context_packet);
}





/* setup routine */
static void pdp_3Dcontext_glx_setup_inthread(void)
{
    /* this opens the connection to the x server and creates a render context
       for windows (glx < 1.3) or windows/pbufs (glx >= 1.3) */

    static int dblBuf24[] =  {GLX_RGBA,
			      GLX_RED_SIZE, 1, 
	                      GLX_GREEN_SIZE, 1, 
			      GLX_BLUE_SIZE, 1, 
			      GLX_ALPHA_SIZE, 0, 
			      GLX_DEPTH_SIZE, 1,
                              GLX_DOUBLEBUFFER,
			      None};

    pdp_glx_env.initialized = 0;


    /* init xlib for thread usage */
    if (!XInitThreads()){
    	post("pdp_opengl_system_setup: can't init Xlib for thread usage.");
    	goto init_failed;
    }
 

    /* open display:
       the first display on the local machine is opened, not DISPLAY.
       since pdp_opengl is all about direct rendering, and there
       is no way to specify another display, or even close it and
       open it again, this seems to be the "least surprise" solution.
       it enables the pd interface to be displayed on another display,
       using the DISPLAY environment variable. */
    

    


    if (NULL == (pdp_glx_env.xdpy = pdp_xdisplay_new(":0"))){
	post("pdp_opengl_system_setup: can't open display");
	goto init_failed;
    }


    /* get visual */
    if (NULL == (pdp_glx_env.visual = glXChooseVisual(pdp_glx_env.xdpy->dpy, pdp_glx_env.xdpy->screen, dblBuf24))){
	post("pdp_opengl_system_setup: can't find appropriate visual");
	goto init_failed_close_dpy;
    }


    /* create a (direct) rendering context */
    if (NULL == (pdp_glx_env.context = glXCreateContext(pdp_glx_env.xdpy->dpy, pdp_glx_env.visual, 0, True))){
	post("pdp_opengl_system_setup: can't create rendering context");
	goto init_failed_close_dpy;
    }


    //post("pdp_opengl_system_setup: pdp_opengl init OK.");
    pdp_glx_env.last_context_packet = -1;
    pdp_glx_env.initialized = 1;

    /* setup class object */
    context_class = pdp_class_new(pdp_gensym("3Dcontext/*"), 0);
    context_class->cleanup = _3Dcontext_cleanup;
    context_class->wakeup = _3Dcontext_reinit;
    //context_class->clone = _3Dcontext_clone;
    context_class->copy = _3Dcontext_copy;


    /* setup conversion programs: NOT IMPLEMENTED */
    return;
    

 init_failed_close_dpy:
    pdp_xdisplay_free(pdp_glx_env.xdpy);
    pdp_glx_env.xdpy = 0;
 init_failed:
    post("pdp_opengl_system_setup: FATAL ERROR: pdp_opengl init failed.");
    exit(1);

}

/* run the setup routine in the procqueue thread, and wait for it to finish */
/* NOTE: this seems to make an Xlib deadlock problem go away when running
   pd with realtime scheduling. frankly, i'm very puzzled by this problem
   and even more by the way this workaround solves it. anyhow... */
void pdp_3Dcontext_glx_setup(void)
{
    t_pdp_procqueue *q = pdp_opengl_get_queue();
    pdp_procqueue_add(q, 0, pdp_3Dcontext_glx_setup_inthread, 0, 0);
    pdp_procqueue_flush(q);
}

#ifdef __cplusplus
//}
#endif





More information about the Pd-cvs mailing list