/**
 * $Id: drawview.c,v 1.103 2004/12/15 11:36:46 ton Exp $
 *
 * ***** BEGIN GPL/BL DUAL 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. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * 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) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

//#define NAN_LINEAR_PHYSICS

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

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

#ifndef WIN32
#include <unistd.h>
#include <sys/times.h>
#else
#include <io.h>
#include "BLI_winstuff.h"
#endif   

#include "MEM_guardedalloc.h"

#include "PIL_time.h"

#include "BMF_Api.h"

#include "IMB_imbuf_types.h"

#include "DNA_action_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_group_types.h"
#include "DNA_image_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_texture_types.h"
#include "DNA_view3d_types.h"
#include "DNA_userdef_types.h"
#include "DNA_space_types.h"

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

#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_anim.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_global.h"
#include "BKE_ika.h"
#include "BKE_lattice.h"
#include "BKE_library.h"
#include "BKE_image.h"
#include "BKE_ipo.h"
#include "BKE_key.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_texture.h"
#include "BKE_utildefines.h"

#include "BIF_gl.h"
#include "BIF_resources.h"
#include "BIF_screen.h"
#include "BIF_interface.h"
#include "BIF_space.h"
#include "BIF_butspace.h"
#include "BIF_drawimage.h"
#include "BIF_editgroup.h"
#include "BIF_mywindow.h"
#include "BIF_editarmature.h"
#include "BIF_poseobject.h"

#include "BDR_drawmesh.h"
#include "BDR_drawobject.h"
#include "BDR_editobject.h"
#include "BDR_vpaint.h"

#include "BSE_view.h"
#include "BSE_drawview.h"
#include "BSE_headerbuttons.h"
#include "BSE_seqaudio.h"
#include "BSE_filesel.h"
#include "BSE_trans_types.h"

#include "RE_renderconverter.h"

#include "BPY_extern.h"

#include "blendef.h"
#include "mydevice.h"
#include "butspace.h"  // event codes

/* Modules used */
#include "render.h"
#include "radio.h"

/* for physics in animation playback */
#ifdef NAN_LINEAR_PHYSICS
#include "sumo.h"
#endif

/* locals */
void drawname(Object *ob);
void star_stuff_init_func(void);
void star_stuff_vertex_func(float* i);
void star_stuff_term_func(void);

void star_stuff_init_func(void)
{
	cpack(-1);
	glPointSize(1.0);
	glBegin(GL_POINTS);
}
void star_stuff_vertex_func(float* i)
{
	glVertex3fv(i);
}
void star_stuff_term_func(void)
{
	glEnd();
}

void setalpha_bgpic(BGpic *bgpic)
{
	int x, y, alph;
	char *rect;
	
	alph= (int)(255.0*(1.0-bgpic->blend));
	
	rect= (char *)bgpic->rect;
	for(y=0; y< bgpic->yim; y++) {
		for(x= bgpic->xim; x>0; x--, rect+=4) {
			rect[3]= alph;
		}
	}
}


void default_gl_light(void)
{
	int a;
	
	/* initialize */
	if(U.light[0].flag==0 && U.light[1].flag==0 && U.light[2].flag==0) {
		U.light[0].flag= 1;
		U.light[0].vec[0]= -0.3; U.light[0].vec[1]= 0.3; U.light[0].vec[2]= 0.9;
		U.light[0].col[0]= 0.8; U.light[0].col[1]= 0.8; U.light[0].col[2]= 0.8;
		U.light[0].spec[0]= 0.5; U.light[0].spec[1]= 0.5; U.light[0].spec[2]= 0.5;
		U.light[0].spec[3]= 1.0;
		
		U.light[1].flag= 0;
		U.light[1].vec[0]= 0.5; U.light[1].vec[1]= 0.5; U.light[1].vec[2]= 0.1;
		U.light[1].col[0]= 0.4; U.light[1].col[1]= 0.4; U.light[1].col[2]= 0.8;
		U.light[1].spec[0]= 0.3; U.light[1].spec[1]= 0.3; U.light[1].spec[2]= 0.5;
		U.light[1].spec[3]= 1.0;
	
		U.light[2].flag= 0;
		U.light[2].vec[0]= 0.3; U.light[2].vec[1]= -0.3; U.light[2].vec[2]= -0.2;
		U.light[2].col[0]= 0.8; U.light[2].col[1]= 0.5; U.light[2].col[2]= 0.4;
		U.light[2].spec[0]= 0.5; U.light[2].spec[1]= 0.4; U.light[2].spec[2]= 0.3;
		U.light[2].spec[3]= 1.0;
	}
	

	glLightfv(GL_LIGHT0, GL_POSITION, U.light[0].vec); 
	glLightfv(GL_LIGHT0, GL_DIFFUSE, U.light[0].col); 
	glLightfv(GL_LIGHT0, GL_SPECULAR, U.light[0].spec); 

	glLightfv(GL_LIGHT1, GL_POSITION, U.light[1].vec); 
	glLightfv(GL_LIGHT1, GL_DIFFUSE, U.light[1].col); 
	glLightfv(GL_LIGHT1, GL_SPECULAR, U.light[1].spec); 

	glLightfv(GL_LIGHT2, GL_POSITION, U.light[2].vec); 
	glLightfv(GL_LIGHT2, GL_DIFFUSE, U.light[2].col); 
	glLightfv(GL_LIGHT2, GL_SPECULAR, U.light[2].spec); 

	for(a=0; a<8; a++) {
		if(a<3) {
			if(U.light[a].flag) glEnable(GL_LIGHT0+a);
			else glDisable(GL_LIGHT0+a);
			
			// clear stuff from other opengl lamp usage
			glLightf(GL_LIGHT0+a, GL_SPOT_CUTOFF, 180.0);
			glLightf(GL_LIGHT0+a, GL_CONSTANT_ATTENUATION, 1.0);
			glLightf(GL_LIGHT0+a, GL_LINEAR_ATTENUATION, 0.0);
		}
		else glDisable(GL_LIGHT0+a);
	}
	
	glDisable(GL_LIGHTING);

	glDisable(GL_COLOR_MATERIAL);
}

/* also called when render 'ogl' */
void init_gl_stuff(void)	
{
	float mat_specular[] = { 0.5, 0.5, 0.5, 1.0 };
	float mat_shininess[] = { 35.0 };
/*  	float one= 1.0; */
	int a, x, y;
	GLubyte pat[32*32];
	const GLubyte *patc= pat;
		
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

	default_gl_light();
	
	/* no local viewer, looks ugly in ortho mode */
	/* glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, &one); */
	
	glDepthFunc(GL_LEQUAL);
	/* scaling matrices */
	glEnable(GL_NORMALIZE);

	glShadeModel(GL_FLAT);

	glDisable(GL_ALPHA_TEST);
	glDisable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_FOG);
	glDisable(GL_LIGHTING);
	glDisable(GL_LOGIC_OP);
	glDisable(GL_STENCIL_TEST);
	glDisable(GL_TEXTURE_1D);
	glDisable(GL_TEXTURE_2D);

	glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
	glPixelTransferi(GL_RED_SCALE, 1);
	glPixelTransferi(GL_RED_BIAS, 0);
	glPixelTransferi(GL_GREEN_SCALE, 1);
	glPixelTransferi(GL_GREEN_BIAS, 0);
	glPixelTransferi(GL_BLUE_SCALE, 1);
	glPixelTransferi(GL_BLUE_BIAS, 0);
	glPixelTransferi(GL_ALPHA_SCALE, 1);
	glPixelTransferi(GL_ALPHA_BIAS, 0);
	
	glPixelTransferi(GL_DEPTH_BIAS, 0);
	glPixelTransferi(GL_DEPTH_SCALE, 1);
	glDepthRange(0.0, 1.0);
	
	a= 0;
	for(x=0; x<32; x++) {
		for(y=0; y<4; y++) {
			if( (x) & 1) pat[a++]= 0x88;
			else pat[a++]= 0x22;
		}
	}
	
	glPolygonStipple(patc);


	init_realtime_GL();	
}

void two_sided(int val)
{

	/* twosided on: gives errors with x flip! */
	glLightModeliv(GL_LIGHT_MODEL_TWO_SIDE, (GLint *)&val);
}

void circf(float x, float y, float rad)
{
	GLUquadricObj *qobj = gluNewQuadric(); 
	
	gluQuadricDrawStyle(qobj, GLU_FILL); 
	
	glPushMatrix(); 
	
	glTranslatef(x,  y, 0.); 
	
	gluDisk( qobj, 0.0,  rad, 32, 1); 
	
	glPopMatrix(); 
	
	gluDeleteQuadric(qobj);
}

void circ(float x, float y, float rad)
{
	GLUquadricObj *qobj = gluNewQuadric(); 
	
	gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); 
	
	glPushMatrix(); 
	
	glTranslatef(x,  y, 0.); 
	
	gluDisk( qobj, 0.0,  rad, 32, 1); 
	
	glPopMatrix(); 
	
	gluDeleteQuadric(qobj);
}

/* **********  ********** */

static void draw_bgpic(void)
{
	BGpic *bgpic;
	Image *ima;
	float vec[3], fac, asp, zoomx, zoomy;
	int x1, y1, x2, y2, cx, cy;
	short mval[2];
	
	bgpic= G.vd->bgpic;
	if(bgpic==0) return;
	
	if(bgpic->tex) {
		init_render_texture(bgpic->tex);
		free_unused_animimages();
		ima= bgpic->tex->ima;
		end_render_texture(bgpic->tex);
	}
	else {
		ima= bgpic->ima;
	}
	
	if(ima==0) return;
	if(ima->ok==0) return;
	
	/* test for image */
	if(ima->ibuf==0) {
	
		if(bgpic->rect) MEM_freeN(bgpic->rect);
		bgpic->rect= 0;
		
		if(bgpic->tex) {
			ima_ibuf_is_nul(bgpic->tex);
		}
		else {
			waitcursor(1);
			load_image(ima, IB_rect, G.sce, G.scene->r.cfra);
			waitcursor(0);
		}
		if(ima->ibuf==0) {
			ima->ok= 0;
			return;
		}
	}

	if(bgpic->rect==0) {
		
		bgpic->rect= MEM_dupallocN(ima->ibuf->rect);
		bgpic->xim= ima->ibuf->x;
		bgpic->yim= ima->ibuf->y;
		setalpha_bgpic(bgpic);
	}

	if(G.vd->persp==2) {
		rcti vb;

		calc_viewborder(G.vd, &vb);

		x1= vb.xmin;
		y1= vb.ymin;
		x2= vb.xmax;
		y2= vb.ymax;
	}
	else {
		/* calc window coord */
		initgrabz(0.0, 0.0, 0.0);
		window_to_3d(vec, 1, 0);
		fac= MAX3( fabs(vec[0]), fabs(vec[1]), fabs(vec[1]) );
		fac= 1.0/fac;
	
		asp= ( (float)ima->ibuf->y)/(float)ima->ibuf->x;
	
		vec[0]= vec[1]= vec[2]= 0.0;
		project_short_noclip(vec, mval);
		cx= mval[0];
		cy= mval[1];
	
		x1=  cx+ fac*(bgpic->xof-bgpic->size);
		y1=  cy+ asp*fac*(bgpic->yof-bgpic->size);
		x2=  cx+ fac*(bgpic->xof+bgpic->size);
		y2=  cy+ asp*fac*(bgpic->yof+bgpic->size);
	}
	
	/* complete clip? */
	
	if(x2 < 0 ) return;
	if(y2 < 0 ) return;
	if(x1 > curarea->winx ) return;
	if(y1 > curarea->winy ) return;
	
	zoomx= x2-x1;
	zoomx /= (float)ima->ibuf->x;
	zoomy= y2-y1;
	zoomy /= (float)ima->ibuf->y;

	glEnable(GL_BLEND);
	if(G.zbuf) glDisable(GL_DEPTH_TEST);

	glBlendFunc(GL_SRC_ALPHA,  GL_ONE_MINUS_SRC_ALPHA); 
	 
	rectwrite_part(curarea->winrct.xmin, curarea->winrct.ymin, curarea->winrct.xmax, curarea->winrct.ymax, 
                   x1+curarea->winrct.xmin, y1+curarea->winrct.ymin, ima->ibuf->x, ima->ibuf->y, zoomx, zoomy, bgpic->rect);

	glBlendFunc(GL_ONE,  GL_ZERO); 
	glDisable(GL_BLEND);
	if(G.zbuf) glEnable(GL_DEPTH_TEST);
	 
}

