/**
 * $Id: drawarmature.c 16902 2008-10-03 19:34:49Z blendix $
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2005 by the Blender Foundation.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "MEM_guardedalloc.h"

#include "BMF_Api.h"

#include "DNA_action_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_ID.h"
#include "DNA_nla_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_userdef_types.h"

#include "BLI_blenlib.h"
#include "BLI_arithb.h"

#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_depsgraph.h"
#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_ipo.h"
#include "BKE_utildefines.h"

#include "BIF_editaction.h"
#include "BIF_editarmature.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "BIF_graphics.h"
#include "BIF_interface.h"
#include "BIF_poseobject.h"
#include "BIF_mywindow.h"
#include "BIF_resources.h"
#include "BIF_screen.h"

#include "BDR_editobject.h"
#include "BDR_drawobject.h"
#include "BDR_drawaction.h"

#include "BSE_edit.h"
#include "BSE_view.h"

#include "mydevice.h"
#include "blendef.h"
#include "nla.h"


/* *************** Armature Drawing - Coloring API ***************************** */

/* global here is reset before drawing each bone */
static ThemeWireColor *bcolor= NULL;

/* values of colCode for set_pchan_glcolor */
enum {
	PCHAN_COLOR_NORMAL	= 0, 		/* normal drawing */
	PCHAN_COLOR_SOLID,				/* specific case where "solid" color is needed */
	PCHAN_COLOR_CONSTS,				/* "constraint" colors (which may/may-not be suppressed) */
	
	PCHAN_COLOR_SPHEREBONE_BASE,	/* for the 'stick' of sphere (envelope) bones */
	PCHAN_COLOR_SPHEREBONE_END,		/* for the ends of sphere (envelope) bones */
	PCHAN_COLOR_LINEBONE			/* for the middle of line-bones */
};	

/* This function sets the color-set for coloring a certain bone */
static void set_pchan_colorset (Object *ob, bPoseChannel *pchan)
{
	bPose *pose= (ob) ? ob->pose : NULL;
	bArmature *arm= (ob) ? ob->data : NULL;
	bActionGroup *grp= NULL;
	short color_index= 0;
	
	/* sanity check */
	if (ELEM4(NULL, ob, arm, pose, pchan)) {
		bcolor= NULL;
		return;
	}
	
	/* only try to set custom color if enabled for armature */
	if (arm->flag & ARM_COL_CUSTOM) {	
		/* currently, a bone can only use a custom color set if it's group (if it has one),
		 * has been set to use one
		 */
		if (pchan->agrp_index) {
			grp= (bActionGroup *)BLI_findlink(&pose->agroups, (pchan->agrp_index - 1));
			if (grp)
				color_index= grp->customCol;
		}
	}
	
	/* bcolor is a pointer to the color set to use. If NULL, then the default
	 * color set (based on the theme colors for 3d-view) is used. 
	 */
	if (color_index > 0) {
		bTheme *btheme= U.themes.first;
		bcolor= &btheme->tarm[(color_index - 1)];
	}
	else if (color_index == -1) {
		/* use the group's own custom color set */
		bcolor= (grp)? &grp->cs : NULL;
	}
	else 
		bcolor= NULL;
}

/* This function is for brightening/darkening a given color (like BIF_ThemeColorShade()) */
static void cp_shade_color3ub (char cp[], int offset)
{
	int r, g, b;
	
	r= offset + (int) cp[0];
	CLAMP(r, 0, 255);
	g= offset + (int) cp[1];
	CLAMP(g, 0, 255);
	b= offset + (int) cp[2];
	CLAMP(b, 0, 255);
	
	cp[0]= r;
	cp[1]= g;
	cp[2]= b;
}

/* This function sets the gl-color for coloring a certain bone (based on bcolor) */
static short set_pchan_glColor (short colCode, int armflag, int boneflag, int constflag)
{
	switch (colCode) {
	case PCHAN_COLOR_NORMAL:
	{
		if (bcolor) {
			char cp[3];
			
			if (boneflag & BONE_ACTIVE) {
				VECCOPY(cp, bcolor->active);
			}
			else if (boneflag & BONE_SELECTED) {
				VECCOPY(cp, bcolor->select);
			}
			else {
				/* a bit darker than solid */
				VECCOPY(cp, bcolor->solid);
				cp_shade_color3ub(cp, -50);
			}
			
			glColor3ub(cp[0], cp[1], cp[2]);
		}
		else {
			if (boneflag & BONE_ACTIVE) BIF_ThemeColorShade(TH_BONE_POSE, 40);
			else if (boneflag & BONE_SELECTED) BIF_ThemeColor(TH_BONE_POSE);
			else BIF_ThemeColor(TH_WIRE);
		}
		
		return 1;
	}
		break;
	
	case PCHAN_COLOR_SOLID:
	{
		if (bcolor) {
			char *cp= bcolor->solid;
			glColor3ub(cp[0], cp[1], cp[2]);
		}
		else 
			BIF_ThemeColor(TH_BONE_SOLID);
			
		return 1;
	}
		break;
		
	case PCHAN_COLOR_CONSTS:
	{
		if ( (bcolor == NULL) || (bcolor->flag & TH_WIRECOLOR_CONSTCOLS) ) {
			if (constflag & PCHAN_HAS_STRIDE) glColor4ub(0, 0, 200, 80);
			else if (constflag & PCHAN_HAS_TARGET) glColor4ub(255, 150, 0, 80);
			else if (constflag & PCHAN_HAS_IK) glColor4ub(255, 255, 0, 80);
			else if (constflag & PCHAN_HAS_CONST) glColor4ub(0, 255, 120, 80);
			else if (constflag) BIF_ThemeColor4(TH_BONE_POSE);	// PCHAN_HAS_ACTION 
			
			return 1;
		}
		else 
			return 0;
	}
		break;
		
	case PCHAN_COLOR_SPHEREBONE_BASE:
	{
		if (bcolor) {
			char cp[3];
			
			if (boneflag & BONE_ACTIVE) {
				VECCOPY(cp, bcolor->active);
			}
			else if (boneflag & BONE_SELECTED) {
				VECCOPY(cp, bcolor->select);
			}
			else {
				VECCOPY(cp, bcolor->solid);
			}
			
			glColor3ub(cp[0], cp[1], cp[2]);
		}
		else {
			if (boneflag & BONE_ACTIVE) BIF_ThemeColorShade(TH_BONE_POSE, 40);
			else if (boneflag & BONE_SELECTED) BIF_ThemeColor(TH_BONE_POSE);
			else BIF_ThemeColor(TH_BONE_SOLID);
		}
		
		return 1;
	}
		break;
	case PCHAN_COLOR_SPHEREBONE_END:
	{
		if (bcolor) {
			char cp[3];
			
			if (boneflag & BONE_ACTIVE) {
				VECCOPY(cp, bcolor->active);
				cp_shade_color3ub(cp, 10);
			}
			else if (boneflag & BONE_SELECTED) {
				VECCOPY(cp, bcolor->select);
				cp_shade_color3ub(cp, -30);
			}
			else {
				VECCOPY(cp, bcolor->solid);
				cp_shade_color3ub(cp, -30);
			}
			
			glColor3ub(cp[0], cp[1], cp[2]);
		}
		else {
			if (boneflag & BONE_ACTIVE) BIF_ThemeColorShade(TH_BONE_POSE, 10);
			else if (boneflag & BONE_SELECTED) BIF_ThemeColorShade(TH_BONE_POSE, -30);
			else BIF_ThemeColorShade(TH_BONE_SOLID, -30);
		}
	}
		break;
		
	case PCHAN_COLOR_LINEBONE:
	{
		/* inner part in background color or constraint */
		if ( (constflag) && ((bcolor==NULL) || (bcolor->flag & TH_WIRECOLOR_CONSTCOLS)) ) {
			if (constflag & PCHAN_HAS_STRIDE) glColor3ub(0, 0, 200);
			else if (constflag & PCHAN_HAS_TARGET) glColor3ub(255, 150, 0);
			else if (constflag & PCHAN_HAS_IK) glColor3ub(255, 255, 0);
			else if (constflag & PCHAN_HAS_CONST) glColor3ub(0, 255, 120);
			else if (constflag) BIF_ThemeColor(TH_BONE_POSE);	/* PCHAN_HAS_ACTION */ 
		}
		else {
			if (bcolor) {
				char *cp= bcolor->solid;
				glColor4ub(cp[0], cp[1], cp[2], 0.8);	
			}
			else
				BIF_ThemeColorShade(TH_BACK, -30);
		}
		
		return 1;
	}
		break;
	}
	
	return 0;
}


/* *************** Armature drawing, helper calls for parts ******************* */

/* half the cube, in Y */
static float cube[8][3] = {
{-1.0,  0.0, -1.0},
{-1.0,  0.0,  1.0},
{-1.0,  1.0,  1.0},
{-1.0,  1.0, -1.0},
{ 1.0,  0.0, -1.0},
{ 1.0,  0.0,  1.0},
{ 1.0,  1.0,  1.0},
{ 1.0,  1.0, -1.0},
};

static void drawsolidcube_size(float xsize, float ysize, float zsize)
{
	static GLuint displist=0;
	float n[3];
	
	glScalef(xsize, ysize, zsize);
	
	n[0]=0; n[1]=0; n[2]=0;

	if(displist==0) {
		displist= glGenLists(1);
		glNewList(displist, GL_COMPILE_AND_EXECUTE);

		glBegin(GL_QUADS);
		n[0]= -1.0;
		glNormal3fv(n); 
		glVertex3fv(cube[0]); glVertex3fv(cube[1]); glVertex3fv(cube[2]); glVertex3fv(cube[3]);
		n[0]=0;
		n[1]= -1.0;
		glNormal3fv(n); 
		glVertex3fv(cube[0]); glVertex3fv(cube[4]); glVertex3fv(cube[5]); glVertex3fv(cube[1]);
		n[1]=0;
		n[0]= 1.0;
		glNormal3fv(n); 
		glVertex3fv(cube[4]); glVertex3fv(cube[7]); glVertex3fv(cube[6]); glVertex3fv(cube[5]);
		n[0]=0;
		n[1]= 1.0;
		glNormal3fv(n); 
		glVertex3fv(cube[7]); glVertex3fv(cube[3]); glVertex3fv(cube[2]); glVertex3fv(cube[6]);
		n[1]=0;
		n[2]= 1.0;
		glNormal3fv(n); 
		glVertex3fv(cube[1]); glVertex3fv(cube[5]); glVertex3fv(cube[6]); glVertex3fv(cube[2]);
		n[2]=0;
		n[2]= -1.0;
		glNormal3fv(n); 
		glVertex3fv(cube[7]); glVertex3fv(cube[4]); glVertex3fv(cube[0]); glVertex3fv(cube[3]);
		glEnd();

		glEndList();
	}
	else glCallList(displist);
	
}

