/*  deform.c   June 2001
 *  
 *  support for deformation groups
 * 
 *	Reevan McKay
 *
 * $Id: deform.c,v 1.10 2004/11/10 15:55:07 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 *****
 */

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

#include "MEM_guardedalloc.h"

#include "DNA_curve_types.h"
#include "DNA_effect_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"

#include "BKE_curve.h"
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_effect.h"
#include "BKE_global.h"
#include "BKE_lattice.h"
#include "BKE_object.h"
#include "BKE_softbody.h"
#include "BKE_utildefines.h"

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

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

void color_temperature (float input, unsigned char *r, unsigned char *g, unsigned char *b)
{
	
	/* blue to red */
	
	float fr = (float)(*r);
	float fg = (float)(*g);
	float fb = (float)(*b);

	if (input < 0.0F)
		input = 0.0F;

	if (input > 1.0F)
		input = 1.0F;

	if (input<=0.25f){
		fr=0.0f;
		fg=255.0f * (input*4.0f);
		fb=255.0f;
	}
	else if (input<=0.50f){
		fr=0.0f;
		fg=255.0f;
		fb=255.0f * (1.0f-((input-0.25f)*4.0f)); 
	}
	else if (input<=0.75){
		fr=255.0f * ((input-0.50f)*4.0f);
		fg=255.0f;
		fb=0.0f;
	}
	else if (input<=1.0){
		fr=255.0f;
		fg=255.0f * (1.0f-((input-0.75f)*4.0f)); 
		fb=0.0f;
	}

	(*r) = (unsigned char)(fr * ((input/2.0f)+0.5f));
	(*g) = (unsigned char)(fg * ((input/2.0f)+0.5f));
	(*b) = (unsigned char)(fb * ((input/2.0f)+0.5f));


}

void copy_defgroups(ListBase *outbase, ListBase *inbase)
{
	bDeformGroup *defgroup, *defgroupn;

	outbase->first= outbase->last= 0;

	for (defgroup = inbase->first; defgroup; defgroup=defgroup->next){
		defgroupn= copy_defgroup(defgroup);
		BLI_addtail(outbase, defgroupn);
	}
}

bDeformGroup* copy_defgroup (bDeformGroup *ingroup)
{
	bDeformGroup *outgroup;

	if (!ingroup)
		return NULL;

	outgroup=MEM_callocN(sizeof(bDeformGroup), "deformGroup");
	
	/* For now, just copy everything over. */
	memcpy (outgroup, ingroup, sizeof(bDeformGroup));

	outgroup->next=outgroup->prev=NULL;

	return outgroup;
}

/* *************** HOOK ****************** */

/* vec==NULL: init
   vec is supposed to be local coord, deform happens in local space
*/

void hook_object_deform(Object *ob, int index, float *vec)
{
	float totforce;
	ObHook *hook;
	float vect[3], vectot[3];
	
	if(ob->hooks.first==NULL) return;
	
	/* reinitialize if... */
	if(vec==NULL) {
		totforce= 0.0;
		for(hook= ob->hooks.first; hook; hook= hook->next) {
			if(hook->parent) {
				hook->curindex= 0;
				Mat4Invert(ob->imat, ob->obmat);
				/* apparently this call goes from right to left... */
				Mat4MulSerie(hook->mat, ob->imat, hook->parent->obmat, hook->parentinv, NULL, 
							NULL, NULL, NULL, NULL);
			}
		}
		return;
	}

	totforce= 0.0;
	vectot[0]= vectot[1]= vectot[2]= 0.0;
	
	for(hook= ob->hooks.first; hook; hook= hook->next) {
		if(hook->parent) {
			
			/* is 'index' in hook array? */
			while(hook->curindex < hook->totindex-1) {
				if( hook->indexar[hook->curindex] < index ) hook->curindex++;
				else break;
			}
			
			if( hook->indexar[hook->curindex]==index ) {
				float fac= hook->force, len;
				
				VecMat4MulVecfl(vect, hook->mat, vec);

				if(hook->falloff!=0.0) {
					/* hook->cent is in local coords */
					len= VecLenf(vec, hook->cent);
					if(len > hook->falloff) fac= 0.0;
					else if(len>0.0) fac*= sqrt(1.0 - len/hook->falloff);
				}
				if(fac!=0.0) {
					totforce+= fac;
					vectot[0]+= fac*vect[0];
					vectot[1]+= fac*vect[1];
					vectot[2]+= fac*vect[2];
				}
			}
		}
	}

	/* if totforce < 1.0, we take old position also into account */
	if(totforce<1.0) {
		vectot[0]+= (1.0-totforce)*vec[0];
		vectot[1]+= (1.0-totforce)*vec[1];
		vectot[2]+= (1.0-totforce)*vec[2];
	}
	else VecMulf(vectot, 1.0/totforce);
	
	VECCOPY(vec, vectot);
}


/* modifiers: hooks, deform, softbody 
   mode=='s' is start, 'e' is end , 'a' is apply
*/