void timestr(double time, char *str)
{
	/* format 00:00:00.00 (hr:min:sec) string has to be 12 long */
	int  hr= (int)      time/(60*60);
	int min= (int) fmod(time/60, 60.0);
	int sec= (int) fmod(time, 60.0);
	int hun= (int) fmod(time*100.0, 100.0);

	if (hr) {
		sprintf(str, "%.2d:%.2d:%.2d.%.2d",hr,min,sec,hun);
	} else {
		sprintf(str, "%.2d:%.2d.%.2d",min,sec,hun);
	}

	str[11]=0;
}

static void drawgrid_draw(float wx, float wy, float x, float y, float dx)
{
	float fx, fy;
	
	x+= (wx); 
	y+= (wy);
	fx= x/dx;
	fx= x-dx*floor(fx);
	
	while(fx< curarea->winx) {
		fdrawline(fx,  0.0,  fx,  (float)curarea->winy); 
		fx+= dx; 
	}

	fy= y/dx;
	fy= y-dx*floor(fy);
	

	while(fy< curarea->winy) {
		fdrawline(0.0,  fy,  (float)curarea->winx,  fy); 
		fy+= dx;
	}

}

// not intern, called in editobject for constraint axis too
void make_axis_color(char *col, char *col2, char axis)
{
	if(axis=='x') {
		col2[0]= col[0]>219?255:col[0]+36;
		col2[1]= col[1]<26?0:col[1]-26;
		col2[2]= col[2]<26?0:col[2]-26;
	}
	else if(axis=='y') {
		col2[0]= col[0]<46?0:col[0]-36;
		col2[1]= col[1]>189?255:col[1]+66;
		col2[2]= col[2]<46?0:col[2]-36; 
	}
	else {
		col2[0]= col[0]<26?0:col[0]-26; 
		col2[1]= col[1]<26?0:col[1]-26; 
		col2[2]= col[2]>209?255:col[2]+46;
	}
	
}

static void drawgrid(void)
{
	/* extern short bgpicmode; */
	float wx, wy, x, y, fw, fx, fy, dx;
	float vec4[4];
	char col[3], col2[3];
	
	vec4[0]=vec4[1]=vec4[2]=0.0; 
	vec4[3]= 1.0;
	Mat4MulVec4fl(G.vd->persmat, vec4);
	fx= vec4[0]; 
	fy= vec4[1]; 
	fw= vec4[3];

	wx= (curarea->winx/2.0);	/* because of rounding errors, grid at wrong location */
	wy= (curarea->winy/2.0);

	x= (wx)*fx/fw;
	y= (wy)*fy/fw;

	vec4[0]=vec4[1]=G.vd->grid; 
	vec4[2]= 0.0;
	vec4[3]= 1.0;
	Mat4MulVec4fl(G.vd->persmat, vec4);
	fx= vec4[0]; 
	fy= vec4[1]; 
	fw= vec4[3];

	dx= fabs(x-(wx)*fx/fw);
	if(dx==0) dx= fabs(y-(wy)*fy/fw);
	
	glDepthMask(0);		// disable write in zbuffer
	
	/* check zoom out */
	BIF_ThemeColor(TH_GRID);
	persp(PERSP_WIN);

	if(dx<6.0) {
		dx*= 10.0;
		
		if(dx<6.0) {	
			dx*= 10.0;
			
			if(dx<6.0) {
				dx*=10;
				if(dx<6.0);
				else {
					BIF_ThemeColor(TH_GRID);
					drawgrid_draw(wx, wy, x, y, dx);
				}
			}
			else {	// start blending out
				BIF_ThemeColorBlend(TH_BACK, TH_GRID, dx/60.0);
				drawgrid_draw(wx, wy, x, y, dx);
			
				BIF_ThemeColor(TH_GRID);
				drawgrid_draw(wx, wy, x, y, 10*dx);
			}
		}
		else {	// start blending out (6 < dx < 60)
			BIF_ThemeColorBlend(TH_BACK, TH_GRID, dx/60.0);
			drawgrid_draw(wx, wy, x, y, dx);
			
			BIF_ThemeColor(TH_GRID);
			drawgrid_draw(wx, wy, x, y, 10*dx);
		}
	}
	else {
		if(dx>60.0) {		// start blending in
			dx/= 10.0;			
			if(dx>60.0) {		// start blending in
				dx/= 10.0;
				if(dx>60.0) {
					BIF_ThemeColor(TH_GRID);
					drawgrid_draw(wx, wy, x, y, dx);
				}
				else {
					BIF_ThemeColorBlend(TH_BACK, TH_GRID, dx/60.0);
					drawgrid_draw(wx, wy, x, y, dx);
					BIF_ThemeColor(TH_GRID);
					drawgrid_draw(wx, wy, x, y, dx*10);
				}
			}
			else {
				BIF_ThemeColorBlend(TH_BACK, TH_GRID, dx/60.0);
				drawgrid_draw(wx, wy, x, y, dx);
				BIF_ThemeColor(TH_GRID);				
				drawgrid_draw(wx, wy, x, y, dx*10);
			}
		}
		else {
			BIF_ThemeColorBlend(TH_BACK, TH_GRID, dx/60.0);
			drawgrid_draw(wx, wy, x, y, dx);
			BIF_ThemeColor(TH_GRID);
			drawgrid_draw(wx, wy, x, y, dx*10);
		}
	}

	x+= (wx); 
	y+= (wy);
	BIF_GetThemeColor3ubv(TH_GRID, col);

	setlinestyle(0);
	
	/* center cross */
	if(G.vd->view==3) make_axis_color(col, col2, 'y');
	else make_axis_color(col, col2, 'x');
	glColor3ubv(col2);
	
	fdrawline(0.0,  y,  (float)curarea->winx,  y); 
	
	if(G.vd->view==7) make_axis_color(col, col2, 'y');
	else make_axis_color(col, col2, 'z');
	glColor3ubv(col2);

	fdrawline(x, 0.0, x, (float)curarea->winy); 

	glDepthMask(1);		// enable write in zbuffer
	persp(PERSP_VIEW);
}


static void drawfloor(void)
{
	View3D *vd;
	float vert[3], grid;
	int a, gridlines;
	char col[3], col2[3];
	short draw_line = 0;
		
	vd= curarea->spacedata.first;

	vert[2]= 0.0;

	if(vd->gridlines<3) return;
	
	if(G.zbuf && G.obedit) glDepthMask(0);	// for zbuffer-select
	
	gridlines= vd->gridlines/2;
	grid= gridlines*vd->grid;
	
	BIF_GetThemeColor3ubv(TH_GRID, col);
	
	/* draw the Y axis and/or grid lines */
	for(a= -gridlines;a<=gridlines;a++) {
		if(a==0) {
			/* check for the 'show Y axis' preference */
			if (vd->gridflag & V3D_SHOW_Y) { 
				make_axis_color(col, col2, 'y');
				glColor3ubv(col2);
				
				draw_line = 1;
			} else if (vd->gridflag & V3D_SHOW_FLOOR) {
				BIF_ThemeColorShade(TH_GRID, -10);
			} else {
				draw_line = 0;
			}
		} else {
			/* check for the 'show grid floor' preference */
			if (vd->gridflag & V3D_SHOW_FLOOR) {
				if( (a % 10)==0) {
					BIF_ThemeColorShade(TH_GRID, -10);
				}
				else BIF_ThemeColorShade(TH_GRID, 10);
				
				draw_line = 1;
			} else {
				draw_line = 0;
			}
		}
		
		if (draw_line) {
			glBegin(GL_LINE_STRIP);
	        vert[0]= a*vd->grid;
	        vert[1]= grid;
	        glVertex3fv(vert);
	        vert[1]= -grid;
	        glVertex3fv(vert);
			glEnd();
		}
	}
	
	/* draw the X axis and/or grid lines */
	for(a= -gridlines;a<=gridlines;a++) {
		if(a==0) {
			/* check for the 'show X axis' preference */
			if (vd->gridflag & V3D_SHOW_X) { 
				make_axis_color(col, col2, 'x');
				glColor3ubv(col2);
				
				draw_line = 1;
			} else if (vd->gridflag & V3D_SHOW_FLOOR) {
				BIF_ThemeColorShade(TH_GRID, -10);
			} else {
				draw_line = 0;
			}
		} else {
			/* check for the 'show grid floor' preference */
			if (vd->gridflag & V3D_SHOW_FLOOR) {
				if( (a % 10)==0) {
					BIF_ThemeColorShade(TH_GRID, -10);
				}
				else BIF_ThemeColorShade(TH_GRID, 10);
				
				draw_line = 1;
			} else {
				draw_line = 0;
			}
		}
		
		if (draw_line) {
			glBegin(GL_LINE_STRIP);
	        vert[1]= a*vd->grid;
	        vert[0]= grid;
	        glVertex3fv(vert );
	        vert[0]= -grid;
	        glVertex3fv(vert);
			glEnd();
		}
	}
	
	/* draw the Z axis line */	
	/* check for the 'show Z axis' preference */
	if (vd->gridflag & V3D_SHOW_Z) {
		make_axis_color(col, col2, 'z');
		glColor3ubv(col2);
		
		glBegin(GL_LINE_STRIP);
		vert[0]= 0;
		vert[1]= 0;
		vert[2]= grid;
		glVertex3fv(vert );
		vert[2]= -grid;
		glVertex3fv(vert);
		glEnd();
	}

	if(G.zbuf && G.obedit) glDepthMask(1);	

}