static void drawcube_size(float xsize, float ysize, float zsize)
{
	static GLuint displist=0;
	
	glScalef(xsize, ysize, zsize);
	
	if(displist == 0) {
		displist= glGenLists(1);
		glNewList(displist, GL_COMPILE_AND_EXECUTE);
		
		glBegin(GL_LINE_STRIP);
		glVertex3fv(cube[0]); glVertex3fv(cube[1]);glVertex3fv(cube[2]); glVertex3fv(cube[3]);
		glVertex3fv(cube[0]); glVertex3fv(cube[4]);glVertex3fv(cube[5]); glVertex3fv(cube[6]);
		glVertex3fv(cube[7]); glVertex3fv(cube[4]);
		glEnd();
		
		glBegin(GL_LINES);
		glVertex3fv(cube[1]); glVertex3fv(cube[5]);
		glVertex3fv(cube[2]); glVertex3fv(cube[6]);
		glVertex3fv(cube[3]); glVertex3fv(cube[7]);
		glEnd();
		
		glEndList();
	}
	else glCallList(displist);
	
}


static void draw_bonevert(void)
{
	static GLuint displist=0;
	
	if (displist == 0) {
		GLUquadricObj	*qobj;
		
		displist= glGenLists(1);
		glNewList(displist, GL_COMPILE_AND_EXECUTE);
			
		glPushMatrix();
		
		qobj	= gluNewQuadric(); 
		gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); 
		gluDisk(qobj, 0.0,  0.05, 16, 1);
		
		glRotatef(90, 0, 1, 0);
		gluDisk(qobj, 0.0,  0.05, 16, 1);
		
		glRotatef(90, 1, 0, 0);
		gluDisk(qobj, 0.0,  0.05, 16, 1);
		
		gluDeleteQuadric(qobj);  
		
		glPopMatrix();
		glEndList();
	}
	else 
		glCallList(displist);
}

static void draw_bonevert_solid(void)
{
	static GLuint displist=0;
	
	if (displist == 0) {
		GLUquadricObj	*qobj;
		
		displist= glGenLists(1);
		glNewList(displist, GL_COMPILE_AND_EXECUTE);
		
		qobj	= gluNewQuadric();
		gluQuadricDrawStyle(qobj, GLU_FILL); 
		glShadeModel(GL_SMOOTH);
		gluSphere( qobj, 0.05, 8, 5);
		glShadeModel(GL_FLAT);
		gluDeleteQuadric(qobj);  
		
		glEndList();
	}
	else 
		glCallList(displist);
}

static void draw_bone_octahedral()
{
	static GLuint displist=0;
	
	if (displist == 0) {
		float vec[6][3];	
		
		displist= glGenLists(1);
		glNewList(displist, GL_COMPILE_AND_EXECUTE);
		
		vec[0][0]= vec[0][1]= vec[0][2]= 0.0;
		vec[5][0]= vec[5][2]= 0.0; vec[5][1]= 1.0;
		
		vec[1][0]= 0.1; vec[1][2]= 0.1; vec[1][1]= 0.1;
		vec[2][0]= 0.1; vec[2][2]= -0.1; vec[2][1]= 0.1;
		vec[3][0]= -0.1; vec[3][2]= -0.1; vec[3][1]= 0.1;
		vec[4][0]= -0.1; vec[4][2]= 0.1; vec[4][1]= 0.1;
		
		/*	Section 1, sides */
		glBegin(GL_LINE_LOOP);
		glVertex3fv(vec[0]);
		glVertex3fv(vec[1]);
		glVertex3fv(vec[5]);
		glVertex3fv(vec[3]);
		glVertex3fv(vec[0]);
		glVertex3fv(vec[4]);
		glVertex3fv(vec[5]);
		glVertex3fv(vec[2]);
		glEnd();
		
		/*	Section 1, square */
		glBegin(GL_LINE_LOOP);
		glVertex3fv(vec[1]);
		glVertex3fv(vec[2]);
		glVertex3fv(vec[3]);
		glVertex3fv(vec[4]);
		glEnd();
		
		glEndList();
	}
	else 
		glCallList(displist);
}	

static void draw_bone_solid_octahedral(void)
{
	static GLuint displist=0;
	
	if (displist == 0) {
		float vec[6][3], nor[3];	
		
		displist= glGenLists(1);
		glNewList(displist, GL_COMPILE_AND_EXECUTE);
		
		vec[0][0]= vec[0][1]= vec[0][2]= 0.0;
		vec[5][0]= vec[5][2]= 0.0; vec[5][1]= 1.0;
		
		vec[1][0]= 0.1; vec[1][2]= 0.1; vec[1][1]= 0.1;
		vec[2][0]= 0.1; vec[2][2]= -0.1; vec[2][1]= 0.1;
		vec[3][0]= -0.1; vec[3][2]= -0.1; vec[3][1]= 0.1;
		vec[4][0]= -0.1; vec[4][2]= 0.1; vec[4][1]= 0.1;
		
		
		glBegin(GL_TRIANGLES);
		/* bottom */
		CalcNormFloat(vec[2], vec[1], vec[0], nor);
		glNormal3fv(nor);
		glVertex3fv(vec[2]);glVertex3fv(vec[1]);glVertex3fv(vec[0]);
		
		CalcNormFloat(vec[3], vec[2], vec[0], nor);
		glNormal3fv(nor);
		glVertex3fv(vec[3]);glVertex3fv(vec[2]);glVertex3fv(vec[0]);
		
		CalcNormFloat(vec[4], vec[3], vec[0], nor);
		glNormal3fv(nor);
		glVertex3fv(vec[4]);glVertex3fv(vec[3]);glVertex3fv(vec[0]);

		CalcNormFloat(vec[1], vec[4], vec[0], nor);
		glNormal3fv(nor);
		glVertex3fv(vec[1]);glVertex3fv(vec[4]);glVertex3fv(vec[0]);

		/* top */
		CalcNormFloat(vec[5], vec[1], vec[2], nor);
		glNormal3fv(nor);
		glVertex3fv(vec[5]);glVertex3fv(vec[1]);glVertex3fv(vec[2]);
		
		CalcNormFloat(vec[5], vec[2], vec[3], nor);
		glNormal3fv(nor);
		glVertex3fv(vec[5]);glVertex3fv(vec[2]);glVertex3fv(vec[3]);
		
		CalcNormFloat(vec[5], vec[3], vec[4], nor);
		glNormal3fv(nor);
		glVertex3fv(vec[5]);glVertex3fv(vec[3]);glVertex3fv(vec[4]);
		
		CalcNormFloat(vec[5], vec[4], vec[1], nor);
		glNormal3fv(nor);
		glVertex3fv(vec[5]);glVertex3fv(vec[4]);glVertex3fv(vec[1]);
		
		glEnd();
		
		glEndList();
	}
	else 
		glCallList(displist);
}	

/* *************** Armature drawing, bones ******************* */


static void draw_bone_points(int dt, int armflag, unsigned int boneflag, int id)
{
	/*	Draw root point if we are not connected */
	if ((boneflag & BONE_CONNECTED)==0) {
		if (id != -1)
			glLoadName(id | BONESEL_ROOT);
		
		if(dt <= OB_WIRE) {
			if (armflag & ARM_EDITMODE) {
				if (boneflag & BONE_ROOTSEL) BIF_ThemeColor(TH_VERTEX_SELECT);
				else BIF_ThemeColor(TH_VERTEX);
			}
		}
		else {
			if (armflag & ARM_POSEMODE) 
				set_pchan_glColor(PCHAN_COLOR_SOLID, armflag, boneflag, 0);
			else
				BIF_ThemeColor(TH_BONE_SOLID);
		}
		
		if (dt > OB_WIRE) 
			draw_bonevert_solid();
		else 
			draw_bonevert();
	}
	
	/*	Draw tip point */
	if (id != -1)
		glLoadName(id | BONESEL_TIP);
	
	if (dt <= OB_WIRE) {
		if (armflag & ARM_EDITMODE) {
			if (boneflag & BONE_TIPSEL) BIF_ThemeColor(TH_VERTEX_SELECT);
			else BIF_ThemeColor(TH_VERTEX);
		}
	}
	else {
		if (armflag & ARM_POSEMODE) 
			set_pchan_glColor(PCHAN_COLOR_SOLID, armflag, boneflag, 0);
		else
			BIF_ThemeColor(TH_BONE_SOLID);
	}
	
	glTranslatef(0.0, 1.0, 0.0);
	if (dt > OB_WIRE) 
		draw_bonevert_solid();
	else 
		draw_bonevert();
	glTranslatef(0.0, -1.0, 0.0);
	
}

/* 16 values of sin function (still same result!) */
static float si[16] = {
	0.00000000,
	0.20129852, 0.39435585,
	0.57126821, 0.72479278,
	0.84864425, 0.93775213,
	0.98846832, 0.99871650,
	0.96807711, 0.89780453,
	0.79077573, 0.65137248,
	0.48530196, 0.29936312,
	0.10116832
};
/* 16 values of cos function (still same result!) */
static float co[16] ={
	1.00000000,
	0.97952994, 0.91895781,
	0.82076344, 0.68896691,
	0.52896401, 0.34730525,
	0.15142777, -0.05064916,
	-0.25065253, -0.44039415,
	-0.61210598, -0.75875812,
	-0.87434661, -0.95413925,
	-0.99486932
};