int mesh_modifier(Object *ob, char mode)
{
	static MVert *mvert=NULL;
	Mesh *me= ob->data;
	MVert *mv;
	int a, done=0;
	
	/* conditions if it's needed */
	if(ob->hooks.first);
	else if(ob->effect.first);	// weak... particles too
	else if(ob->parent && ob->parent->type==OB_LATTICE);
	else if(ob->parent && ob->partype==PARSKEL); 
	else if(ob->softflag);
	else return 0;
	
	if(me->totvert==0) return 0;
	
	if(mode=='s') { // "start"
		/* copy  */
		mvert= MEM_dupallocN(me->mvert);
		
		/* hooks */
		if(ob->hooks.first) {
			done= 1;
			
			/* NULL signals initialize */
			hook_object_deform(ob, 0, NULL);
			
			for(a=0, mv= me->mvert; a<me->totvert; a++, mv++) {
				hook_object_deform(ob, a, mv->co);
			}
		}
		
		if(ob->effect.first) done |= object_wave(ob);
		
		if(ob->softflag) {
			float ctime= bsystem_time(ob, NULL, (float)G.scene->r.cfra, 0.0);
			done= 1;
			object_softbody_step(ob, ctime);
		}
		
		/* deform: input mesh, output ob dl_verts. is used by subsurf (output should be in mesh ton!) */
		done |= object_deform(ob);	
		
		/* put deformed vertices in dl->verts, optional subsurf will replace that */
		if(done) {
			DispList *dl= find_displist_create(&ob->disp, DL_VERTS);
			float *fp;
			
			if(dl->verts) MEM_freeN(dl->verts);
			if(dl->nors) MEM_freeN(dl->nors);
			dl->nr= me->totvert;
			if(dl->nr) {
				
				/* make disp array */
				dl->verts= fp= MEM_mallocN(3*sizeof(float)*me->totvert, "deform1");
				mv= me->mvert;
				for(a=0; a<me->totvert; a++, mv++, fp+=3) {
					VECCOPY(fp, mv->co);
				}
			}
		}
		
	}
	else if(mode=='e') { // end
		if(mvert) {
			if(me->mvert) MEM_freeN(me->mvert);
			me->mvert= mvert;
			mvert= NULL;
		}
	}
	else if(mode=='a') { // apply
		if(mvert) MEM_freeN(mvert);
		mvert= NULL;
	}
	
	return done;
}

int curve_modifier(Object *ob, char mode)
{
	static ListBase nurb={NULL, NULL};
	Curve *cu= ob->data;
	Nurb *nu, *newnu;
	BezTriple *bezt;
	BPoint *bp;
	int a, index, done= 0;
	
	/* conditions if it's needed */
	if(ob->hooks.first);
	else if(ob->parent && ob->partype==PARSKEL); 
	else if(ob->parent && ob->parent->type==OB_LATTICE);
	else return 0;
	
	if(mode=='s') { // "start"
		/* copy  */
		nurb.first= nurb.last= NULL;	
		nu= cu->nurb.first;
		while(nu) {
			newnu= duplicateNurb(nu);
			BLI_addtail(&nurb, newnu);
			nu= nu->next;
		}
		
		/* hooks */
		if(ob->hooks.first) {
			done= 1;
			
			/* NULL signals initialize */
			hook_object_deform(ob, 0, NULL);
			index= 0;
			
			nu= cu->nurb.first;
			while(nu) {
				if((nu->type & 7)==CU_BEZIER) {
					bezt= nu->bezt;
					a= nu->pntsu;
					while(a--) {
						hook_object_deform(ob, index++, bezt->vec[0]);
						hook_object_deform(ob, index++, bezt->vec[1]);
						hook_object_deform(ob, index++, bezt->vec[2]);
						bezt++;
					}
				}
				else {
					bp= nu->bp;
					a= nu->pntsu*nu->pntsv;
					while(a--) {
						hook_object_deform(ob, index++, bp->vec);
						bp++;
					}
				}
					
				nu= nu->next;
			}
		}
	}
	else if(mode=='e') {
		/* paste */
		freeNurblist(&cu->nurb);
		cu->nurb= nurb;
	}
	else if(mode=='a') {
		freeNurblist(&nurb);
	}
	
	return done;
}

int lattice_modifier(Object *ob, char mode)
{
	static BPoint *bpoint;
	Lattice *lt= ob->data;
	BPoint *bp;
	int a, index, done= 0;
	
	/* conditions if it's needed */
	if(ob->hooks.first);
	else if(ob->parent && ob->partype==PARSKEL); 
	else return 0;
	
	if(mode=='s') { // "start"
		/* copy  */
		bpoint= MEM_dupallocN(lt->def);
		
		/* hooks */
		if(ob->hooks.first) {
			done= 1;
			
			/* NULL signals initialize */
			hook_object_deform(ob, 0, NULL);
			index= 0;
			bp= lt->def;
			a= lt->pntsu*lt->pntsv*lt->pntsw;
			while(a--) {
				hook_object_deform(ob, index++, bp->vec);
				bp++;
			}
		}
	}
	else { // end
		MEM_freeN(lt->def);
		lt->def= bpoint;
		bpoint= NULL;
	}
	
	return done;
}