static void drawcursor(void)
{

	if(G.f & G_PLAYANIM) return;
	
	project_short( give_cursor(), &G.vd->mx);

	G.vd->mxo= G.vd->mx;
	G.vd->myo= G.vd->my;

	if( G.vd->mx!=3200) {
		
		setlinestyle(0); 
		cpack(0xFF);
		circ((float)G.vd->mx, (float)G.vd->my, 10.0);
		setlinestyle(4); 
		cpack(0xFFFFFF);
		circ((float)G.vd->mx, (float)G.vd->my, 10.0);
		setlinestyle(0);
		cpack(0x0);
		
		sdrawline(G.vd->mx-20, G.vd->my, G.vd->mx-5, G.vd->my);
		sdrawline(G.vd->mx+5, G.vd->my, G.vd->mx+20, G.vd->my);
		sdrawline(G.vd->mx, G.vd->my-20, G.vd->mx, G.vd->my-5);
		sdrawline(G.vd->mx, G.vd->my+5, G.vd->mx, G.vd->my+20);
	}
}

static void view3d_get_viewborder_size(View3D *v3d, float size_r[2])
{
	float winmax= MAX2(v3d->area->winx, v3d->area->winy);
	float aspect= (float) (G.scene->r.xsch*G.scene->r.xasp)/(G.scene->r.ysch*G.scene->r.yasp);

	if(aspect>1.0) {
		size_r[0]= winmax;
		size_r[1]= winmax/aspect;
	} else {
		size_r[0]= winmax*aspect;
		size_r[1]= winmax;
	}
}

void calc_viewborder(struct View3D *v3d, rcti *viewborder_r)
{
	float zoomfac, size[2];

	view3d_get_viewborder_size(v3d, size);

		/* magic zoom calculation, no idea what
	     * it signifies, if you find out, tell me! -zr
		 */
	/* simple, its magic dude!
	 * well, to be honest, this gives a natural feeling zooming
	 * with multiple keypad presses (ton)
	 */
	
	zoomfac= (M_SQRT2 + v3d->camzoom/50.0);
	zoomfac= (zoomfac*zoomfac)*0.25;
	
	size[0]= size[0]*zoomfac;
	size[1]= size[1]*zoomfac;

		/* center in window */
	viewborder_r->xmin= 0.5*v3d->area->winx - 0.5*size[0];
	viewborder_r->ymin= 0.5*v3d->area->winy - 0.5*size[1];
	viewborder_r->xmax= viewborder_r->xmin + size[0];
	viewborder_r->ymax= viewborder_r->ymin + size[1];
}

void view3d_set_1_to_1_viewborder(View3D *v3d)
{
	float size[2];
	int im_width= (G.scene->r.size*G.scene->r.xsch)/100;

	view3d_get_viewborder_size(v3d, size);

	v3d->camzoom= (sqrt(4.0*im_width/size[0]) - M_SQRT2)*50.0;
	v3d->camzoom= CLAMPIS(v3d->camzoom, -30, 300);
}

static void drawviewborder(void)
{
	float fac, a;
	float x1, x2, y1, y2;
	float x3, y3, x4, y4;
	rcti viewborder;
	
	calc_viewborder(G.vd, &viewborder);
	x1= viewborder.xmin;
	y1= viewborder.ymin;
	x2= viewborder.xmax;
	y2= viewborder.ymax;

	/* passepartout, in color of backdrop minus 50 */
	if(G.scene->r.scemode & R_PASSEPARTOUT) {
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
		glEnable(GL_BLEND);
		glColor4ub(0, 0, 0, 50);
		
		if (x1 > 0.0) {
			glRectf(0.0, (float)curarea->winy, x1, 0.0);
			glRectf(x2, (float)curarea->winy, (float)curarea->winx, 0.0);
		}
		if (y1 > 0.0)	{
			glRectf(x1, (float)curarea->winy, x2, y2);
			glRectf(x1, y1, x2, 0.0);
		}
		glDisable(GL_BLEND);
	}
	/* edge */
	setlinestyle(3);
	cpack(0);
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 
	glRectf(x1+1,  y1-1,  x2+1,  y2-1); 
	
	cpack(0xFFFFFF);
	glRectf(x1,  y1,  x2,  y2); 

	/* border */
	if(G.scene->r.mode & R_BORDER) {
		
		cpack(0);
		x3= x1+ G.scene->r.border.xmin*(x2-x1);
		y3= y1+ G.scene->r.border.ymin*(y2-y1);
		x4= x1+ G.scene->r.border.xmax*(x2-x1);
		y4= y1+ G.scene->r.border.ymax*(y2-y1);
		
		glRectf(x3+1,  y3-1,  x4+1,  y4-1); 
		
		cpack(0x4040FF);
		glRectf(x3,  y3,  x4,  y4); 
	}

	/* safety border */

	fac= 0.1;
	
	a= fac*(x2-x1);
	x1+= a; 
	x2-= a;

	a= fac*(y2-y1);
	y1+= a;
	y2-= a;

	cpack(0);
	glRectf(x1+1,  y1-1,  x2+1,  y2-1);
	cpack(0xFFFFFF);
	glRectf(x1,  y1,  x2,  y2);

	setlinestyle(0);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

}


void backdrawview3d(int test)
{
	struct Base *base;

	if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT));
	else if(G.obedit && G.vd->drawtype>OB_WIRE && (G.vd->flag & V3D_ZBUF_SELECT));
	else {
		G.vd->flag &= ~V3D_NEEDBACKBUFDRAW;
		return;
	}

	if( !(G.vd->flag & V3D_NEEDBACKBUFDRAW) ) return;

	if(test) {
		if(qtest()) {
			addafterqueue(curarea->win, BACKBUFDRAW, 1);
			return;
		}
	}
	persp(PERSP_VIEW);

#ifdef __APPLE__
	glDrawBuffer(GL_AUX0);
#endif	
	if(G.vd->drawtype > OB_WIRE) G.zbuf= TRUE;
	curarea->win_swap &= ~WIN_BACK_OK;
	
	glDisable(GL_DITHER);

	glClearColor(0.0, 0.0, 0.0, 0.0); 
	if(G.zbuf) {
		glEnable(GL_DEPTH_TEST);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	}
	else {
		glClear(GL_COLOR_BUFFER_BIT);
		glDisable(GL_DEPTH_TEST);
	}
	
	G.f |= G_BACKBUFSEL;
	
	base= (G.scene->basact);
	if(base && (base->lay & G.vd->lay)) {
		draw_object_backbufsel(base->object);
	}

	G.vd->flag &= ~V3D_NEEDBACKBUFDRAW;

	G.f &= ~G_BACKBUFSEL;
	G.zbuf= FALSE; 
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_DITHER);

#ifdef __APPLE__
	glDrawBuffer(GL_BACK); /* we were in aux buffers */
#endif

	/* it is important to end a view in a transform compatible with buttons */
	persp(PERSP_WIN);  // set ortho
	bwin_scalematrix(curarea->win, G.vd->blockscale, G.vd->blockscale, G.vd->blockscale);

}

		
void drawname(Object *ob)
{
	cpack(0x404040);
	glRasterPos3f(0.0,  0.0,  0.0);
	
	BMF_DrawString(G.font, " ");
	BMF_DrawString(G.font, ob->id.name+2);
}


static void draw_selected_name(char *name)
{
	char info[128];

	sprintf(info, "(%d) %s", CFRA, name);

	BIF_ThemeColor(TH_TEXT_HI);
	glRasterPos2i(30,  10);
	BMF_DrawString(G.fonts, info);
}


static void draw_view_icon(void)
{
	BIFIconID icon;
	
	if(G.vd->view==7) icon= ICON_AXIS_TOP;
	else if(G.vd->view==1) icon= ICON_AXIS_FRONT;
	else if(G.vd->view==3) icon= ICON_AXIS_SIDE;
	else return ;

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,  GL_ONE_MINUS_SRC_ALPHA); 
	
	glRasterPos2f(5.0, 5.0);
	BIF_draw_icon(icon);
	
	glBlendFunc(GL_ONE,  GL_ZERO); 
	glDisable(GL_BLEND);
}

/* ******************* view3d space & buttons ************** */

static void view3d_change_bgpic_ima(View3D *v3d, Image *newima) {
	if (v3d->bgpic && v3d->bgpic->ima!=newima) {
		if (newima)
			id_us_plus((ID*) newima);
		if (v3d->bgpic->ima)
			v3d->bgpic->ima->id.us--;
		v3d->bgpic->ima= newima;

		if(v3d->bgpic->rect) MEM_freeN(v3d->bgpic->rect);
		v3d->bgpic->rect= NULL;
		
		allqueue(REDRAWVIEW3D, 0);
	}
}
static void view3d_change_bgpic_tex(View3D *v3d, Tex *newtex) {
	if (v3d->bgpic && v3d->bgpic->tex!=newtex) {
		if (newtex)
			id_us_plus((ID*) newtex);
		if (v3d->bgpic->tex)
			v3d->bgpic->tex->id.us--;
		v3d->bgpic->tex= newtex;
		
		allqueue(REDRAWVIEW3D, 0);
	}
}

static void load_bgpic_image(char *name)
{
	Image *ima;
	View3D *vd;
	
	areawinset(curarea->win);
	vd= G.vd;
	if(vd==0 || vd->bgpic==0) return;
	
	ima= add_image(name);
	if(ima) {
		if(vd->bgpic->ima) {
			vd->bgpic->ima->id.us--;
		}
		vd->bgpic->ima= ima;
		
		free_image_buffers(ima);	/* force read again */
		ima->ok= 1;
	}
	allqueue(REDRAWVIEW3D, 0);
	
}

/* this one assumes there is only one global active object in blender...  (for object panel) */
static float ob_eul[4];	// used for quat too....
/* this one assumes there is only one editmode in blender...  (for object panel) */
static float ve_median[4];