/* smat, imat = mat & imat to draw screenaligned */
static void draw_sphere_bone_dist(float smat[][4], float imat[][4], int boneflag, bPoseChannel *pchan, EditBone *ebone)
{
	float head, tail, length, dist;
	float *headvec, *tailvec, dirvec[3];
	
	/* figure out the sizes of spheres */
	if (ebone) {
		/* this routine doesn't call set_matrix_editbone() that calculates it */
		ebone->length = VecLenf(ebone->head, ebone->tail);
		
		length= ebone->length;
		tail= ebone->rad_tail;
		dist= ebone->dist;
		if (ebone->parent && (ebone->flag & BONE_CONNECTED))
			head= ebone->parent->rad_tail;
		else
			head= ebone->rad_head;
		headvec= ebone->head;
		tailvec= ebone->tail;
	}
	else {
		length= pchan->bone->length;
		tail= pchan->bone->rad_tail;
		dist= pchan->bone->dist;
		if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED))
			head= pchan->parent->bone->rad_tail;
		else
			head= pchan->bone->rad_head;
		headvec= pchan->pose_head;
		tailvec= pchan->pose_tail;
	}
	
	/* ***** draw it ***** */
	
	/* move vector to viewspace */
	VecSubf(dirvec, tailvec, headvec);
	Mat4Mul3Vecfl(smat, dirvec);
	/* clear zcomp */
	dirvec[2]= 0.0;
	/* move vector back */
	Mat4Mul3Vecfl(imat, dirvec);
	
	if (0.0f != Normalize(dirvec)) {
		float norvec[3], vec1[3], vec2[3], vec[3];
		int a;
		
		//VecMulf(dirvec, head);
		Crossf(norvec, dirvec, imat[2]);
		
		glBegin(GL_QUAD_STRIP);
		
		for (a=0; a<16; a++) {
			vec[0]= - *(si+a) * dirvec[0] + *(co+a) * norvec[0];
			vec[1]= - *(si+a) * dirvec[1] + *(co+a) * norvec[1];
			vec[2]= - *(si+a) * dirvec[2] + *(co+a) * norvec[2];
			
			vec1[0]= headvec[0] + head*vec[0];
			vec1[1]= headvec[1] + head*vec[1];
			vec1[2]= headvec[2] + head*vec[2];
			vec2[0]= headvec[0] + (head+dist)*vec[0];
			vec2[1]= headvec[1] + (head+dist)*vec[1];
			vec2[2]= headvec[2] + (head+dist)*vec[2];
			
			glColor4ub(255, 255, 255, 50);
			glVertex3fv(vec1);
			//glColor4ub(255, 255, 255, 0);
			glVertex3fv(vec2);
		}
		
		for (a=15; a>=0; a--) {
			vec[0]= *(si+a) * dirvec[0] + *(co+a) * norvec[0];
			vec[1]= *(si+a) * dirvec[1] + *(co+a) * norvec[1];
			vec[2]= *(si+a) * dirvec[2] + *(co+a) * norvec[2];
			
			vec1[0]= tailvec[0] + tail*vec[0];
			vec1[1]= tailvec[1] + tail*vec[1];
			vec1[2]= tailvec[2] + tail*vec[2];
			vec2[0]= tailvec[0] + (tail+dist)*vec[0];
			vec2[1]= tailvec[1] + (tail+dist)*vec[1];
			vec2[2]= tailvec[2] + (tail+dist)*vec[2];
			
			//glColor4ub(255, 255, 255, 50);
			glVertex3fv(vec1);
			//glColor4ub(255, 255, 255, 0);
			glVertex3fv(vec2);
		}
		/* make it cyclic... */
		
		vec[0]= - *(si) * dirvec[0] + *(co) * norvec[0];
		vec[1]= - *(si) * dirvec[1] + *(co) * norvec[1];
		vec[2]= - *(si) * dirvec[2] + *(co) * norvec[2];
		
		vec1[0]= headvec[0] + head*vec[0];
		vec1[1]= headvec[1] + head*vec[1];
		vec1[2]= headvec[2] + head*vec[2];
		vec2[0]= headvec[0] + (head+dist)*vec[0];
		vec2[1]= headvec[1] + (head+dist)*vec[1];
		vec2[2]= headvec[2] + (head+dist)*vec[2];
		
		//glColor4ub(255, 255, 255, 50);
		glVertex3fv(vec1);
		//glColor4ub(255, 255, 255, 0);
		glVertex3fv(vec2);
		
		glEnd();
	}
}


/* smat, imat = mat & imat to draw screenaligned */
static void draw_sphere_bone_wire(float smat[][4], float imat[][4], int armflag, int boneflag, int constflag, unsigned int id, bPoseChannel *pchan, EditBone *ebone)
{
	float head, tail, length;
	float *headvec, *tailvec, dirvec[3];
	
	/* figure out the sizes of spheres */
	if (ebone) {
		/* this routine doesn't call set_matrix_editbone() that calculates it */
		ebone->length = VecLenf(ebone->head, ebone->tail);
		
		length= ebone->length;
		tail= ebone->rad_tail;
		if (ebone->parent && (boneflag & BONE_CONNECTED))
			head= ebone->parent->rad_tail;
		else
			head= ebone->rad_head;
		headvec= ebone->head;
		tailvec= ebone->tail;
	}
	else {
		length= pchan->bone->length;
		tail= pchan->bone->rad_tail;
		if ((pchan->parent) && (boneflag & BONE_CONNECTED))
			head= pchan->parent->bone->rad_tail;
		else
			head= pchan->bone->rad_head;
		headvec= pchan->pose_head;
		tailvec= pchan->pose_tail;
	}
	
	/* sphere root color */
	if (armflag & ARM_EDITMODE) {
		if (boneflag & BONE_ROOTSEL) BIF_ThemeColor(TH_VERTEX_SELECT);
		else BIF_ThemeColor(TH_VERTEX);
	}
	else if (armflag & ARM_POSEMODE)
		set_pchan_glColor(PCHAN_COLOR_NORMAL, armflag, boneflag, constflag);
	
	/*	Draw root point if we are not connected */
	if ((boneflag & BONE_CONNECTED)==0) {
		if (id != -1)
			glLoadName(id | BONESEL_ROOT);
		
		drawcircball(GL_LINE_LOOP, headvec, head, imat);
	}
	
	/*	Draw tip point */
	if (armflag & ARM_EDITMODE) {
		if (boneflag & BONE_TIPSEL) BIF_ThemeColor(TH_VERTEX_SELECT);
		else BIF_ThemeColor(TH_VERTEX);
	}
	
	if (id != -1)
		glLoadName(id | BONESEL_TIP);
	
	drawcircball(GL_LINE_LOOP, tailvec, tail, imat);
	
	/* base */
	if (armflag & ARM_EDITMODE) {
		if (boneflag & BONE_SELECTED) BIF_ThemeColor(TH_SELECT);
		else BIF_ThemeColor(TH_WIRE);
	}
	
	VecSubf(dirvec, tailvec, headvec);
	
	/* move vector to viewspace */
	Mat4Mul3Vecfl(smat, dirvec);
	/* clear zcomp */
	dirvec[2]= 0.0;
	/* move vector back */
	Mat4Mul3Vecfl(imat, dirvec);
	
	if (0.0f != Normalize(dirvec)) {
		float norvech[3], norvect[3], vec[3];
		
		VECCOPY(vec, dirvec);
		
		VecMulf(dirvec, head);
		Crossf(norvech, dirvec, imat[2]);
		
		VecMulf(vec, tail);
		Crossf(norvect, vec, imat[2]);
		
		if (id != -1)
			glLoadName(id | BONESEL_BONE);
		
		glBegin(GL_LINES);
		vec[0]= headvec[0] + norvech[0];
		vec[1]= headvec[1] + norvech[1];
		vec[2]= headvec[2] + norvech[2];
		glVertex3fv(vec);
		vec[0]= tailvec[0] + norvect[0];
		vec[1]= tailvec[1] + norvect[1];
		vec[2]= tailvec[2] + norvect[2];
		glVertex3fv(vec);
		vec[0]= headvec[0] - norvech[0];
		vec[1]= headvec[1] - norvech[1];
		vec[2]= headvec[2] - norvech[2];
		glVertex3fv(vec);
		vec[0]= tailvec[0] - norvect[0];
		vec[1]= tailvec[1] - norvect[1];
		vec[2]= tailvec[2] - norvect[2];
		glVertex3fv(vec);
		
		glEnd();
	}
}

/* does wire only for outline selecting */
static void draw_sphere_bone(int dt, int armflag, int boneflag, int constflag, unsigned int id, bPoseChannel *pchan, EditBone *ebone)
{
	GLUquadricObj	*qobj;
	float head, tail, length;
	float fac1, fac2;
	
	glPushMatrix();
	qobj	= gluNewQuadric();

	/* figure out the sizes of spheres */
	if (ebone) {
		length= ebone->length;
		tail= ebone->rad_tail;
		if (ebone->parent && (boneflag & BONE_CONNECTED))
			head= ebone->parent->rad_tail;
		else
			head= ebone->rad_head;
	}
	else {
		length= pchan->bone->length;
		tail= pchan->bone->rad_tail;
		if (pchan->parent && (boneflag & BONE_CONNECTED))
			head= pchan->parent->bone->rad_tail;
		else
			head= pchan->bone->rad_head;
	}
	
	/* move to z-axis space */
	glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);

	if (dt==OB_SOLID) {
		/* set up solid drawing */
		glEnable(GL_COLOR_MATERIAL);
		glEnable(GL_LIGHTING);
		
		gluQuadricDrawStyle(qobj, GLU_FILL); 
		glShadeModel(GL_SMOOTH);
	}
	else {
		gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); 
	}
	
	/* sphere root color */
	if (armflag & ARM_EDITMODE) {
		if (boneflag & BONE_ROOTSEL) BIF_ThemeColor(TH_VERTEX_SELECT);
		else BIF_ThemeColorShade(TH_BONE_SOLID, -30);
	}
	else if (armflag & ARM_POSEMODE)
		set_pchan_glColor(PCHAN_COLOR_SPHEREBONE_END, armflag, boneflag, constflag);
	else if (dt==OB_SOLID) 
		BIF_ThemeColorShade(TH_BONE_SOLID, -30);
	
	/*	Draw root point if we are not connected */
	if ((boneflag & BONE_CONNECTED)==0) {
		if (id != -1)
			glLoadName(id | BONESEL_ROOT);
		gluSphere(qobj, head, 16, 10);
	}
	
	/*	Draw tip point */
	if (armflag & ARM_EDITMODE) {
		if (boneflag & BONE_TIPSEL) BIF_ThemeColor(TH_VERTEX_SELECT);
		else BIF_ThemeColorShade(TH_BONE_SOLID, -30);
	}

	if (id != -1)
		glLoadName(id | BONESEL_TIP);
	
	glTranslatef(0.0, 0.0, length);
	gluSphere(qobj, tail, 16, 10);
	glTranslatef(0.0, 0.0, -length);
	
	/* base */
	if (armflag & ARM_EDITMODE) {
		if (boneflag & BONE_SELECTED) BIF_ThemeColor(TH_SELECT);
		else BIF_ThemeColor(TH_BONE_SOLID);
	}
	else if (armflag & ARM_POSEMODE)
		set_pchan_glColor(PCHAN_COLOR_SPHEREBONE_BASE, armflag, boneflag, constflag);
	else if (dt == OB_SOLID)
		BIF_ThemeColor(TH_BONE_SOLID);
	
	fac1= (length-head)/length;
	fac2= (length-tail)/length;
	
	if (length > (head+tail)) {
		if (id != -1)
			glLoadName (id | BONESEL_BONE);
		
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(-1.0, -1.0);
		
		glTranslatef(0.0f, 0.0f, head);
		gluCylinder(qobj, fac1*head + (1.0f-fac1)*tail, fac2*tail + (1.0f-fac2)*head, length-head-tail, 16, 1);
		glTranslatef(0.0f, 0.0f, -head);
		
		glDisable(GL_POLYGON_OFFSET_FILL);
		
		/* draw sphere on extrema */
		glTranslatef(0.0f, 0.0f, length-tail);
		gluSphere(qobj, fac2*tail + (1.0f-fac2)*head, 16, 10);
		glTranslatef(0.0f, 0.0f, -length+tail);
		
		glTranslatef(0.0f, 0.0f, head);
		gluSphere(qobj, fac1*head + (1.0f-fac1)*tail, 16, 10);
	}
	else {		
		/* 1 sphere in center */
		glTranslatef(0.0f, 0.0f, (head + length-tail)/2.0);
		gluSphere(qobj, fac1*head + (1.0f-fac1)*tail, 16, 10);
	}
	
	/* restore */
	if (dt==OB_SOLID) {
		glShadeModel(GL_FLAT);
		glDisable(GL_LIGHTING);
		glDisable(GL_COLOR_MATERIAL);
	}
	
	glPopMatrix();
	gluDeleteQuadric(qobj);  
}

static GLubyte bm_dot6[]= {0x0, 0x18, 0x3C, 0x7E, 0x7E, 0x3C, 0x18, 0x0}; 
static GLubyte bm_dot8[]= {0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C}; 