/* is used for both read and write... */
static void v3d_editvertex_buts(uiBlock *block, Object *ob, float lim)
{
	EditMesh *em = G.editMesh;
	EditVert *eve;
	EditEdge *eed;
	float median[4];
	int tot, totw, totedge;
	
	median[0]= median[1]= median[2]= median[3]= 0.0;
	tot= totw= totedge= 0;
	
	if(ob->type==OB_MESH) {
		Mesh *me= ob->data;
		
		eve= em->verts.first;
		while(eve) {
			if(eve->f & 1) {
				tot++;
				VecAddf(median, median, eve->co);
			}
			eve= eve->next;
		}
		if(me->medge) {
			eed= em->edges.first;
			while(eed) {
				if((eed->f & SELECT)) {
					totedge++;
					median[3]+= eed->crease;
				}
				eed= eed->next;
			}
		}
	}
	else if(ob->type==OB_CURVE || ob->type==OB_SURF) {
		extern ListBase editNurb; /* editcurve.c */
		Nurb *nu;
		BPoint *bp;
		BezTriple *bezt;
		int a;
		
		nu= editNurb.first;
		while(nu) {
			if((nu->type & 7)==1) {
				bezt= nu->bezt;
				a= nu->pntsu;
				while(a--) {
					if(bezt->f2 & 1) {
						VecAddf(median, median, bezt->vec[1]);
						tot++;
					}
					else {
						if(bezt->f1 & 1) {
							VecAddf(median, median, bezt->vec[0]);
							tot++;
						}
						if(bezt->f3 & 1) {
							VecAddf(median, median, bezt->vec[2]);
							tot++;
						}
					}
					bezt++;
				}
			}
			else {
				bp= nu->bp;
				a= nu->pntsu*nu->pntsv;
				while(a--) {
					if(bp->f1 & 1) {
						VecAddf(median, median, bp->vec);
						median[3]+= bp->vec[3];
						totw++;
						tot++;
					}
					bp++;
				}
			}
			nu= nu->next;
		}
	
	}
	if(tot==0) return;

	median[0] /= (float)tot;
	median[1] /= (float)tot;
	median[2] /= (float)tot;
	if(totedge) median[3] /= (float)totedge;
	else median[3] /= (float)tot;
	
	if(block) {	// buttons
	
		QUATCOPY(ve_median, median);
		
		uiBlockBeginAlign(block);
		if(tot==1) {
			uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Vertex X:",	10, 140, 300, 19, &(ve_median[0]), -lim, lim, 10, 3, "");
			uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Vertex Y:",	10, 120, 300, 19, &(ve_median[1]), -lim, lim, 10, 3, "");
			uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Vertex Z:",	10, 100, 300, 19, &(ve_median[2]), -lim, lim, 10, 3, "");
			if(totw==1)
				uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Vertex W:",	10, 80, 300, 19, &(ve_median[3]), 0.01, 100.0, 10, 3, "");
		}
		else {
			uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Median X:",	10, 140, 300, 19, &(ve_median[0]), -lim, lim, 10, 3, "");
			uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Median Y:",	10, 120, 300, 19, &(ve_median[1]), -lim, lim, 10, 3, "");
			uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Median Z:",	10, 100, 300, 19, &(ve_median[2]), -lim, lim, 10, 3, "");
			if(totw==tot)
				uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Median W:",	10, 80, 300, 19, &(ve_median[3]), 0.01, 100.0, 10, 3, "");
		}
		uiBlockEndAlign(block);
		
		if(totedge==1)
			uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Crease W:",	10, 60, 300, 19, &(ve_median[3]), 0.0, 1.0, 10, 3, "");
		else if(totedge>1)
			uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, "Median Crease W:",	10, 60, 300, 19, &(ve_median[3]), 0.0, 1.0, 10, 3, "");
		
	}
	else {	// apply
		
		VecSubf(median, ve_median, median);
		median[3]= ve_median[3]-median[3];
		
		if(ob->type==OB_MESH) {
			float diffac= 1.0;
			
			eve= em->verts.first;
			while(eve) {
				if(eve->f & 1) {
					VecAddf(eve->co, eve->co, median);
				}
				eve= eve->next;
			}
			
			/* calculate the differences to squeeze a range smaller when values are close to 1 or 0 */
			/* this way you can edit a median value which is applied on clipped values :) */
			if(totedge>1) {
				float max= 0.0;
				for(eed= em->edges.first; eed; eed= eed->next) {
					if(eed->f & SELECT) {
						if(max < ABS(eed->crease-ve_median[3])) max= ABS(eed->crease-ve_median[3]);
					}
				}
				if(max>0.0) {
					if(ve_median[3]> 0.5) diffac= (1.0-ve_median[3])/max;
					else diffac= (ve_median[3])/max;
					if(diffac>1.0) diffac= 1.0;
				}
			}
			
			for(eed= em->edges.first; eed; eed= eed->next) {
				if(eed->f & SELECT) {
					eed->crease+= median[3];
					eed->crease= ve_median[3] + diffac*(eed->crease-ve_median[3]);
					
					CLAMP(eed->crease, 0.0, 1.0);
				}
			}
		}
		else if(ob->type==OB_CURVE || ob->type==OB_SURF) {
			extern ListBase editNurb; /* editcurve.c */
			Nurb *nu;
			BPoint *bp;
			BezTriple *bezt;
			int a;
			
			nu= editNurb.first;
			while(nu) {
				if((nu->type & 7)==1) {
					bezt= nu->bezt;
					a= nu->pntsu;
					while(a--) {
						if(bezt->f2 & 1) {
							VecAddf(bezt->vec[0], bezt->vec[0], median);
							VecAddf(bezt->vec[1], bezt->vec[1], median);
							VecAddf(bezt->vec[2], bezt->vec[2], median);
						}
						else {
							if(bezt->f1 & 1) {
								VecAddf(bezt->vec[0], bezt->vec[0], median);
							}
							if(bezt->f3 & 1) {
								VecAddf(bezt->vec[2], bezt->vec[2], median);
							}
						}
						bezt++;
					}
				}
				else {
					bp= nu->bp;
					a= nu->pntsu*nu->pntsv;
					while(a--) {
						if(bp->f1 & 1) {
							VecAddf(bp->vec, bp->vec, median);
							bp->vec[3]+= median[3];
							tot++;
						}
						bp++;
					}
				}
				test2DNurb(nu);
				testhandlesNurb(nu); /* test for bezier too */

				nu= nu->next;
			}
		}
		BIF_undo_push("Transform properties");
	}
}

static void v3d_posearmature_buts(uiBlock *block, Object *ob, float lim)
{
	bArmature *arm;
	Bone *bone;

	arm = get_armature(OBACT);
	if (!arm)
		return;

	bone = get_first_selected_bone();

	if (!bone)
		return;
	uiBlockBeginAlign(block);
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "QuatX:",	10, 120, 140, 19, bone->quat, -100.0, 100.0, 10, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "QuatZ:",	10, 100, 140, 19, bone->quat+2, -100.0, 100.0, 10, 3, "");
	uiBlockBeginAlign(block);
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "QuatY:",	160, 120, 140, 19, bone->quat+1, -100.0, 100.0, 10, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "QuatW:",	160, 100, 140, 19, bone->quat+3, -100.0, 100.0, 10, 3, "");
	uiBlockBeginAlign(block);
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "LocX:",	10, 70, 140, 19, bone->loc, -lim, lim, 100, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "LocY:",	10, 50, 140, 19, bone->loc+1, -lim, lim, 100, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "locZ:",	10, 30, 140, 19, bone->loc+2, -lim, lim, 100, 3, "");
	uiBlockBeginAlign(block);
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "SizeX:",	160, 70, 140, 19, bone->size, -lim, lim, 100, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "SizeY:",	160, 50, 140, 19, bone->size+1, -lim, lim, 100, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL2, "SizeZ:",	160, 30, 140, 19, bone->size+2, -lim, lim, 100, 3, "");
	uiBlockEndAlign(block);
}

static void v3d_editarmature_buts(uiBlock *block, Object *ob, float lim)
{
	EditBone *ebone;
	
	ebone= G.edbo.first;

	for (ebone = G.edbo.first; ebone; ebone=ebone->next){
		if (ebone->flag & BONE_SELECTED)
			break;
	}

	if (!ebone)
		return;
	uiBlockBeginAlign(block);
	uiDefButF(block, NUM, B_ARMATUREPANEL1, "RootX:",	10, 70, 140, 19, ebone->head, -lim, lim, 100, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL1, "RootY:",	10, 50, 140, 19, ebone->head+1, -lim, lim, 100, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL1, "RootZ:",	10, 30, 140, 19, ebone->head+2, -lim, lim, 100, 3, "");
	uiBlockBeginAlign(block);
	uiDefButF(block, NUM, B_ARMATUREPANEL1, "TipX:",	160, 70, 140, 19, ebone->tail, -lim, lim, 100, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL1, "TipY:",	160, 50, 140, 19, ebone->tail+1, -lim, lim, 100, 3, "");
	uiDefButF(block, NUM, B_ARMATUREPANEL1, "TipZ:",	160, 30, 140, 19, ebone->tail+2, -lim, lim, 100, 3, "");
	uiBlockEndAlign(block);
	ob_eul[0]= 180.0*ebone->roll/M_PI;
	uiDefButF(block, NUM, B_ARMATUREPANEL1, "Roll:",	10, 100, 140, 19, ob_eul, -lim, lim, 100, 3, "");

}

static void v3d_editmetaball_buts(uiBlock *block, Object *ob, float lim)
{
	extern MetaElem *lastelem;
																															
	if(lastelem) {
		uiBlockBeginAlign(block);
		uiDefButF(block, NUM, B_RECALCMBALL, "LocX:", 10, 70, 140, 19, &lastelem->x, -lim, lim, 100, 3, "");
		uiDefButF(block, NUM, B_RECALCMBALL, "LocY:", 10, 50, 140, 19, &lastelem->y, -lim, lim, 100, 3, "");
		uiDefButF(block, NUM, B_RECALCMBALL, "LocZ:", 10, 30, 140, 19, &lastelem->z, -lim, lim, 100, 3, "");
																																
		uiBlockBeginAlign(block);
		if(lastelem->type!=MB_BALL)
		uiDefButF(block, NUM, B_RECALCMBALL, "dx:", 160, 70, 140, 19, &lastelem->expx, 0, lim, 100, 3, "");
		if((lastelem->type!=MB_BALL) && (lastelem->type!=MB_TUBE))
		uiDefButF(block, NUM, B_RECALCMBALL, "dy:", 160, 50, 140, 19, &lastelem->expy, 0, lim, 100, 3, "");
		if((lastelem->type==MB_ELIPSOID) || (lastelem->type==MB_CUBE))
		uiDefButF(block, NUM, B_RECALCMBALL, "dz:", 160, 30, 140, 19, &lastelem->expz, 0, lim, 100, 3, "");
																																
		uiBlockEndAlign(block);
																																
		uiDefButF(block, NUM, B_RECALCMBALL, "Stiffness:", 10, 100, 140, 19, &lastelem->s, 0, lim, 100, 3, "");
	}
}