static GLubyte bm_dot5[]= {0x0, 0x0, 0x10, 0x38, 0x7c, 0x38, 0x10, 0x0}; 
static GLubyte bm_dot7[]= {0x0, 0x38, 0x7C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38}; 


static void draw_line_bone(int armflag, int boneflag, int constflag, unsigned int id, bPoseChannel *pchan, EditBone *ebone)
{
	float length;
	
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	
	if (pchan) 
		length= pchan->bone->length;
	else 
		length= ebone->length;
	
	glPushMatrix();
	glScalef(length, length, length);
	
	/* this chunk not in object mode */
	if (armflag & (ARM_EDITMODE|ARM_POSEMODE)) {
		glLineWidth(4.0);
		if (armflag & ARM_POSEMODE)
			set_pchan_glColor(PCHAN_COLOR_NORMAL, armflag, boneflag, constflag);
		else if (armflag & ARM_EDITMODE) {
			BIF_ThemeColor(TH_WIRE);
		}
		
		/*	Draw root point if we are not connected */
		if ((boneflag & BONE_CONNECTED)==0) {
			if (G.f & G_PICKSEL) {	// no bitmap in selection mode, crashes 3d cards...
				glLoadName (id | BONESEL_ROOT);
				glBegin(GL_POINTS);
				glVertex3f(0.0f, 0.0f, 0.0f);
				glEnd();
			}
			else {
				glRasterPos3f(0.0f, 0.0f, 0.0f);
				glBitmap(8, 8, 4, 4, 0, 0, bm_dot8);
			}
		}
		
		if (id != -1)
			glLoadName((GLuint) id|BONESEL_BONE);
		
		glBegin(GL_LINES);
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(0.0f, 1.0f, 0.0f);
		glEnd();
		
		/* tip */
		if (G.f & G_PICKSEL) {	
			/* no bitmap in selection mode, crashes 3d cards... */
			glLoadName(id | BONESEL_TIP);
			glBegin(GL_POINTS);
			glVertex3f(0.0f, 1.0f, 0.0f);
			glEnd();
		}
		else {
			glRasterPos3f(0.0f, 1.0f, 0.0f);
			glBitmap(8, 8, 4, 4, 0, 0, bm_dot7);
		}
		
		/* further we send no names */
		if (id != -1)
			glLoadName(id & 0xFFFF);	/* object tag, for bordersel optim */
		
		if (armflag & ARM_POSEMODE)
			set_pchan_glColor(PCHAN_COLOR_LINEBONE, armflag, boneflag, constflag);
	}
	
	glLineWidth(2.0);
	
	/*Draw root point if we are not connected */
	if ((boneflag & BONE_CONNECTED)==0) {
		if ((G.f & G_PICKSEL)==0) {	
			/* no bitmap in selection mode, crashes 3d cards... */
			if (armflag & ARM_EDITMODE) {
				if (boneflag & BONE_ROOTSEL) BIF_ThemeColor(TH_VERTEX_SELECT);
				else BIF_ThemeColor(TH_VERTEX);
			}
			glRasterPos3f(0.0f, 0.0f, 0.0f);
			glBitmap(8, 8, 4, 4, 0, 0, bm_dot6);
		}
	}
	
	if (armflag & ARM_EDITMODE) {
		if (boneflag & BONE_SELECTED) BIF_ThemeColor(TH_EDGE_SELECT);
		else BIF_ThemeColorShade(TH_BACK, -30);
	}
	glBegin(GL_LINES);
	glVertex3f(0.0f, 0.0f, 0.0f);
	glVertex3f(0.0f, 1.0f, 0.0f);
	glEnd();
	
	/* tip */
	if ((G.f & G_PICKSEL)==0) {	
		/* no bitmap in selection mode, crashes 3d cards... */
		if (armflag & ARM_EDITMODE) {
			if (boneflag & BONE_TIPSEL) BIF_ThemeColor(TH_VERTEX_SELECT);
			else BIF_ThemeColor(TH_VERTEX);
		}
		glRasterPos3f(0.0f, 1.0f, 0.0f);
		glBitmap(8, 8, 4, 4, 0, 0, bm_dot5);
	}
	
	glLineWidth(1.0);
	
	glPopMatrix();
}

static void draw_b_bone_boxes(int dt, bPoseChannel *pchan, float xwidth, float length, float zwidth)
{
	int segments= 0;
	
	if (pchan) 
		segments= pchan->bone->segments;
	
	if ((segments > 1) && (pchan)) {
		float dlen= length/(float)segments;
		Mat4 *bbone= b_bone_spline_setup(pchan, 0);
		int a;
		
		for (a=0; a<segments; a++, bbone++) {
			glPushMatrix();
			glMultMatrixf(bbone->mat);
			if (dt==OB_SOLID) drawsolidcube_size(xwidth, dlen, zwidth);
			else drawcube_size(xwidth, dlen, zwidth);
			glPopMatrix();
		}
	}
	else {
		glPushMatrix();
		if (dt==OB_SOLID) drawsolidcube_size(xwidth, length, zwidth);
		else drawcube_size(xwidth, length, zwidth);
		glPopMatrix();
	}
}

static void draw_b_bone(int dt, int armflag, int boneflag, int constflag, unsigned int id, bPoseChannel *pchan, EditBone *ebone)
{
	float xwidth, length, zwidth;
	
	if (pchan) {
		xwidth= pchan->bone->xwidth;
		length= pchan->bone->length;
		zwidth= pchan->bone->zwidth;
	}
	else {
		xwidth= ebone->xwidth;
		length= ebone->length;
		zwidth= ebone->zwidth;
	}
	
	/* draw points only if... */
	if (armflag & ARM_EDITMODE) {
		/* move to unitspace */
		glPushMatrix();
		glScalef(length, length, length);
		draw_bone_points(dt, armflag, boneflag, id);
		glPopMatrix();
		length*= 0.95f;	// make vertices visible
	}

	/* colors for modes */
	if (armflag & ARM_POSEMODE) {
		if (dt <= OB_WIRE)
			set_pchan_glColor(PCHAN_COLOR_NORMAL, armflag, boneflag, constflag);
		else 
			set_pchan_glColor(PCHAN_COLOR_SOLID, armflag, boneflag, constflag);
	}
	else if (armflag & ARM_EDITMODE) {
		if (dt==OB_WIRE) {
			if (boneflag & BONE_ACTIVE) BIF_ThemeColor(TH_EDGE_SELECT);
			else if (boneflag & BONE_SELECTED) BIF_ThemeColorShade(TH_EDGE_SELECT, -20);
			else BIF_ThemeColor(TH_WIRE);
		}
		else 
			BIF_ThemeColor(TH_BONE_SOLID);
	}
	
	if (id != -1) {
		glLoadName ((GLuint) id|BONESEL_BONE);
	}
	
	/* set up solid drawing */
	if (dt > OB_WIRE) {
		glEnable(GL_COLOR_MATERIAL);
		glEnable(GL_LIGHTING);
		
		if (armflag & ARM_POSEMODE)
			set_pchan_glColor(PCHAN_COLOR_SOLID, armflag, boneflag, constflag);
		else
			BIF_ThemeColor(TH_BONE_SOLID);
		
		draw_b_bone_boxes(OB_SOLID, pchan, xwidth, length, zwidth);
		
		/* disable solid drawing */
		glDisable(GL_COLOR_MATERIAL);
		glDisable(GL_LIGHTING);
	}
	else {	
		/* wire */
		if (armflag & ARM_POSEMODE) {
			if (constflag) {
				/* set constraint colors */
				if (set_pchan_glColor(PCHAN_COLOR_CONSTS, armflag, boneflag, constflag)) {
					glEnable(GL_BLEND);
					
					draw_b_bone_boxes(OB_SOLID, pchan, xwidth, length, zwidth);
					
					glDisable(GL_BLEND);
				}
				
				/* restore colors */
				set_pchan_glColor(PCHAN_COLOR_NORMAL, armflag, boneflag, constflag);
			}
		}		
		
		draw_b_bone_boxes(OB_WIRE, pchan, xwidth, length, zwidth);		
	}
}

static void draw_bone(int dt, int armflag, int boneflag, int constflag, unsigned int id, float length)
{
	
	/*	Draw a 3d octahedral bone, we use normalized space based on length,
	    for glDisplayLists */
	
	glScalef(length, length, length);

	/* set up solid drawing */
	if (dt > OB_WIRE) {
		glEnable(GL_COLOR_MATERIAL);
		glEnable(GL_LIGHTING);
		BIF_ThemeColor(TH_BONE_SOLID);
	}
	
	/* colors for posemode */
	if (armflag & ARM_POSEMODE) {
		if (dt <= OB_WIRE)
			set_pchan_glColor(PCHAN_COLOR_NORMAL, armflag, boneflag, constflag);
		else 
			set_pchan_glColor(PCHAN_COLOR_SOLID, armflag, boneflag, constflag);
	}
	
	
	draw_bone_points(dt, armflag, boneflag, id);
	
	/* now draw the bone itself */
	if (id != -1) {
		glLoadName((GLuint) id|BONESEL_BONE);
	}
	
	/* wire? */
	if (dt <= OB_WIRE) {
		/* colors */
		if (armflag & ARM_EDITMODE) {
			if (boneflag & BONE_ACTIVE) BIF_ThemeColor(TH_EDGE_SELECT);
			else if (boneflag & BONE_SELECTED) BIF_ThemeColorShade(TH_EDGE_SELECT, -20);
			else BIF_ThemeColor(TH_WIRE);
		}
		else if (armflag & ARM_POSEMODE) {
			if (constflag) {
				/* draw constraint colors */
				if (set_pchan_glColor(PCHAN_COLOR_CONSTS, armflag, boneflag, constflag)) {	
					glEnable(GL_BLEND);
					
					draw_bone_solid_octahedral();
					
					glDisable(GL_BLEND);
				}
				
				/* restore colors */
				set_pchan_glColor(PCHAN_COLOR_NORMAL, armflag, boneflag, constflag);
			}
		}		
		draw_bone_octahedral();
	}
	else {	
		/* solid */
		if (armflag & ARM_POSEMODE)
			set_pchan_glColor(PCHAN_COLOR_SOLID, armflag, boneflag, constflag);
		else
			BIF_ThemeColor(TH_BONE_SOLID);
		draw_bone_solid_octahedral();
	}

	/* disable solid drawing */
	if (dt > OB_WIRE) {
		glDisable(GL_COLOR_MATERIAL);
		glDisable(GL_LIGHTING);
	}
}

static void draw_custom_bone(Object *ob, int dt, int armflag, int boneflag, unsigned int id, float length)
{
	if(ob==NULL) return;
	
	glScalef(length, length, length);
	
	/* colors for posemode */
	if (armflag & ARM_POSEMODE) {
		set_pchan_glColor(PCHAN_COLOR_NORMAL, armflag, boneflag, 0);
	}
	
	if (id != -1) {
		glLoadName((GLuint) id|BONESEL_BONE);
	}
	
	draw_object_instance(ob, dt, armflag & ARM_POSEMODE);
}