void do_viewbuts(unsigned short event)
{
	View3D *vd;
	Object *ob= OBACT;
	char *name;
	
	vd= G.vd;
	if(vd==0) return;

	switch(event) {
	case B_LOADBGPIC:
		if(vd->bgpic && vd->bgpic->ima) name= vd->bgpic->ima->name;
		else name= G.ima;
		
		if(G.qual==LR_CTRLKEY)
			activate_imageselect(FILE_SPECIAL, "Select Image", name, load_bgpic_image);
		else
			activate_fileselect(FILE_SPECIAL, "Select Image", name, load_bgpic_image);
		break;
		
	case B_BLENDBGPIC:
		if(vd->bgpic && vd->bgpic->rect) setalpha_bgpic(vd->bgpic);
		addqueue(curarea->win, REDRAW, 1);
		break;
		
	case B_BGPICBROWSE:
		if(vd->bgpic) {
			if (vd->menunr==-2) {
				activate_databrowse((ID*) vd->bgpic->ima, ID_IM, 0, B_BGPICBROWSE, &vd->menunr, do_viewbuts);
			} else if (vd->menunr>0) {
				Image *newima= (Image*) BLI_findlink(&G.main->image, vd->menunr-1);

				if (newima)
					view3d_change_bgpic_ima(vd, newima);
			}
		}
		break;
		
	case B_BGPICCLEAR:
		if (vd->bgpic)
			view3d_change_bgpic_ima(vd, NULL);
		break;
		
	case B_BGPICTEX:
		if (vd->bgpic) {
			if (vd->texnr==-2) {
				activate_databrowse((ID*) vd->bgpic->tex, ID_TE, 0, B_BGPICTEX, &vd->texnr, do_viewbuts);
			} else if (vd->texnr>0) {
				Tex *newtex= (Tex*) BLI_findlink(&G.main->tex, vd->texnr-1);
				
				if (newtex)
					view3d_change_bgpic_tex(vd, newtex);
			}
		}
		break;
		
	case B_BGPICTEXCLEAR:
		if (vd->bgpic)
			view3d_change_bgpic_tex(vd, NULL);
		break;
		
	case B_OBJECTPANELROT:
		if(ob) {
			ob->rot[0]= M_PI*ob_eul[0]/180.0;
			ob->rot[1]= M_PI*ob_eul[1]/180.0;
			ob->rot[2]= M_PI*ob_eul[2]/180.0;
			allqueue(REDRAWVIEW3D, 1);
		}
		break;
	
	case B_OBJECTPANELMEDIAN:
		if(ob) {
			v3d_editvertex_buts(NULL, ob, 1.0);
			makeDispList(ob);
			allqueue(REDRAWVIEW3D, 1);
		}
		break;
	case B_OBJECTPANELPARENT:
		if(ob) {
			if( test_parent_loop(ob->parent, ob) ) 
				ob->parent= NULL;
			allqueue(REDRAWVIEW3D, 1);
		}
		break;
		
	case B_ARMATUREPANEL1:
		{
			EditBone *ebone, *child;
			
			ebone= G.edbo.first;
			for (ebone = G.edbo.first; ebone; ebone=ebone->next){
				if (ebone->flag & BONE_SELECTED) break;
			}
			if (ebone) {
				ebone->roll= M_PI*ob_eul[0]/180.0;
				//	Update our parent
				if (ebone->parent && ebone->flag & BONE_IK_TOPARENT){
					VECCOPY (ebone->parent->tail, ebone->head);
				}
			
				//	Update our children if necessary
				for (child = G.edbo.first; child; child=child->next){
					if (child->parent == ebone && child->flag & BONE_IK_TOPARENT){
						VECCOPY (child->head, ebone->tail);
					}
				}
				allqueue(REDRAWVIEW3D, 1);
			}
		}
		break;
	case B_ARMATUREPANEL2:
		{
			bPoseChannel *chan;
			bArmature *arm;
			Bone *bone;

			arm = get_armature(OBACT);
			if (!arm) return;
			bone = get_first_selected_bone();
		
			if (!bone) return;

			/* This is similar to code in special_trans_update */
	
			if (!G.obpose->pose) G.obpose->pose= MEM_callocN(sizeof(bPose), "pose");
			chan = MEM_callocN (sizeof (bPoseChannel), "transPoseChannel");
		
			chan->flag |= POSE_LOC|POSE_ROT|POSE_SIZE;
			memcpy (chan->loc, bone->loc, sizeof (chan->loc));
			memcpy (chan->quat, bone->quat, sizeof (chan->quat));
			memcpy (chan->size, bone->size, sizeof (chan->size));
			strcpy (chan->name, bone->name);
			
			set_pose_channel (G.obpose->pose, chan);

			rebuild_all_armature_displists();

			allqueue(REDRAWVIEW3D, 1);
		}
	}
}


static void view3d_panel_object(short cntrl)	// VIEW3D_HANDLER_OBJECT
{
	uiBlock *block;
	uiBut *bt;
	Object *ob= OBACT;
	float lim;
	
	if(ob==NULL) return;

	block= uiNewBlock(&curarea->uiblocks, "view3d_panel_object", UI_EMBOSS, UI_HELV, curarea->win);
	uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
	uiSetPanelHandler(VIEW3D_HANDLER_OBJECT);  // for close and esc
	
/* (ton) can't use the rename trick for paint... panel names and settings are stored in the files and
   used to find previous locations when re-open. This causes flipping */

	if(uiNewPanel(curarea, block, "Transform Properties", "View3d", 10, 230, 318, 204)==0) return;
	
	if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT)) {
		uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER);	// force old style frontbuffer draw
	}
	else {
		bt= uiDefBut(block, TEX, B_IDNAME, "OB: ",	10,180,140,20, ob->id.name+2, 0.0, 19.0, 0, 0, "");
		uiButSetFunc(bt, test_idbutton_cb, ob->id.name, NULL);

		uiDefIDPoinBut(block, test_obpoin_but, B_OBJECTPANELPARENT, "Par:", 160, 180, 140, 20, &ob->parent, "Parent Object"); 
	}

	lim= 1000.0*MAX2(1.0, G.vd->grid);

	if(ob==G.obedit) {
		if(ob->type==OB_ARMATURE) v3d_editarmature_buts(block, ob, lim);
		if(ob->type==OB_MBALL) v3d_editmetaball_buts(block, ob, lim);
		else v3d_editvertex_buts(block, ob, lim);
	}
	else if(ob==G.obpose) {
		v3d_posearmature_buts(block, ob, lim);
	}
	else if(G.f & (G_VERTEXPAINT|G_TEXTUREPAINT)) {
		extern VPaint Gvp;         /* from vpaint */
		static float hsv[3], old[3];	// used as temp mem for picker
		uiBlockPickerButtons(block, &Gvp.r, hsv, old, 'f', REDRAWBUTSEDIT);	/* 'f' is for floating panel */
	}
	else {
		uiBlockBeginAlign(block);
		uiDefButF(block, NUM, REDRAWVIEW3D, "LocX:",		10, 140, 140, 19, &(ob->loc[0]), -lim, lim, 100, 3, "");
		uiDefButF(block, NUM, REDRAWVIEW3D, "LocY:",		10, 120, 140, 19, &(ob->loc[1]), -lim, lim, 100, 3, "");
		uiDefButF(block, NUM, REDRAWVIEW3D, "LocZ:",		10, 100, 140, 19, &(ob->loc[2]), -lim, lim, 100, 3, "");
	
		ob_eul[0]= 180.0*ob->rot[0]/M_PI;
		ob_eul[1]= 180.0*ob->rot[1]/M_PI;
		ob_eul[2]= 180.0*ob->rot[2]/M_PI;
		
		uiBlockBeginAlign(block);
		uiDefButF(block, NUM, B_OBJECTPANELROT, "RotX:",	10, 70, 140, 19, &(ob_eul[0]), -lim, lim, 1000, 3, "");
		uiDefButF(block, NUM, B_OBJECTPANELROT, "RotY:",	10, 50, 140, 19, &(ob_eul[1]), -lim, lim, 1000, 3, "");
		uiDefButF(block, NUM, B_OBJECTPANELROT, "RotZ:",	10, 30, 140, 19, &(ob_eul[2]), -lim, lim, 1000, 3, "");
		uiBlockBeginAlign(block);
		uiDefButF(block, NUM, REDRAWVIEW3D, "SizeX:",		160, 70, 140, 19, &(ob->size[0]), -lim, lim, 100, 3, "");
		uiDefButF(block, NUM, REDRAWVIEW3D, "SizeY:",		160, 50, 140, 19, &(ob->size[1]), -lim, lim, 100, 3, "");
		uiDefButF(block, NUM, REDRAWVIEW3D, "SizeZ:",		160, 30, 140, 19, &(ob->size[2]), -lim, lim, 100, 3, "");
		uiBlockEndAlign(block);
	}
}

static void view3d_panel_background(short cntrl)	// VIEW3D_HANDLER_BACKGROUND
{
	uiBlock *block;
	View3D *vd;
	ID *id;
	char *strp;
	
	vd= G.vd;

	block= uiNewBlock(&curarea->uiblocks, "view3d_panel_background", UI_EMBOSS, UI_HELV, curarea->win);
	uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE  | cntrl);
	uiSetPanelHandler(VIEW3D_HANDLER_BACKGROUND);  // for close and esc
	if(uiNewPanel(curarea, block, "Background Image", "View3d", 340, 10, 318, 204)==0) return;

	if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT)) {
		uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER);	// force old style frontbuffer draw
	}
	
	if(vd->flag & V3D_DISPBGPIC) {
		if(vd->bgpic==0) {
			vd->bgpic= MEM_callocN(sizeof(BGpic), "bgpic");
			vd->bgpic->size= 5.0;
			vd->bgpic->blend= 0.5;
		}
	}
	
	uiDefButBitS(block, TOG, V3D_DISPBGPIC, REDRAWVIEW3D, "Use Background Image", 0, 162, 200, 20, &vd->flag, 0, 0, 0, 0, "Display an image in the background of the 3D View");
	
	uiDefBut(block, LABEL, 1, " ",	206, 162, 84, 20, NULL, 0.0, 0.0, 0, 0, "");
	
	
	if(vd->flag & V3D_DISPBGPIC) {

		/* Background Image */
		uiDefBut(block, LABEL, 1, "Image:",	0, 128, 76, 19, NULL, 0.0, 0.0, 0, 0, "");
		
		uiBlockBeginAlign(block);
		uiDefIconBut(block, BUT, B_LOADBGPIC, ICON_FILESEL,	90, 128, 20, 20, 0, 0, 0, 0, 0, "Open a new background image");

		id= (ID *)vd->bgpic->ima;
		IDnames_to_pupstring(&strp, NULL, NULL, &(G.main->image), id, &(vd->menunr));
		if(strp[0]) {
		
			uiDefButS(block, MENU, B_BGPICBROWSE, strp, 	110, 128, 20, 20, &(vd->menunr), 0, 0, 0, 0, "Select a background image");
		
			if(vd->bgpic->ima)  {
				uiDefBut(block, TEX,	    0,"BG: ",		130, 128, 140, 20, &vd->bgpic->ima->name,0.0,100.0, 0, 0, "The currently selected background image");
				uiDefIconBut(block, BUT, B_BGPICCLEAR, ICON_X, 270, 128, 20, 20, 0, 0, 0, 0, 0, "Remove background image link");
			}
			uiBlockEndAlign(block);
		} else {
			uiBlockEndAlign(block);
		}
		MEM_freeN(strp);


		/* Background texture */
		uiDefBut(block, LABEL, 1, "Texture:",	0, 100, 76, 19, NULL, 0.0, 0.0, 0, 0, "");
		
		id= (ID *)vd->bgpic->tex;
		IDnames_to_pupstring(&strp, NULL, NULL, &(G.main->tex), id, &(vd->texnr));
		if (strp[0])
			uiBlockBeginAlign(block);
			uiDefButS(block, MENU, B_BGPICTEX, strp,			90, 100, 20,20, &(vd->texnr), 0, 0, 0, 0, "Select a texture to use as an animated background image");
		MEM_freeN(strp);
		
		if (id) {
			uiDefBut(block, TEX, B_IDNAME, "TE:",				110, 100, 160, 20, id->name+2, 0.0, 18.0, 0, 0, "");
			uiDefIconBut(block, BUT, B_BGPICTEXCLEAR, ICON_X, 	270, 100, 20, 20, 0, 0, 0, 0, 0, "Remove background texture link");
			uiBlockEndAlign(block);
		} else {
			uiBlockEndAlign(block);
		}

		uiDefButF(block, NUMSLI, B_BLENDBGPIC, "Blend:",	0, 60 , 290, 19, &vd->bgpic->blend, 0.0,1.0, 0, 0, "Set the transparency of the background image");

		uiDefButF(block, NUM, REDRAWVIEW3D, "Size:",		0, 28, 140, 19, &vd->bgpic->size, 0.1, 250.0, 100, 0, "Set the size (width) of the background image");

		uiDefButF(block, NUM, REDRAWVIEW3D, "X Offset:",	0, 6, 140, 19, &vd->bgpic->xof, -20.0,20.0, 10, 2, "Set the horizontal offset of the background image");
		uiDefButF(block, NUM, REDRAWVIEW3D, "Y Offset:",	150, 6, 140, 19, &vd->bgpic->yof, -20.0,20.0, 10, 2, "Set the vertical offset of the background image");

	
		
		// uiDefButF(block, NUM, REDRAWVIEW3D, "Size:", 		160,160,150,20, &vd->bgpic->size, 0.1, 250.0, 100, 0, "Set the size for the width of the BackGroundPic");
		


//		uiDefButF(block, NUMSLI, B_BLENDBGPIC, "Blend:",	120,100,190,20,&vd->bgpic->blend, 0.0,1.0, 0, 0, "Set the BackGroundPic transparency");
		
//		uiDefButF(block, NUM, B_DIFF, "Center X: ",	10,70,140,20,&vd->bgpic->xof, -20.0,20.0, 10, 2, "Set the BackGroundPic X Offset");
//		uiDefButF(block, NUM, B_DIFF, "Center Y: ",	160,70,140,20,&vd->bgpic->yof, -20.0,20.0, 10, 2, "Set the BackGroundPic Y Offset");

	}
}