static void pchan_draw_IK_root_lines(bPoseChannel *pchan, short only_temp)
{
	bConstraint *con;
	bPoseChannel *parchan;
	
	for (con= pchan->constraints.first; con; con= con->next) {
		if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce!=0.0)) {
			bKinematicConstraint *data = (bKinematicConstraint*)con->data;
			int segcount= 0;
			
			/* if only_temp, only draw if it is a temporary ik-chain */
			if ((only_temp) && !(data->flag & CONSTRAINT_IK_TEMP))
				continue;
			
			setlinestyle(3);
			glBegin(GL_LINES);
			
			/* exclude tip from chain? */
			if ((data->flag & CONSTRAINT_IK_TIP)==0)
				parchan= pchan->parent;
			else
				parchan= pchan;
			
			glVertex3fv(parchan->pose_tail);
			
			/* Find the chain's root */
			while (parchan->parent) {
				segcount++;
				if(segcount==data->rootbone || segcount>255) break; // 255 is weak
				parchan= parchan->parent;
			}
			if (parchan)
				glVertex3fv(parchan->pose_head);
			
			glEnd();
			setlinestyle(0);
		}
	}
}

static void bgl_sphere_project(float ax, float az)
{
	float dir[3], sine, q3;

	sine= 1.0f-ax*ax-az*az;
	q3= (sine < 0.0f)? 0.0f: 2.0f*sqrt(sine);

	dir[0]= -az*q3;
	dir[1]= 1.0f-2.0f*sine;
	dir[2]= ax*q3;

	glVertex3fv(dir);
}

static void draw_dof_ellipse(float ax, float az)
{
	static float staticSine[16] = {
		0.0, 0.104528463268, 0.207911690818, 0.309016994375,
		0.406736643076, 0.5, 0.587785252292, 0.669130606359,
		0.743144825477, 0.809016994375, 0.866025403784,
		0.913545457643, 0.951056516295, 0.978147600734,
		0.994521895368, 1.0
	};

	int i, j, n=16;
	float x, z, px, pz;

	glEnable(GL_BLEND);
	glDepthMask(0);

	glColor4ub(70, 70, 70, 50);

	glBegin(GL_QUADS);
	pz= 0.0f;
	for(i=1; i<n; i++) {
		z= staticSine[i];

		px= 0.0f;
		for(j=1; j<n-i+1; j++) {
			x = staticSine[j];

			if(j == n-i) {
				glEnd();
				glBegin(GL_TRIANGLES);
				bgl_sphere_project(ax*px, az*z);
				bgl_sphere_project(ax*px, az*pz);
				bgl_sphere_project(ax*x, az*pz);
				glEnd();
				glBegin(GL_QUADS);
			}
			else {
				bgl_sphere_project(ax*x, az*z);
				bgl_sphere_project(ax*x, az*pz);
				bgl_sphere_project(ax*px, az*pz);
				bgl_sphere_project(ax*px, az*z);
			}

			px= x;
		}
		pz= z;
	}
	glEnd();

	glDisable(GL_BLEND);
	glDepthMask(1);

	glColor3ub(0, 0, 0);

	glBegin(GL_LINE_STRIP);
	for (i=0; i<n; i++)
		bgl_sphere_project(staticSine[n-i-1]*ax, staticSine[i]*az);
	glEnd();
}

static void draw_pose_dofs(Object *ob)
{
	bArmature *arm= ob->data;
	bPoseChannel *pchan;
	Bone *bone;
	
	for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
		bone= pchan->bone;
		
		if ( (bone) && !(bone->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG))) {
			if (bone->flag & BONE_SELECTED) {
				if (bone->layer & arm->layer) {
					if (pchan->ikflag & (BONE_IK_XLIMIT|BONE_IK_ZLIMIT)) {
						if (pose_channel_in_IK_chain(ob, pchan)) {
							float corner[4][3], posetrans[3], mat[4][4];
							float phi=0.0f, theta=0.0f, scale;
							int a, i;
							
							/* in parent-bone pose, but own restspace */
							glPushMatrix();
							
							VECCOPY(posetrans, pchan->pose_mat[3]);
							glTranslatef(posetrans[0], posetrans[1], posetrans[2]);
							
							if (pchan->parent) {
								Mat4CpyMat4(mat, pchan->parent->pose_mat);
								mat[3][0]= mat[3][1]= mat[3][2]= 0.0f;
								glMultMatrixf(mat);
							}
							
							Mat4CpyMat3(mat, pchan->bone->bone_mat);
							glMultMatrixf(mat);
							
							scale= bone->length*pchan->size[1];
							glScalef(scale, scale, scale);
							
							if (pchan->ikflag & BONE_IK_XLIMIT) {
								if (pchan->ikflag & BONE_IK_ZLIMIT) {
									float amin[3], amax[3];
									
									for (i=0; i<3; i++) {
										amin[i]= sin(pchan->limitmin[i]*M_PI/360.0);
										amax[i]= sin(pchan->limitmax[i]*M_PI/360.0);
									}
									
									glScalef(1.0, -1.0, 1.0);
									if (amin[0] != 0.0 && amin[2] != 0.0)
										draw_dof_ellipse(amin[0], amin[2]);
									if (amin[0] != 0.0 && amax[2] != 0.0)
										draw_dof_ellipse(amin[0], amax[2]);
									if (amax[0] != 0.0 && amin[2] != 0.0)
										draw_dof_ellipse(amax[0], amin[2]);
									if (amax[0] != 0.0 && amax[2] != 0.0)
										draw_dof_ellipse(amax[0], amax[2]);
									glScalef(1.0, -1.0, 1.0);
								}
							}
							
							/* arcs */
							if (pchan->ikflag & BONE_IK_ZLIMIT) {
								theta= 0.5*(pchan->limitmin[2]+pchan->limitmax[2]);
								glRotatef(theta, 0.0f, 0.0f, 1.0f);
								
								glColor3ub(50, 50, 255);	// blue, Z axis limit
								glBegin(GL_LINE_STRIP);
								for (a=-16; a<=16; a++) {
									float fac= ((float)a)/16.0f;
									phi= fac*(M_PI/360.0f)*(pchan->limitmax[2]-pchan->limitmin[2]);
									
									i= (a == -16) ? 0 : 1;
									corner[i][0]= sin(phi);
									corner[i][1]= cos(phi);
									corner[i][2]= 0.0f;
									glVertex3fv(corner[i]);
								}
								glEnd();
								
								glRotatef(-theta, 0.0f, 0.0f, 1.0f);
							}					
							
							if (pchan->ikflag & BONE_IK_XLIMIT) {
								theta=  0.5*(pchan->limitmin[0]+pchan->limitmax[0]);
								glRotatef(theta, 1.0f, 0.0f, 0.0f);
								
								glColor3ub(255, 50, 50);	// Red, X axis limit
								glBegin(GL_LINE_STRIP);
								for (a=-16; a<=16; a++) {
									float fac= ((float)a)/16.0f;
									phi= 0.5f*M_PI + fac*(M_PI/360.0f)*(pchan->limitmax[0]-pchan->limitmin[0]);
									
									i= (a == -16) ? 2 : 3;
									corner[i][0]= 0.0f;
									corner[i][1]= sin(phi);
									corner[i][2]= cos(phi);
									glVertex3fv(corner[i]);
								}
								glEnd();
								
								glRotatef(-theta, 1.0f, 0.0f, 0.0f);
							}
							
							/* out of cone, out of bone */
							glPopMatrix(); 
						}
					}
				}
			}
		}
	}
}

/* assumes object is Armature with pose */
static void draw_pose_channels(Base *base, int dt)
{
	Object *ob= base->object;
	bArmature *arm= ob->data;
	bPoseChannel *pchan;
	Bone *bone;
	GLfloat tmp;
	float smat[4][4], imat[4][4];
	int index= -1;
	short do_dashed= 3, draw_wire= 0;
	short flag, constflag;
	
	/* hacky... prevent outline select from drawing dashed helplines */
	glGetFloatv(GL_LINE_WIDTH, &tmp);
	if (tmp > 1.1) do_dashed &= ~1;
	if (G.vd->flag & V3D_HIDE_HELPLINES) do_dashed &= ~2;
	
	/* precalc inverse matrix for drawing screen aligned */
	if (arm->drawtype==ARM_ENVELOPE) {
		/* precalc inverse matrix for drawing screen aligned */
		mygetmatrix(smat);
		Mat4MulFloat3(smat[0], 1.0f/VecLength(ob->obmat[0]));
		Mat4Invert(imat, smat);
		
		/* and draw blended distances */
		if (arm->flag & ARM_POSEMODE) {
			glEnable(GL_BLEND);
			//glShadeModel(GL_SMOOTH);
			
			if (G.vd->zbuf) glDisable(GL_DEPTH_TEST);
			
			for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
				bone= pchan->bone;
				if (bone && !(bone->flag & (BONE_HIDDEN_P|BONE_NO_DEFORM|BONE_HIDDEN_PG))) {
					if (bone->flag & (BONE_SELECTED)) {
						if (bone->layer & arm->layer)
							draw_sphere_bone_dist(smat, imat, bone->flag, pchan, NULL);
					}
				}
			}
			
			if (G.vd->zbuf) glEnable(GL_DEPTH_TEST);
			glDisable(GL_BLEND);
			//glShadeModel(GL_FLAT);
		}
	}
	
	/* little speedup, also make sure transparent only draws once */
	glCullFace(GL_BACK); 
	glEnable(GL_CULL_FACE);
	
	/* if solid we draw that first, with selection codes, but without names, axes etc */
	if (dt > OB_WIRE) {
		if (arm->flag & ARM_POSEMODE) 
			index= base->selcol;
		
		for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
			bone= pchan->bone;
			
			if ( (bone) && !(bone->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG)) ) {
				if (bone->layer & arm->layer) {
					glPushMatrix();
					glMultMatrixf(pchan->pose_mat);
					
					/* catch exception for bone with hidden parent */
					flag= bone->flag;
					if ( (bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG)) )
						flag &= ~BONE_CONNECTED;
						
					/* set color-set to use */
					set_pchan_colorset(ob, pchan);
					
					if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) {
						/* if drawwire, don't try to draw in solid */
						if (pchan->bone->flag & BONE_DRAWWIRE) 
							draw_wire= 1;
						else
							draw_custom_bone(pchan->custom, OB_SOLID, arm->flag, flag, index, bone->length);
					}
					else if (arm->drawtype==ARM_LINE)
						;	/* nothing in solid */
					else if (arm->drawtype==ARM_ENVELOPE)
						draw_sphere_bone(OB_SOLID, arm->flag, flag, 0, index, pchan, NULL);
					else if (arm->drawtype==ARM_B_BONE)
						draw_b_bone(OB_SOLID, arm->flag, flag, 0, index, pchan, NULL);
					else
						draw_bone(OB_SOLID, arm->flag, flag, 0, index, bone->length);
						
					glPopMatrix();
				}
			}
			
			if (index!= -1) 
				index+= 0x10000;	// pose bones count in higher 2 bytes only
		}
		
		/* very very confusing... but in object mode, solid draw, we cannot do glLoadName yet,
		 * stick bones and/or wire custom-shapes are drawn in next loop 
		 */
		if ((arm->drawtype != ARM_LINE) && (draw_wire == 0)) {
			/* object tag, for bordersel optim */
			glLoadName(index & 0xFFFF);	
			index= -1;
		}
	}
	
	/* draw custom bone shapes as wireframes */
	if ( !(arm->flag & ARM_NO_CUSTOM) &&
		 ((draw_wire) || (dt <= OB_WIRE)) ) 
	{
		if (arm->flag & ARM_POSEMODE)
			index= base->selcol;
			
		/* only draw custom bone shapes that need to be drawn as wires */
		for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
			bone= pchan->bone;
			
			if ((bone) && !(bone->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG))) {
				if (bone->layer & arm->layer) {
					if (pchan->custom) {
						if ((dt < OB_SOLID) || (bone->flag & BONE_DRAWWIRE)) {
							glPushMatrix();
							glMultMatrixf(pchan->pose_mat);
							
							/* prepare colors */
							if (arm->flag & ARM_POSEMODE)	
								set_pchan_colorset(ob, pchan);
							else {
								if ((G.scene->basact)==base) {
									if (base->flag & (SELECT+BA_WAS_SEL)) BIF_ThemeColor(TH_ACTIVE);
									else BIF_ThemeColor(TH_WIRE);
								}
								else {
									if (base->flag & (SELECT+BA_WAS_SEL)) BIF_ThemeColor(TH_SELECT);
									else BIF_ThemeColor(TH_WIRE);
								}
							}
								
							/* catch exception for bone with hidden parent */
							flag= bone->flag;
							if ((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG)))
								flag &= ~BONE_CONNECTED;
							
							draw_custom_bone(pchan->custom, OB_WIRE, arm->flag, flag, index, bone->length);
							
							glPopMatrix();
						}
					}
				}
			}
			
			if (index != -1) 
				index+= 0x10000;	// pose bones count in higher 2 bytes only
		}
		
		if (draw_wire) {
			/* object tag, for bordersel optim */
			glLoadName(index & 0xFFFF);	
			index= -1;
		}
	}
	
	/* wire draw over solid only in posemode */
	if ((dt <= OB_WIRE) || (arm->flag & ARM_POSEMODE) || (arm->drawtype==ARM_LINE)) {
		/* draw line check first. we do selection indices */
		if (arm->drawtype==ARM_LINE) {
			if (arm->flag & ARM_POSEMODE) 
				index= base->selcol;
		}
		/* if solid && posemode, we draw again with polygonoffset */
		else if ((dt > OB_WIRE) && (arm->flag & ARM_POSEMODE)) {
			bglPolygonOffset(1.0);
		}
		else {
			/* and we use selection indices if not done yet */
			if (arm->flag & ARM_POSEMODE) 
				index= base->selcol;
		}
		
		for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
			bone= pchan->bone;
			
			if ((bone) && !(bone->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG))) {
				if (bone->layer & arm->layer) {
					if ((do_dashed & 1) && (bone->parent)) {
						/* Draw a line from our root to the parent's tip 
						 *	- only if V3D_HIDE_HELPLINES is enabled...
						 */
						if ( (do_dashed & 2) && ((bone->flag & BONE_CONNECTED)==0) ) {
							if (arm->flag & ARM_POSEMODE) {
								glLoadName(index & 0xFFFF);	// object tag, for bordersel optim
								BIF_ThemeColor(TH_WIRE);
							}
							setlinestyle(3);
							glBegin(GL_LINES);
							glVertex3fv(pchan->pose_head);
							glVertex3fv(pchan->parent->pose_tail);
							glEnd();
							setlinestyle(0);
						}
						
						/* Draw a line to IK root bone 
						 * 	- only if temporary chain (i.e. "autoik")
						 */
						if (arm->flag & ARM_POSEMODE) {
							if (pchan->constflag & PCHAN_HAS_IK) {
								if (bone->flag & BONE_SELECTED) {
									if (pchan->constflag & PCHAN_HAS_TARGET) glColor3ub(200, 120, 0);
									else glColor3ub(200, 200, 50);	// add theme!
									
									glLoadName(index & 0xFFFF);
									pchan_draw_IK_root_lines(pchan, !(do_dashed & 2));
								}
							}
						}
					}
					
					glPushMatrix();
					if (arm->drawtype != ARM_ENVELOPE)
						glMultMatrixf(pchan->pose_mat);
					
					/* catch exception for bone with hidden parent */
					flag= bone->flag;
					if ((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG)))
						flag &= ~BONE_CONNECTED;
					
					/* extra draw service for pose mode */
					constflag= pchan->constflag;
					if (pchan->flag & (POSE_ROT|POSE_LOC|POSE_SIZE))
						constflag |= PCHAN_HAS_ACTION;
					if (pchan->flag & POSE_STRIDE)
						constflag |= PCHAN_HAS_STRIDE;
						
					/* set color-set to use */
					set_pchan_colorset(ob, pchan);
					
					if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM))
						; // custom bone shapes should not be drawn here!
					else if (arm->drawtype==ARM_ENVELOPE) {
						if (dt < OB_SOLID)
							draw_sphere_bone_wire(smat, imat, arm->flag, flag, constflag, index, pchan, NULL);
					}
					else if (arm->drawtype==ARM_LINE)
						draw_line_bone(arm->flag, flag, constflag, index, pchan, NULL);
					else if (arm->drawtype==ARM_B_BONE)
						draw_b_bone(OB_WIRE, arm->flag, flag, constflag, index, pchan, NULL);
					else
						draw_bone(OB_WIRE, arm->flag, flag, constflag, index, bone->length);
					
					glPopMatrix();
				}
			}
			
			/* pose bones count in higher 2 bytes only */
			if (index != -1) 
				index+= 0x10000;	
		}
		/* restore things */
		if ((arm->drawtype!=ARM_LINE)&& (dt>OB_WIRE) && (arm->flag & ARM_POSEMODE))
			bglPolygonOffset(0.0);
	}	
	
	/* restore */
	glDisable(GL_CULL_FACE);
	
	/* draw DoFs */
	if (arm->flag & ARM_POSEMODE)
		draw_pose_dofs(ob);

	/* finally names and axes */
	if (arm->flag & (ARM_DRAWNAMES|ARM_DRAWAXES)) {
		/* patch for several 3d cards (IBM mostly) that crash on glSelect with text drawing */
		if ((G.f & G_PICKSEL) == 0) {
			float vec[3];
			
			if (G.vd->zbuf) glDisable(GL_DEPTH_TEST);
			
			for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) {
				if ((pchan->bone->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG))==0) {
					if (pchan->bone->layer & arm->layer) {
						if (arm->flag & (ARM_EDITMODE|ARM_POSEMODE)) {
							bone= pchan->bone;
							
							if (bone->flag & BONE_SELECTED) BIF_ThemeColor(TH_TEXT_HI);
							else BIF_ThemeColor(TH_TEXT);
						}
						else if (dt > OB_WIRE)
							BIF_ThemeColor(TH_TEXT);
						
						/* 	Draw names of bone 	*/
						if (arm->flag & ARM_DRAWNAMES) {
							VecMidf(vec, pchan->pose_head, pchan->pose_tail);
							glRasterPos3fv(vec);
							BMF_DrawString(G.font, " ");
							BMF_DrawString(G.font, pchan->name);
						}	
						
						/*	Draw additional axes on the bone tail  */
						if ( (arm->flag & ARM_DRAWAXES) && (arm->flag & ARM_POSEMODE) ) {
							glPushMatrix();
							glMultMatrixf(pchan->pose_mat);
							glTranslatef(0.0f, pchan->bone->length, 0.0f);
							drawaxes(0.25f*pchan->bone->length, 0, OB_ARROWS);
							glPopMatrix();
						}
					}
				}
			}
			
			if (G.vd->zbuf) glEnable(GL_DEPTH_TEST);
		}
	}
}

/* in editmode, we don't store the bone matrix... */
static void set_matrix_editbone(EditBone *eBone)
{
	float		delta[3],offset[3];
	float		mat[3][3], bmat[4][4];
	
	/* Compose the parent transforms (i.e. their translations) */
	VECCOPY(offset, eBone->head);
	
	glTranslatef(offset[0],offset[1],offset[2]);
	
	VecSubf(delta, eBone->tail, eBone->head);	
	
	eBone->length = sqrt (delta[0]*delta[0] + delta[1]*delta[1] +delta[2]*delta[2]);
	
	vec_roll_to_mat3(delta, eBone->roll, mat);
	Mat4CpyMat3(bmat, mat);
		
	glMultMatrixf(bmat);
	
}