static void view3d_panel_properties(short cntrl)	// VIEW3D_HANDLER_SETTINGS
{
	uiBlock *block;
	View3D *vd;
	float *curs;
	
	vd= G.vd;

	block= uiNewBlock(&curarea->uiblocks, "view3d_panel_properties", UI_EMBOSS, UI_HELV, curarea->win);
	uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE  | cntrl);
	uiSetPanelHandler(VIEW3D_HANDLER_PROPERTIES);  // for close and esc
	if(uiNewPanel(curarea, block, "View Properties", "View3d", 340, 10, 318, 204)==0) return;

	if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT)) {
		uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER);	// force old style frontbuffer draw
	}

	uiDefBut(block, LABEL, 1, "Grid:",					10, 180, 150, 19, NULL, 0.0, 0.0, 0, 0, "");
	uiDefButF(block, NUM, REDRAWVIEW3D, "Spacing:",		10, 160, 140, 19, &vd->grid, 0.001, 100.0, 10, 0, "Set the distance between grid lines");
	uiDefButS(block, NUM, REDRAWVIEW3D, "Lines:",		10, 136, 140, 19, &vd->gridlines, 0.0, 100.0, 100, 0, "Set the number of grid lines");

	uiDefBut(block, LABEL, 1, "3D Display:",					160, 180, 150, 19, NULL, 0.0, 0.0, 0, 0, "");
	uiDefButBitS(block, TOG, V3D_SHOW_FLOOR, REDRAWVIEW3D, "Grid Floor",160, 160, 150, 19, &vd->gridflag, 0, 0, 0, 0, "Show the grid floor in free camera mode");
	uiDefButBitS(block, TOG, V3D_SHOW_X, REDRAWVIEW3D, "X Axis",		160, 136, 48, 19, &vd->gridflag, 0, 0, 0, 0, "Show the X Axis line");
	uiDefButBitS(block, TOG, V3D_SHOW_Y, REDRAWVIEW3D, "Y Axis",		212, 136, 48, 19, &vd->gridflag, 0, 0, 0, 0, "Show the Y Axis line");
	uiDefButBitS(block, TOG, V3D_SHOW_Z, REDRAWVIEW3D, "Z Axis",		262, 136, 48, 19, &vd->gridflag, 0, 0, 0, 0, "Show the Z Axis line");

	uiDefBut(block, LABEL, 1, "View Camera:",			10, 110, 140, 19, NULL, 0.0, 0.0, 0, 0, "");
	
	uiDefButF(block, NUM, REDRAWVIEW3D, "Lens:",		10, 90, 140, 19, &vd->lens, 10.0, 120.0, 100, 0, "The lens angle in perspective view");
	uiBlockBeginAlign(block);
	uiDefButF(block, NUM, REDRAWVIEW3D, "Clip Start:",	10, 66, 140, 19, &vd->near, vd->grid/10.0, 100.0, 10, 0, "Set the beginning of the range in which 3D objects are displayed (perspective view)");
	uiDefButF(block, NUM, REDRAWVIEW3D, "Clip End:",	10, 46, 140, 19, &vd->far, 1.0, 1000.0*vd->grid, 100, 0, "Set the end of the range in which 3D objects are displayed (perspective view)");
	uiBlockEndAlign(block);

	uiDefBut(block, LABEL, 1, "3D Cursor:",				160, 110, 140, 19, NULL, 0.0, 0.0, 0, 0, "");

	uiBlockBeginAlign(block);
	curs= give_cursor();
	uiDefButF(block, NUM, REDRAWVIEW3D, "X:",			160, 90, 150, 22, curs, -1000.0*vd->grid, 1000.0*vd->grid, 10, 0, "X co-ordinate of the 3D cursor");
	uiDefButF(block, NUM, REDRAWVIEW3D, "Y:",			160, 68, 150, 22, curs+1, -1000.0*vd->grid, 1000.0*vd->grid, 10, 0, "Y co-ordinate of the 3D cursor");
	uiDefButF(block, NUM, REDRAWVIEW3D, "Z:",			160, 46, 150, 22, curs+2, -1000.0*vd->grid, 1000.0*vd->grid, 10, 0, "Z co-ordinate of the 3D cursor");
	uiBlockEndAlign(block);

	uiDefButBitS(block, TOG, V3D_SELECT_OUTLINE, REDRAWVIEW3D, "Outline Selected Objects", 10, 10, 300, 19, &vd->flag, 0, 0, 0, 0, "Highlight selected objects with an outline, in Solid, Shaded or Textured viewport shading modes");

}



static void view3d_blockhandlers(ScrArea *sa)
{
	View3D *v3d= sa->spacedata.first;
	short a;
	
	/* warning; blocks need to be freed each time, handlers dont remove */
	uiFreeBlocksWin(&sa->uiblocks, sa->win);

	for(a=0; a<SPACE_MAXHANDLER; a+=2) {
	
		switch(v3d->blockhandler[a]) {

		case VIEW3D_HANDLER_PROPERTIES:
			view3d_panel_properties(v3d->blockhandler[a+1]);
			break;
		case VIEW3D_HANDLER_BACKGROUND:
			view3d_panel_background(v3d->blockhandler[a+1]);
			break;
		case VIEW3D_HANDLER_OBJECT:
			view3d_panel_object(v3d->blockhandler[a+1]);
		
			break;
		
		}
		/* clear action value for event */
		v3d->blockhandler[a+1]= 0;
	}
	uiDrawBlocksPanels(sa, 0);

}

void drawview3dspace(ScrArea *sa, void *spacedata)
{
	extern void constline_callback(void);	// editobject.c helpline
	Base *base;
	Object *ob;
	
	setwinmatrixview3d(0);	/* 0= no pick rect */
	setviewmatrixview3d();

	Mat4MulMat4(G.vd->persmat, G.vd->viewmat, curarea->winmat);
	Mat4Invert(G.vd->persinv, G.vd->persmat);
	Mat4Invert(G.vd->viewinv, G.vd->viewmat);

	if(G.vd->drawtype > OB_WIRE) {
		G.zbuf= TRUE;
		glEnable(GL_DEPTH_TEST);
		if(G.f & G_SIMULATION) {
			glClearColor(0.0, 0.0, 0.0, 0.0); 
		}
		else {
			float col[3];
			BIF_GetThemeColor3fv(TH_BACK, col);
			glClearColor(col[0], col[1], col[2], 0.0); 
		}
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		
		glLoadIdentity();
	}
	else {
		float col[3];
		BIF_GetThemeColor3fv(TH_BACK, col);
		glClearColor(col[0], col[1], col[2], 0.0);
		glClear(GL_COLOR_BUFFER_BIT);
	}
	
	myloadmatrix(G.vd->viewmat);
	persp(PERSP_STORE);  // store correct view for persp(PERSP_VIEW) calls

	if(G.vd->view==0 || G.vd->persp!=0) {
		drawfloor();
		if(G.vd->persp==2) {
			if(G.scene->world) {
				if(G.scene->world->mode & WO_STARS) RE_make_stars(star_stuff_init_func,
																  star_stuff_vertex_func,
																  star_stuff_term_func);
			}
			if(G.vd->flag & V3D_DISPBGPIC) draw_bgpic();
		}
	}
	else {
		drawgrid();

		if(G.vd->flag & V3D_DISPBGPIC) {
			draw_bgpic();
		}
	}
	
#if 0
	/* Lets be a little more selective about when and where we do this,
	 * or else armatures/poses/displists get recalculated all of the
	 * time
	 */
	clear_all_constraints();
#endif

	/* draw set first */
	if(G.scene->set) {
	
		/* patch: color remains constant */ 
		G.f |= G_PICKSEL;

		base= G.scene->set->base.first;
		while(base) {
			if(G.vd->lay & base->lay) {
				where_is_object(base->object);

				cpack(0x404040);
				draw_object(base);

				if(base->object->transflag & OB_DUPLI) {
					extern ListBase duplilist;
					Base tbase;
					
					tbase= *base;
					
					tbase.flag= OB_FROMDUPLI;
					make_duplilist(G.scene->set, base->object);
					ob= duplilist.first;
					while(ob) {
						tbase.object= ob;
						draw_object(&tbase);
						ob= ob->id.next;
					}
					free_duplilist();
					
				}
			}
			base= base->next;
		}
		
		G.f &= ~G_PICKSEL;
	}
	
	/* first calculate positions, we do this in separate loop to make sure displists
	   (mball, deform, etc) are recaluclated based on correct object (parent/children) positions
	*/
	base= G.scene->base.first;
	while(base) {
		if(G.vd->lay & base->lay) where_is_object(base->object);
		base= base->next;
	}
	
	/* then draw not selected and the duplis */
	base= G.scene->base.first;
	while(base) {
		
		if(G.vd->lay & base->lay) {
			
			/* dupli drawing temporal off here */
			if(FALSE && base->object->transflag & OB_DUPLI) {
				extern ListBase duplilist;
				Base tbase;

				/* draw original always first because of make_displist */
				draw_object(base);

				/* patch: color remains constant */ 
				G.f |= G_PICKSEL;
				cpack(0x404040);
				
				tbase.flag= OB_FROMDUPLI;
				make_duplilist(G.scene, base->object);

				ob= duplilist.first;
				while(ob) {
					tbase.object= ob;
					draw_object(&tbase);
					ob= ob->id.next;
				}
				free_duplilist();
				
				G.f &= ~G_PICKSEL;				
			}
			else if((base->flag & SELECT)==0) {
				draw_object(base);
			}
			
		}
		
		base= base->next;
	}
	/* draw selected */
	base= G.scene->base.first;
	while(base) {
		
		if ( ((base)->flag & SELECT) && ((base)->lay & G.vd->lay) ) {
			draw_object(base);
		}
		
		base= base->next;
	}

	if(G.moving) constline_callback();

	/* duplis, draw as last to make sure the displists are ok */
	base= G.scene->base.first;
	while(base) {
		
		if(G.vd->lay & base->lay) {
			if(base->object->transflag & OB_DUPLI) {
				extern ListBase duplilist;
				Base tbase;

				/* patch: color remains constant */ 
				G.f |= G_PICKSEL;
				cpack(0x404040);
				
				tbase.flag= OB_FROMDUPLI;
				make_duplilist(G.scene, base->object);

				ob= duplilist.first;
				while(ob) {
					tbase.object= ob;
					draw_object(&tbase);
					ob= ob->id.next;
				}
				free_duplilist();
				
				G.f &= ~G_PICKSEL;				
			}
		}
		base= base->next;
	}


	if(G.scene->radio) RAD_drawall(G.vd->drawtype>=OB_SOLID);
	
	if(G.zbuf) {
		G.zbuf= FALSE;
		glDisable(GL_DEPTH_TEST);
	}

	persp(PERSP_WIN);  // set ortho
	
	if(G.vd->persp>1) drawviewborder();
	drawcursor();
	draw_view_icon();

	ob= OBACT;
	if(ob!=0 && (U.uiflag & USER_DRAWVIEWINFO)) draw_selected_name(ob->id.name+2);
	
	draw_area_emboss(sa);
	
	/* it is important to end a view in a transform compatible with buttons */

	bwin_scalematrix(sa->win, G.vd->blockscale, G.vd->blockscale, G.vd->blockscale);
	view3d_blockhandlers(sa);

	curarea->win_swap= WIN_BACK_OK;
	
	if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT)) {
		G.vd->flag |= V3D_NEEDBACKBUFDRAW;
		addafterqueue(curarea->win, BACKBUFDRAW, 1);
	}
	// test for backbuf select
	if(G.obedit && G.vd->drawtype>OB_WIRE && (G.vd->flag & V3D_ZBUF_SELECT)) {
		extern int afterqtest(short win, unsigned short evt);	//editscreen.c

		G.vd->flag |= V3D_NEEDBACKBUFDRAW;
		if(afterqtest(curarea->win, BACKBUFDRAW)==0) {
			addafterqueue(curarea->win, BACKBUFDRAW, 1);
		}
	}


	/* scene redraw script link */
	if(G.scene->scriptlink.totscript && !during_script()) {
		BPY_do_pyscript((ID *)G.scene, SCRIPT_REDRAW);
	}

}


	/* Called back by rendering system, icky
	 */