static void draw_ebones(Object *ob, int dt)
{
	EditBone *eBone;
	bArmature *arm= ob->data;
	float smat[4][4], imat[4][4];
	unsigned int index;
	int flag;
	
	/* envelope (deform distance) */
	if(arm->drawtype==ARM_ENVELOPE) {
		/* precalc inverse matrix for drawing screen aligned */
		mygetmatrix(smat);
		Mat4MulFloat3(smat[0], 1.0f/VecLength(ob->obmat[0]));
		Mat4Invert(imat, smat);
		
		/* and draw blended distances */
		glEnable(GL_BLEND);
		//glShadeModel(GL_SMOOTH);
		
		if (G.vd->zbuf) glDisable(GL_DEPTH_TEST);

		for (eBone=G.edbo.first, index=0; eBone; eBone=eBone->next, index++) {
			if (eBone->layer & arm->layer) {
				if ((eBone->flag & (BONE_HIDDEN_A|BONE_NO_DEFORM))==0) {
					if (eBone->flag & (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL))
						draw_sphere_bone_dist(smat, imat, eBone->flag, NULL, eBone);
				}
			}
		}
		
		if (G.vd->zbuf) glEnable(GL_DEPTH_TEST);
		glDisable(GL_BLEND);
		//glShadeModel(GL_FLAT);
	}
	
	/* if solid we draw it first */
	if ((dt > OB_WIRE) && (arm->drawtype!=ARM_LINE)) {
		index= 0;
		for (eBone=G.edbo.first, index=0; eBone; eBone=eBone->next, index++) {
			if (eBone->layer & arm->layer) {
				if ((eBone->flag & BONE_HIDDEN_A)==0) {
					glPushMatrix();
					set_matrix_editbone(eBone);
					
					/* catch exception for bone with hidden parent */
					flag= eBone->flag;
					if ( (eBone->parent) && ((eBone->parent->flag & BONE_HIDDEN_A) || (eBone->parent->layer & arm->layer)==0) )
						flag &= ~BONE_CONNECTED;
					
					if (arm->drawtype==ARM_ENVELOPE)
						draw_sphere_bone(OB_SOLID, arm->flag, flag, 0, index, NULL, eBone);
					else if(arm->drawtype==ARM_B_BONE)
						draw_b_bone(OB_SOLID, arm->flag, flag, 0, index, NULL, eBone);
					else {
						draw_bone(OB_SOLID, arm->flag, flag, 0, index, eBone->length);
					}
					
					glPopMatrix();
				}
			}
		}
	}
	
	/* if wire over solid, set offset */
	index= -1;
	glLoadName(-1);
	if (arm->drawtype==ARM_LINE) {
		if(G.f & G_PICKSEL)
			index= 0;
	}
	else if (dt > OB_WIRE) 
		bglPolygonOffset(1.0);
	else if (arm->flag & ARM_EDITMODE) 
		index= 0;	/* do selection codes */
	
	for (eBone=G.edbo.first; eBone; eBone=eBone->next) {
		if (eBone->layer & arm->layer) {
			if ((eBone->flag & BONE_HIDDEN_A)==0) {
				
				/* catch exception for bone with hidden parent */
				flag= eBone->flag;
				if ( (eBone->parent) && ((eBone->parent->flag & BONE_HIDDEN_A) || (eBone->parent->layer & arm->layer)==0) )
					flag &= ~BONE_CONNECTED;
				
				if (arm->drawtype == ARM_ENVELOPE) {
					if (dt < OB_SOLID)
						draw_sphere_bone_wire(smat, imat, arm->flag, flag, 0, index, NULL, eBone);
				}
				else {
					glPushMatrix();
					set_matrix_editbone(eBone);
					
					if (arm->drawtype == ARM_LINE) 
						draw_line_bone(arm->flag, flag, 0, index, NULL, eBone);
					else if (arm->drawtype == ARM_B_BONE)
						draw_b_bone(OB_WIRE, arm->flag, flag, 0, index, NULL, eBone);
					else
						draw_bone(OB_WIRE, arm->flag, flag, 0, index, eBone->length);
					
					glPopMatrix();
				}
				
				/* offset to parent */
				if (eBone->parent) {
					BIF_ThemeColor(TH_WIRE);
					glLoadName (-1);		// -1 here is OK!
					setlinestyle(3);
					
					glBegin(GL_LINES);
					glVertex3fv(eBone->parent->tail);
					glVertex3fv(eBone->head);
					glEnd();
					
					setlinestyle(0);
				}
			}
		}
		if(index!=-1) index++;
	}
	
	/* restore */
	if (arm->drawtype==ARM_LINE);
	else if (dt>OB_WIRE) bglPolygonOffset(0.0);
	
	/* finally names and axes */
	if (arm->flag & (ARM_DRAWNAMES|ARM_DRAWAXES)) {
		// patch for several 3d cards (IBM mostly) that crash on glSelect with text drawing
		if ((G.f & G_PICKSEL) == 0) {
			float vec[3];
			
			if (G.vd->zbuf) glDisable(GL_DEPTH_TEST);
			
			for (eBone=G.edbo.first, index=0; eBone; eBone=eBone->next, index++) {
				if(eBone->layer & arm->layer) {
					if ((eBone->flag & BONE_HIDDEN_A)==0) {
						
						if (eBone->flag & BONE_SELECTED) BIF_ThemeColor(TH_TEXT_HI);
						else BIF_ThemeColor(TH_TEXT);
						
						/*	Draw name */
						if (arm->flag & ARM_DRAWNAMES) {
							VecMidf(vec, eBone->head, eBone->tail);
							glRasterPos3fv(vec);
							BMF_DrawString(G.font, " ");
							BMF_DrawString(G.font, eBone->name);
						}					
						/*	Draw additional axes */
						if (arm->flag & ARM_DRAWAXES) {
							glPushMatrix();
							set_matrix_editbone(eBone);
							glTranslatef(0.0f, eBone->length, 0.0f);
							drawaxes(eBone->length*0.25f, 0, OB_ARROWS);
							glPopMatrix();
						}
						
					}
				}
			}
			
			if (G.vd->zbuf) glEnable(GL_DEPTH_TEST);
		}
	}
}

/* ****************************** Armature Visualisation ******************************** */

/* ---------- Paths --------- */

/* draw bone paths
 *	- in view space 
 */
static void draw_pose_paths(Object *ob)
{
	bArmature *arm= ob->data;
	bPoseChannel *pchan;
	bAction *act;
	bActionChannel *achan;
	ActKeyColumn *ak;
	ListBase keys;
	float *fp, *fp_start;
	int a, stepsize;
	int sfra, efra, len;
	
	if (G.vd->zbuf) glDisable(GL_DEPTH_TEST);
	
	glPushMatrix();
	glLoadMatrixf(G.vd->viewmat);
	
	/* version patch here - cannot access frame info from file reading */
	if (arm->pathsize == 0) arm->pathsize= 1;
	stepsize = arm->pathsize;
	
	for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
		if (pchan->bone->layer & arm->layer) {
			if (pchan->path) {
				/* version patch here - cannot access frame info from file reading */
				if ((pchan->pathsf == 0) || (pchan->pathef == 0)) {
					pchan->pathsf= SFRA;
					pchan->pathef= EFRA;
				}
				
				/* get frame ranges */
				if (arm->pathflag & ARM_PATH_ACFRA) {
					int sind;
					
					/* With "Around Current", we only choose frames from around 
					 * the current frame to draw. However, this range is still 
					 * restricted by the limits of the original path.
					 */
					sfra= CFRA - arm->pathbc;
					efra= CFRA + arm->pathac;
					if (sfra < pchan->pathsf) sfra= pchan->pathsf;
					if (efra > pchan->pathef) efra= pchan->pathef;
					
					len= efra - sfra;
					
					sind= sfra - pchan->pathsf;
					fp_start= (pchan->path + (3*sind));
				}
				else {
					sfra= pchan->pathsf;
					efra = sfra + pchan->pathlen;
					len = pchan->pathlen;
					fp_start = pchan->path;
				}
				
				/* draw curve-line of path */
				glShadeModel(GL_SMOOTH);
				
				glBegin(GL_LINE_STRIP); 				
				for (a=0, fp=fp_start; a<len; a++, fp+=3) {
					float intensity; /* how faint */
					
					/* set color
					 * 	- more intense for active/selected bones, less intense for unselected bones
					 * 	- black for before current frame, green for current frame, blue for after current frame
					 * 	- intensity decreases as distance from current frame increases
					 */
					#define SET_INTENSITY(A, B, C, min, max) (((1.0f - ((C - B) / (C - A))) * (max-min)) + min) 
					if ((a+sfra) < CFRA) {
						/* black - before cfra */
						if (pchan->bone->flag & BONE_SELECTED) {
							// intensity= 0.5;
							intensity = SET_INTENSITY(sfra, a, CFRA, 0.25f, 0.75f);
						}
						else {
							//intensity= 0.8;
							intensity = SET_INTENSITY(sfra, a, CFRA, 0.68f, 0.92f);
						}
						BIF_ThemeColorBlend(TH_WIRE, TH_BACK, intensity);
					}
					else if ((a+sfra) > CFRA) {
						/* blue - after cfra */
						if (pchan->bone->flag & BONE_SELECTED) {
							//intensity = 0.5;
							intensity = SET_INTENSITY(CFRA, a, efra, 0.25f, 0.75f);
						}
						else {
							//intensity = 0.8;
							intensity = SET_INTENSITY(CFRA, a, efra, 0.68f, 0.92f);
						}
						BIF_ThemeColorBlend(TH_BONE_POSE, TH_BACK, intensity);
					}
					else {
						/* green - on cfra */
						if (pchan->bone->flag & BONE_SELECTED) {
							intensity= 0.5;
						}
						else {
							intensity= 0.99;
						}
						BIF_ThemeColorBlendShade(TH_CFRAME, TH_BACK, intensity, 10);
					}	
					
					/* draw a vertex with this color */ 
					glVertex3fv(fp);
				}
				
				glEnd();
				glShadeModel(GL_FLAT);
				
				glPointSize(1.0);
				
				/* draw little black point at each frame
				 * NOTE: this is not really visible/noticable
				 */
				glBegin(GL_POINTS);
				for (a=0, fp=fp_start; a<len; a++, fp+=3) 
					glVertex3fv(fp);
				glEnd();
				
				/* Draw little white dots at each framestep value */
				BIF_ThemeColor(TH_TEXT_HI);
				glBegin(GL_POINTS);
				for (a=0, fp=fp_start; a<len; a+=stepsize, fp+=(stepsize*3)) 
					glVertex3fv(fp);
				glEnd();
				
				/* Draw frame numbers at each framestep value */
				if (arm->pathflag & ARM_PATH_FNUMS) {
					for (a=0, fp=fp_start; a<len; a+=stepsize, fp+=(stepsize*3)) {
						char str[32];
						
						/* only draw framenum if several consecutive highlighted points don't occur on same point */
						if (a == 0) {
							glRasterPos3fv(fp);
							sprintf(str, "  %d\n", (a+sfra));
							BMF_DrawString(G.font, str);
						}
						else if ((a > stepsize) && (a < len-stepsize)) { 
							if ((VecEqual(fp, fp-(stepsize*3))==0) || (VecEqual(fp, fp+(stepsize*3))==0)) {
								glRasterPos3fv(fp);
								sprintf(str, "  %d\n", (a+sfra));
								BMF_DrawString(G.font, str);
							}
						}
					}
				}
				
				/* Keyframes - dots and numbers */
				if (arm->pathflag & ARM_PATH_KFRAS) {
					/* build list of all keyframes in active action for pchan */
					keys.first = keys.last = NULL;	
					act= ob_get_action(ob);
					if (act) {
						achan= get_action_channel(act, pchan->name);
						if (achan) 
							ipo_to_keylist(achan->ipo, &keys, NULL, NULL);
					}
					
					/* Draw slightly-larger yellow dots at each keyframe */
					BIF_ThemeColor(TH_VERTEX_SELECT);
					glPointSize(5.0);
					
					glBegin(GL_POINTS);
					for (a=0, fp=fp_start; a<len; a++, fp+=3) {
						for (ak= keys.first; ak; ak= ak->next) {
							if (ak->cfra == (a+sfra))
								glVertex3fv(fp);
						}
					}
					glEnd();
					
					glPointSize(1.0);
					
					/* Draw frame numbers of keyframes  */
					if ((arm->pathflag & ARM_PATH_FNUMS) || (arm->pathflag & ARM_PATH_KFNOS)) {
						for(a=0, fp=fp_start; a<len; a++, fp+=3) {
							for (ak= keys.first; ak; ak= ak->next) {
								if (ak->cfra == (a+sfra)) {
									char str[32];
									
									glRasterPos3fv(fp);
									sprintf(str, "  %d\n", (a+sfra));
									BMF_DrawString(G.font, str);
								}
							}
						}
					}
					
					BLI_freelistN(&keys);
				}
			}
		}
	}
	
	if (G.vd->zbuf) glEnable(GL_DEPTH_TEST);
	glPopMatrix();
}


/* ---------- Ghosts --------- */

/* helper function for ghost drawing - sets/removes flags for temporarily 
 * hiding unselected bones while drawing ghosts
 */
static void ghost_poses_tag_unselected(Object *ob, short unset)
{
	bArmature *arm= ob->data;
	bPose *pose= ob->pose;
	bPoseChannel *pchan;
	
	/* don't do anything if no hiding any bones */
	if ((arm->flag & ARM_GHOST_ONLYSEL)==0)
		return;
		
	/* loop over all pchans, adding/removing tags as appropriate */
	for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
		if ((pchan->bone) && (arm->layer & pchan->bone->layer)) {
			if (unset) {
				/* remove tags from all pchans if cleaning up */
				pchan->bone->flag &= ~BONE_HIDDEN_PG;
			}
			else {
				/* set tags on unselected pchans only */
				if ((pchan->bone->flag & BONE_SELECTED)==0)
					pchan->bone->flag |= BONE_HIDDEN_PG;
			}
		}
	}
}