void drawview3d_render(struct View3D *v3d)
{
	extern short v3d_windowmode;
	Base *base;
	Object *ob;

	free_all_realtime_images();

	v3d_windowmode= 1;
	setwinmatrixview3d(0);
	v3d_windowmode= 0;
	glMatrixMode(GL_PROJECTION);
	myloadmatrix(R.winmat);
	glMatrixMode(GL_MODELVIEW);
	
	setviewmatrixview3d();
	myloadmatrix(v3d->viewmat);
	Mat4MulMat4(v3d->persmat, v3d->viewmat, R.winmat);
	Mat4Invert(v3d->persinv, v3d->persmat);
	Mat4Invert(v3d->viewinv, v3d->viewmat);

	if(v3d->drawtype > OB_WIRE) {
		G.zbuf= TRUE;
		glEnable(GL_DEPTH_TEST);
	}

	if (v3d->drawtype==OB_TEXTURE && G.scene->world) {
		glClearColor(G.scene->world->horr, G.scene->world->horg, G.scene->world->horb, 0.0); 
	} else {
		float col[3];
		BIF_GetThemeColor3fv(TH_BACK, col);
		glClearColor(col[0], col[1], col[2], 0.0); 
	}
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	glLoadIdentity();
	myloadmatrix(v3d->viewmat);
	
	/* abuse! to make sure it doesnt draw the helpstuff */
	G.f |= G_SIMULATION;

	clear_all_constraints();
	do_all_ipos();
	BPY_do_all_scripts(SCRIPT_FRAMECHANGED);
	do_all_keys();
	do_all_actions();
	do_all_ikas();

	test_all_displists();

	/* not really nice forcing of calc_ipo and where_is */
	ob= G.main->object.first;
	while(ob) {
		ob->ctime= -123.456;
		ob= ob->id.next;
	}

	/* first draw set */
	if(G.scene->set) {
	
		/* patch: color remains constant */ 
		G.f |= G_PICKSEL;
		
		base= G.scene->set->base.first;
		while(base) {
			if(v3d->lay & base->lay) {
				if ELEM3(base->object->type, OB_LAMP, OB_CAMERA, OB_LATTICE);
				else {
					where_is_object(base->object);
	
					cpack(0x404040);
					draw_object(base);
	
					if(base->object->transflag & OB_DUPLI) {
						extern ListBase duplilist;
						Base tbase;
						
						tbase.flag= OB_FROMDUPLI;
						make_duplilist(G.scene->set, base->object);
						ob= duplilist.first;
						while(ob) {
							tbase.object= ob;
							draw_object(&tbase);
							ob= ob->id.next;
						}
						free_duplilist();
					}
				}
			}
			base= base->next;
		}
		
		G.f &= ~G_PICKSEL;
	}

	clear_all_constraints();

	/* first not selected and duplis */
	base= G.scene->base.first;
	while(base) {
		
		if(v3d->lay & base->lay) {
			if ELEM3(base->object->type, OB_LAMP, OB_CAMERA, OB_LATTICE);
			else {
				where_is_object(base->object);
	
				if(base->object->transflag & OB_DUPLI) {
					extern ListBase duplilist;
					Base tbase;
					
					/* always draw original first because of make_displist */
					draw_object(base);
					
					/* patch: color remains constant */ 
					G.f |= G_PICKSEL;
					cpack(0x404040);
					
					tbase.flag= OB_FROMDUPLI;
					make_duplilist(G.scene, base->object);
					ob= duplilist.first;
					while(ob) {
						tbase.object= ob;
						draw_object(&tbase);
						ob= ob->id.next;
					}
					free_duplilist();
					
					G.f &= ~G_PICKSEL;				
				}
				else if((base->flag & SELECT)==0) {
					draw_object(base);
				}
			}
		}
		
		base= base->next;
	}

	/* draw selected */
	base= G.scene->base.first;
	while(base) {
		
		if ( ((base)->flag & SELECT) && ((base)->lay & v3d->lay) ) {
			if ELEM3(base->object->type, OB_LAMP, OB_CAMERA, OB_LATTICE);
			else draw_object(base);
		}
		
		base= base->next;
	}

	if(G.scene->radio) RAD_drawall(G.vd->drawtype>=OB_SOLID);

	if(G.zbuf) {
		G.zbuf= FALSE;
		glDisable(GL_DEPTH_TEST);
	}
	
	G.f &= ~G_SIMULATION;

	glFlush();

	glReadPixels(0, 0, R.rectx, R.recty, GL_RGBA, GL_UNSIGNED_BYTE, R.rectot);
	glLoadIdentity();

	free_all_realtime_images();
}


double tottime = 0.0;

int update_time(void)
{
	static double ltime;
	double time;

	if ((U.mixbufsize)&&(audiostream_pos() != CFRA)&&(G.scene->audio.flag & AUDIO_SYNC)) return 0;

	time = PIL_check_seconds_timer();
	
	tottime += (time - ltime);
	ltime = time;
	return (tottime < 0.0);
}

double speed_to_swaptime(int speed)
{
	switch(speed) {
	case 1:
		return 1.0/60.0;
	case 2:
		return 1.0/50.0;
	case 3:
		return 1.0/30.0;
	case 4:
		return 1.0/25.0;
	case 5:
		return 1.0/20.0;
	case 6:
		return 1.0/15.0;
	case 7:
		return 1.0/12.5;
	case 8:
		return 1.0/10.0;
	case 9:
		return 1.0/6.0;
	}
	return 1.0/4.0;
}

double key_to_swaptime(int key)
{
	switch(key) {
	case PAD1:
		G.animspeed= 1;
		tottime= 0;
		return speed_to_swaptime(1);
	case PAD2:
		G.animspeed= 2;
		tottime= 0;
		return speed_to_swaptime(2);
	case PAD3:
		G.animspeed= 3;
		tottime= 0;
		return speed_to_swaptime(3);
	case PAD4:
		G.animspeed= 4;
		tottime= 0;
		return speed_to_swaptime(4);
	case PAD5:
		G.animspeed= 5;
		tottime= 0;
		return speed_to_swaptime(5);
	case PAD6:
		G.animspeed= 6;
		tottime= 0;
		return speed_to_swaptime(6);
	case PAD7:
		G.animspeed= 7;
		tottime= 0;
		return speed_to_swaptime(7);
	case PAD8:
		G.animspeed= 8;
		tottime= 0;
		return speed_to_swaptime(8);
	case PAD9:
		G.animspeed= 9;
		tottime= 0;
		return speed_to_swaptime(9);
	}
	
	return speed_to_swaptime(G.animspeed);
}

#ifdef NAN_LINEAR_PHYSICS

void sumo_callback(void *obp)
{
	Object *ob= obp;
	SM_Vector3 vec;
	float matf[3][3];
	int i, j;

    SM_GetMatrixf(ob->sumohandle, ob->obmat[0]);

	VECCOPY(ob->loc, ob->obmat[3]);
	
    for (i = 0; i < 3; ++i) {
        for (j = 0; j < 3; ++j) {
            matf[i][j] = ob->obmat[i][j];
        }
    }
    Mat3ToEul(matf, ob->rot);
}

/* for test and fun, i've written the next functions to play with dynamics
   using a variant of play-anim... was never released nor really tested (ton) */