/* draw ghosts that occur within a frame range 
 * 	note: object should be in posemode 
 */
static void draw_ghost_poses_range(Base *base)
{
	Object *ob= base->object;
	bArmature *arm= ob->data;
	bPose *posen, *poseo;
	float start, end, stepsize, range, colfac;
	int cfrao, flago, ipoflago;
	
	start = arm->ghostsf;
	end = arm->ghostef;
	if (end <= start)
		return;
	
	stepsize= (float)(arm->ghostsize);
	range= (float)(end - start);
	
	/* store values */
	ob->flag &= ~OB_POSEMODE;
	cfrao= CFRA;
	flago= arm->flag;
	arm->flag &= ~(ARM_DRAWNAMES|ARM_DRAWAXES);
	ipoflago= ob->ipoflag; 
	ob->ipoflag |= OB_DISABLE_PATH;
	
	/* copy the pose */
	poseo= ob->pose;
	copy_pose(&posen, ob->pose, 1);
	ob->pose= posen;
	armature_rebuild_pose(ob, ob->data);	/* child pointers for IK */
	ghost_poses_tag_unselected(ob, 0);		/* hide unselected bones if need be */
	
	glEnable(GL_BLEND);
	if (G.vd->zbuf) glDisable(GL_DEPTH_TEST);
	
	/* draw from first frame of range to last */
	for (CFRA= start; CFRA<end; CFRA+=stepsize) {
		colfac = (end-CFRA)/range;
		BIF_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0f*sqrt(colfac)));
		
		do_all_pose_actions(ob);
		where_is_pose(ob);
		draw_pose_channels(base, OB_WIRE);
	}
	glDisable(GL_BLEND);
	if (G.vd->zbuf) glEnable(GL_DEPTH_TEST);

	ghost_poses_tag_unselected(ob, 1);		/* unhide unselected bones if need be */
	free_pose(posen);
	
	/* restore */
	CFRA= cfrao;
	ob->pose= poseo;
	arm->flag= flago;
	armature_rebuild_pose(ob, ob->data);
	ob->flag |= OB_POSEMODE;
	ob->ipoflag= ipoflago; 
}

/* draw ghosts on keyframes in action within range 
 *	- object should be in posemode 
 */
static void draw_ghost_poses_keys(Base *base)
{
	Object *ob= base->object;
	bAction *act= ob_get_action(ob);
	bArmature *arm= ob->data;
	bPose *posen, *poseo;
	ListBase keys= {NULL, NULL};
	ActKeysInc aki = {0, 0, 0};
	ActKeyColumn *ak, *akn;
	float start, end, range, colfac, i;
	int cfrao, flago, ipoflago;
	
	aki.start= start = arm->ghostsf;
	aki.end= end = arm->ghostef;
	if (end <= start)
		return;
	
	/* get keyframes - then clip to only within range */
	action_to_keylist(act, &keys, NULL, &aki);
	range= 0;
	for (ak= keys.first; ak; ak= akn) {
		akn= ak->next;
		
		if ((ak->cfra < start) || (ak->cfra > end))
			BLI_freelinkN(&keys, ak);
		else
			range++;
	}
	if (range == 0) return;
	
	/* store values */
	ob->flag &= ~OB_POSEMODE;
	cfrao= CFRA;
	flago= arm->flag;
	arm->flag &= ~(ARM_DRAWNAMES|ARM_DRAWAXES);
	ipoflago= ob->ipoflag; 
	ob->ipoflag |= OB_DISABLE_PATH;
	
	/* copy the pose */
	poseo= ob->pose;
	copy_pose(&posen, ob->pose, 1);
	ob->pose= posen;
	armature_rebuild_pose(ob, ob->data);	/* child pointers for IK */
	ghost_poses_tag_unselected(ob, 0);		/* hide unselected bones if need be */
	
	glEnable(GL_BLEND);
	if (G.vd->zbuf) glDisable(GL_DEPTH_TEST);
	
	/* draw from first frame of range to last */
	for (ak=keys.first, i=0; ak; ak=ak->next, i++) {
		colfac = i/range;
		BIF_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0f*sqrt(colfac)));
		
		CFRA= (int)ak->cfra;
		
		do_all_pose_actions(ob);
		where_is_pose(ob);
		draw_pose_channels(base, OB_WIRE);
	}
	glDisable(GL_BLEND);
	if (G.vd->zbuf) glEnable(GL_DEPTH_TEST);

	ghost_poses_tag_unselected(ob, 1);		/* unhide unselected bones if need be */
	BLI_freelistN(&keys);
	free_pose(posen);
	
	/* restore */
	CFRA= cfrao;
	ob->pose= poseo;
	arm->flag= flago;
	armature_rebuild_pose(ob, ob->data);
	ob->flag |= OB_POSEMODE;
	ob->ipoflag= ipoflago; 
}

/* draw ghosts around current frame
 * 	- object is supposed to be armature in posemode 
 */
static void draw_ghost_poses(Base *base)
{
	Object *ob= base->object;
	bArmature *arm= ob->data;
	bPose *posen, *poseo;
	bActionStrip *strip;
	float cur, start, end, stepsize, range, colfac, actframe, ctime;
	int cfrao, maptime, flago, ipoflago;
	
	/* pre conditions, get an action with sufficient frames */
	if (ob->action==NULL)
		return;

	calc_action_range(ob->action, &start, &end, 0);
	if (start == end)
		return;

	stepsize= (float)(arm->ghostsize);
	range= (float)(arm->ghostep)*stepsize + 0.5f;	/* plus half to make the for loop end correct */
	
	/* we only map time for armature when an active strip exists */
	for (strip=ob->nlastrips.first; strip; strip=strip->next)
		if (strip->flag & ACTSTRIP_ACTIVE)
			break;
	
	maptime= (strip!=NULL);
	
	/* store values */
	ob->flag &= ~OB_POSEMODE;
	cfrao= CFRA;
	if (maptime) actframe= get_action_frame(ob, (float)CFRA);
	else actframe= CFRA;
	flago= arm->flag;
	arm->flag &= ~(ARM_DRAWNAMES|ARM_DRAWAXES);
	ipoflago= ob->ipoflag; 
	ob->ipoflag |= OB_DISABLE_PATH;
	
	/* copy the pose */
	poseo= ob->pose;
	copy_pose(&posen, ob->pose, 1);
	ob->pose= posen;
	armature_rebuild_pose(ob, ob->data);	/* child pointers for IK */
	ghost_poses_tag_unselected(ob, 0);		/* hide unselected bones if need be */
	
	glEnable(GL_BLEND);
	if (G.vd->zbuf) glDisable(GL_DEPTH_TEST);
	
	/* draw from darkest blend to lowest */
	for(cur= stepsize; cur<range; cur+=stepsize) {
		ctime= cur - fmod((float)cfrao, stepsize);	/* ensures consistant stepping */
		colfac= ctime/range;
		BIF_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0f*sqrt(colfac)));
		
		/* only within action range */
		if (actframe+ctime >= start && actframe+ctime <= end) {
			if (maptime) CFRA= (int)get_action_frame_inv(ob, actframe+ctime);
			else CFRA= (int)floor(actframe+ctime);
			
			if (CFRA!=cfrao) {
				do_all_pose_actions(ob);
				where_is_pose(ob);
				draw_pose_channels(base, OB_WIRE);
			}
		}
		
		ctime= cur + fmod((float)cfrao, stepsize) - stepsize+1.0f;	/* ensures consistant stepping */
		colfac= ctime/range;
		BIF_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0f*sqrt(colfac)));
		
		/* only within action range */
		if ((actframe-ctime >= start) && (actframe-ctime <= end)) {
			if (maptime) CFRA= (int)get_action_frame_inv(ob, actframe-ctime);
			else CFRA= (int)floor(actframe-ctime);
			
			if (CFRA != cfrao) {
				do_all_pose_actions(ob);
				where_is_pose(ob);
				draw_pose_channels(base, OB_WIRE);
			}
		}
	}
	glDisable(GL_BLEND);
	if (G.vd->zbuf) glEnable(GL_DEPTH_TEST);

	ghost_poses_tag_unselected(ob, 1);		/* unhide unselected bones if need be */
	free_pose(posen);
	
	/* restore */
	CFRA= cfrao;
	ob->pose= poseo;
	arm->flag= flago;
	armature_rebuild_pose(ob, ob->data);
	ob->flag |= OB_POSEMODE;
	ob->ipoflag= ipoflago; 
}

/* ********************************** Armature Drawing - Main ************************* */

/* called from drawobject.c, return 1 if nothing was drawn */
int draw_armature(Base *base, int dt, int flag)
{
	Object *ob= base->object;
	bArmature *arm= ob->data;
	int retval= 0;

	if(G.f & G_RENDER_SHADOW)
		return 1;
	
	if(dt>OB_WIRE && arm->drawtype!=ARM_LINE) {
		/* we use color for solid lighting */
		glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR);
		glEnable(GL_COLOR_MATERIAL);
		glColor3ub(0,0,0);	// clear spec
		glDisable(GL_COLOR_MATERIAL);
		
		glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
		glFrontFace((ob->transflag&OB_NEG_SCALE)?GL_CW:GL_CCW);	// only for lighting...
	}
	
	/* arm->flag is being used to detect mode... */
	/* editmode? */
	if(ob==G.obedit || (G.obedit && ob->data==G.obedit->data)) {
		if(ob==G.obedit) arm->flag |= ARM_EDITMODE;
		draw_ebones(ob, dt);
		arm->flag &= ~ARM_EDITMODE;
	}
	else{
		/*	Draw Pose */
		if(ob->pose && ob->pose->chanbase.first) {
			/* drawing posemode selection indices or colors only in these cases */
			if(!(base->flag & OB_FROMDUPLI)) {
				if(G.f & G_PICKSEL) {
					if(ob->flag & OB_POSEMODE) 
						arm->flag |= ARM_POSEMODE;
				}
				else if(ob->flag & OB_POSEMODE) {
					if (arm->ghosttype == ARM_GHOST_RANGE) {
						draw_ghost_poses_range(base);
					}
					else if (arm->ghosttype == ARM_GHOST_KEYS) {
						draw_ghost_poses_keys(base);
					}
					else if (arm->ghosttype == ARM_GHOST_CUR) {
						if (arm->ghostep)
							draw_ghost_poses(base);
					}
					if ((flag & DRAW_SCENESET)==0) {
						if(ob==OBACT) 
							arm->flag |= ARM_POSEMODE;
						else if(G.f & G_WEIGHTPAINT)
							arm->flag |= ARM_POSEMODE;
					
						draw_pose_paths(ob);
					}
				}	
			}
			draw_pose_channels(base, dt);
			arm->flag &= ~ARM_POSEMODE; 
			
			if(ob->flag & OB_POSEMODE)
				BIF_ThemeColor(TH_WIRE);	/* restore, for extra draw stuff */
		}
		else retval= 1;
	}
	/* restore */
	glFrontFace(GL_CCW);

	return retval;
}

/* *************** END Armature drawing ******************* */