void init_anim_sumo(void)
{
	extern Material defmaterial;
	Base *base;
    Mesh *me;
	Object *ob;
    Material *mat;
	MFace *mface;
	MVert *mvert;
    float centre[3], size[3];
	int a;
    SM_ShapeHandle shape;
	SM_SceneHandle scene;
    SM_Material material;
    SM_MassProps massprops;
    SM_Vector3 vec;
    SM_Vector3 scaling;
	
	scene= SM_CreateScene();
	G.scene->sumohandle = scene;
	
	vec[0]=  0.0;
	vec[1]=  0.0;
	vec[2]= -9.8;
	SM_SetForceField(scene, vec);
	
    /* ton: cylinders & cones are still Y-axis up, will be Z-axis later */
    /* ton: write location/rotation save and restore */
	
	base= FIRSTBASE;
	while (base) {
		if (G.vd->lay & base->lay) {
            ob= base->object;
			 
            /* define shape, for now only meshes take part in physics */
            get_local_bounds(ob, centre, size);
            
            if (ob->type==OB_MESH) {
                me= ob->data;
                
                if (ob->gameflag & OB_DYNAMIC) {
                    if (me->sumohandle)
                        shape= me->sumohandle;
                    else {
                        /* make new handle */
                        switch(ob->boundtype) {
                        case OB_BOUND_BOX:
                            shape= SM_Box(2.0*size[0], 2.0*size[1], 2.0*size[2]);
                            break;
                        case OB_BOUND_SPHERE:
                            shape= SM_Sphere(size[0]);
                            break;
                        case OB_BOUND_CYLINDER:
                            shape= SM_Cylinder(size[0], 2.0*size[2]);
                            break;
                        case OB_BOUND_CONE:
                            shape= SM_Cone(size[0], 2.0*size[2]);
                            break;
                        }
                        
						me->sumohandle= shape;
					}
                    /* sumo material properties */
                	mat= give_current_material(ob, 0);
                	if(mat==NULL)
                        mat= &defmaterial;
                    
                	material.restitution= mat->reflect;
                	material.static_friction= mat->friction;
                	material.dynamic_friction= mat->friction;
                    
                	/* sumo mass properties */
                	massprops.mass= ob->mass;
                	massprops.center[0]= 0.0;
                	massprops.center[1]= 0.0;
                	massprops.center[2]= 0.0;

                	massprops.inertia[0]= 0.5*ob->mass;
                	massprops.inertia[1]= 0.5*ob->mass;
                	massprops.inertia[2]= 0.5*ob->mass;

                	massprops.orientation[0]= 0.0;
                	massprops.orientation[1]= 0.0;
                	massprops.orientation[2]= 0.0;
                	massprops.orientation[3]= 1.0;

                	ob->sumohandle = SM_CreateObject(ob, shape, &material, 
                                                     &massprops, sumo_callback);
					SM_AddObject(scene, ob->sumohandle);
					
                    scaling[0] = ob->size[0];
                    scaling[1] = ob->size[1];
                    scaling[2] = ob->size[2];
					SM_SetMatrixf(ob->sumohandle, ob->obmat[0]);
					SM_SetScaling(ob->sumohandle, scaling);

				}
 				else {
 					if(me->sumohandle) shape= me->sumohandle;
					else {
						/* make new handle */
            			shape= SM_NewComplexShape();
						
						mface= me->mface;
						mvert= me->mvert;
						for(a=0; a<me->totface; a++,mface++) {
							if(mface->v3) {
								SM_Begin();
								SM_Vertex( (mvert+mface->v1)->co[0], (mvert+mface->v1)->co[1], (mvert+mface->v1)->co[2]);
								SM_Vertex( (mvert+mface->v2)->co[0], (mvert+mface->v2)->co[1], (mvert+mface->v2)->co[2]);
								SM_Vertex( (mvert+mface->v3)->co[0], (mvert+mface->v3)->co[1], (mvert+mface->v3)->co[2]);
								if(mface->v4)
									SM_Vertex( (mvert+mface->v4)->co[0], (mvert+mface->v4)->co[1], (mvert+mface->v4)->co[2]);
								SM_End();
							}
						}
						
						SM_EndComplexShape();
						
						me->sumohandle= shape;
					}
                    /* sumo material properties */
                	mat= give_current_material(ob, 0);
                	if(mat==NULL)
                        mat= &defmaterial;
                	material.restitution= mat->reflect;
                	material.static_friction= mat->friction;
                	material.dynamic_friction= mat->friction;

                	/* sumo mass properties */
                	massprops.mass= ob->mass;
                	massprops.center[0]= 0.0;
                	massprops.center[1]= 0.0;
                	massprops.center[2]= 0.0;

                	massprops.inertia[0]= 0.5*ob->mass;
                	massprops.inertia[1]= 0.5*ob->mass;
                	massprops.inertia[2]= 0.5*ob->mass;

                	massprops.orientation[0]= 0.0;
                	massprops.orientation[1]= 0.0;
                	massprops.orientation[2]= 0.0;
                	massprops.orientation[3]= 1.0;

                	ob->sumohandle= SM_CreateObject(ob, shape, &material, NULL, NULL);
					SM_AddObject(scene, ob->sumohandle);

                    scaling[0] = ob->size[0];
                    scaling[1] = ob->size[1];
                    scaling[2] = ob->size[2];
					SM_SetMatrixf(ob->sumohandle, ob->obmat[0]);
					SM_SetScaling(ob->sumohandle, scaling);
				}
            }
        }    	
    	base= base->next;
    }
}

/* update animated objects */
void update_anim_sumo(void)
{
    SM_Vector3 scaling;

	Base *base;
	Object *ob;
	Mesh *me;
	
	base= FIRSTBASE;
	while(base) {
		if(G.vd->lay & base->lay) {
			ob= base->object;
			
			if(ob->sumohandle) {
				if((ob->gameflag & OB_DYNAMIC)==0) {
					/* maybe: optimise, check for anim */
                    scaling[0] = ob->size[0];
                    scaling[1] = ob->size[1];
                    scaling[2] = ob->size[2];
					SM_SetMatrixf(ob->sumohandle, ob->obmat[0]);
					SM_SetScaling(ob->sumohandle, scaling);
				}
			}				
		}
		base= base->next;
	}

}

void end_anim_sumo(void)
{
	Base *base;
	Object *ob;
	Mesh *me;
	
	base= FIRSTBASE;
	while(base) {
		if(G.vd->lay & base->lay) {
			ob= base->object;
			
            if(ob->type==OB_MESH) {
				if(ob->sumohandle) {
					SM_RemoveObject(G.scene->sumohandle, ob->sumohandle);
					SM_DeleteObject(ob->sumohandle);
					ob->sumohandle= NULL;
				}
				me= ob->data;
				if(me->sumohandle) {
					SM_DeleteShape(me->sumohandle);
					me->sumohandle= NULL;
				}
			}
		}
		base= base->next;
	}
	if(G.scene->sumohandle) {
		SM_DeleteScene(G.scene->sumohandle);
		G.scene->sumohandle= NULL;
	}
}

#endif

void inner_play_anim_loop(int init, int mode)
{
	ScrArea *sa;
	static ScrArea *oldsa;
	static double swaptime;
	static int curmode;

	/* init */
	if(init) {
		oldsa= curarea;
		swaptime= speed_to_swaptime(G.animspeed);
		tottime= 0.0;
		curmode= mode;
#ifdef NAN_LINEAR_PHYSICS
        init_anim_sumo();
#endif        
		return;
	}

	set_timecursor(CFRA);

	//clear_all_constraints();
	//do_all_ipos();
	//BPY_do_all_scripts(SCRIPT_FRAMECHANGED);
	//do_all_keys();
	//do_all_actions();
	//do_all_ikas();
	update_for_newframe_muted();

	//test_all_displists();
#ifdef NAN_LINEAR_PHYSICS	
	update_anim_sumo();
	
	SM_Proceed(G.scene->sumohandle, swaptime, 40, NULL);
#endif
	sa= G.curscreen->areabase.first;
	while(sa) {
		if(sa==oldsa) {
			scrarea_do_windraw(sa);
		}
		else if(curmode & 1) { /* catch modes 1 and 3 */
			if ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_SEQ) {
				scrarea_do_windraw(sa);
			}
		}
		
		sa= sa->next;	
	}
	
	/* make sure that swaptime passed by */
	tottime -= swaptime;
	while (update_time()) PIL_sleep_ms(1);

	if(CFRA>=EFRA) {
		if (tottime > 0.0) tottime = 0.0;
		CFRA= SFRA;
		audiostream_stop();
		audiostream_start( CFRA );
	}
	else {
		if (U.mixbufsize && (G.scene->audio.flag & AUDIO_SYNC)) CFRA = audiostream_pos();
		else CFRA++;
	}
}

/* play_anim: 'mode' defines where to play and if repeat is on:
 * - 0: current area
 * - 1: all view3d and seq areas
 * - 2: current area, no replay
 * - 3: all view3d and seq areas, no replay */
int play_anim(int mode)
{
	ScrArea *sa, *oldsa;
	int cfraont;
	unsigned short event=0;
	short val;

	/* patch for very very old scenes */
	if(SFRA==0) SFRA= 1;
	if(EFRA==0) EFRA= 250;

	if(SFRA>EFRA) return 0;
	
	update_time();

	/* waitcursor(1); */
	G.f |= G_PLAYANIM;		/* in sequence.c and view.c this is handled */

	cfraont= CFRA;
	oldsa= curarea;

	audiostream_start( CFRA );
	
	inner_play_anim_loop(1, mode);	/* 1==init */
	
	 /* forces all buffers to be OK for current frame (otherwise other windows get redrawn with CFRA+1) */
	curarea->win_swap= WIN_BACK_OK;
	screen_swapbuffers();
	
	while(TRUE) {

		inner_play_anim_loop(0, 0);
	
		screen_swapbuffers();
		
		while(qtest()) {
		
			event= extern_qread(&val);
			if(event==ESCKEY) break;
			else if(event==MIDDLEMOUSE) {
				if(U.flag & USER_VIEWMOVE) {
					if(G.qual & LR_SHIFTKEY) viewmove(0);
					else if(G.qual & LR_CTRLKEY) viewmove(2);
					else viewmove(1);
				}
				else {
					if(G.qual & LR_SHIFTKEY) viewmove(1);
					else if(G.qual & LR_CTRLKEY) viewmove(2);
					else viewmove(0);
				}
			}
		}
		if(event==ESCKEY || event==SPACEKEY) break;
				
		if((mode > 1) && CFRA==EFRA) break; /* no replay */	
	}

	if(event==SPACEKEY);
	else CFRA= cfraont;

	clear_all_constraints();
	do_all_ipos();
	do_all_keys();
	do_all_actions();
	do_all_ikas();
	test_all_displists();
	
	audiostream_stop();

	if(oldsa!=curarea) areawinset(oldsa->win);
	
	/* restore all areas */
	sa= G.curscreen->areabase.first;
	while(sa) {
		if( (mode && sa->spacetype==SPACE_VIEW3D) || sa==curarea) addqueue(sa->win, REDRAW, 1);
		
		sa= sa->next;	
	}
	
	/* speed button */
	// allqueue(REDRAWBUTSOBJECT, 0);
	/* groups could have changed ipo */
	allspace(REMAKEIPO, 0);
	allqueue(REDRAWIPO, 0);
	allqueue(REDRAWNLA, 0);
	allqueue (REDRAWACTION, 0);
	/* for the time being */
	update_for_newframe_muted();
#ifdef NAN_LINEAR_PHYSICS	
	end_anim_sumo();
#endif
	waitcursor(0);
	G.f &= ~G_PLAYANIM;
	
	if (event==ESCKEY || event==SPACEKEY) return 1;
	else return 0;
}
