/*********************************************************************************/ /* Map_Sphere 0.20 -- image filter plug-in for The Gimp program */ /* Copyright (C) 1996 Tom Bech */ /* Copyright (C) 1996 Federico Mena Quintero */ /*===============================================================================*/ /* E-mail: tomb@ii.uib.no (Tom) or federico@nuclecu.unam.mx (Federico) */ /* You can contact the original The Gimp authors at gimp@xcf.berkeley.edu */ /*===============================================================================*/ /* This program is free software; you can redistribute it and/or modify it under */ /* the terms of the GNU General Public License as published by the Free Software */ /* Foundation; either version 2 of the License, or (at your option) any later */ /* version. */ /*===============================================================================*/ /* This program is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ /* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.*/ /*===============================================================================*/ /* You should have received a copy of the GNU General Public License along with */ /* this program; if not, write to the Free Software Foundation, Inc., 675 Mass */ /* Ave, Cambridge, MA 02139, USA. */ /*===============================================================================*/ /* In other words, you can't sue me for whatever happens while using this ;) */ /*********************************************************************************/ /* Changes (post 0.10): */ /* -> 0.11 Fixed a bug in PhongShade() (highlights appeared at the dark side of */ /* the sphere too). */ /* -> 0.12 Added bilinear interpolation for fetching pixels (Quartic). */ /* -> 0.13 Added Equator and NorthPole rotation (Tom). */ /* -> 0.14 Fixed a glitch in the interpolation, so that it now wraps coordinates */ /* instead of returning black pixels. Oops. (Quartic) */ /* -> 0.15 Added supersampling (antialiasing). I'll replace it with an adaptive */ /* version later on. This will have to do for now :) (Tom). */ /* -> 0.16 Added adaptive supersampling (antialiasing). Generally produces much */ /* better results. If you set MAX_DEPTH to 1, this is just the same as */ /* the old static variant (Tom). Rearranged the dialog a bit so it will */ /* fit into a 800x600 screen (Tom). */ /* -> 0.17 Dialog for MAX_DEPTH and pixel tresholds. Removed spot-light option: */ /* I've decided that spot-light doesn't make sense with spheres. (Tom) */ /* -> 0.18 Added a line to help compile plug-ins using Ingo's perl script. New */ /* option to either overwrite source image, or create new image (Tom) */ /* -> 0.19 Changed the GIMP tags to MJH style (Tom). */ /* -> 0.20 Changed some UI stuff causing trouble with the 0.60 release (Tom) */ /*********************************************************************************/ /* @(GIMP) = @(GIMP_DEP) = @(GIMP_OBJ) = @(GIMP_LIB) = @(GIMP_AUTHOR) = @(GIMP_EMAIL) = @(GIMP_DESC) = @(GIMP_VERSION) = <0.20> @(GIMP_URL) = */ #include #include #include #include "gimp.h" /************/ /* Typedefs */ /************/ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define EPSILON 1.0e-5 #define MAX_CLEANUP_STACK 10 #define NO_LIGHT 0 #define DIRECTIONAL_LIGHT 1 #define POINT_LIGHT 2 #define WITHIN(a, b, c) ((((a) <= (b)) && ((b) <= (c))) ? 1 : 0) /***********************/ /* Some useful structs */ /***********************/ typedef struct { double x,y,z; } Vector; typedef struct { double r,g,b; } RGBPixel; typedef struct { double AmbientInt; double DiffuseInt; double DiffuseRef; double SpecularRef; double Highlight; RGBPixel Color; } MaterialSettings; typedef struct { Vector Position; Vector Direction; RGBPixel Color; double Intensity; } LightSettings; /*****************************/ /* Global variables and such */ /*****************************/ Vector ZeroVec = {0.0,0.0,0.0}; Vector Unit_X = {1.0,0.0,0.0}; Vector Unit_Y = {0.0,1.0,0.0}; Vector Unit_Z = {0.0,0.0,1.0}; Vector ViewPoint = {0.5,0.5,0.8}; Vector Equator = {0.0,0.0,-1.0}; Vector NorthPole = {0.0,1.0,0.0}; Vector SpherePosition; double SphereRadius; RGBPixel White = {1.0,1.0,1.0}; RGBPixel Black = {0.0,0.0,0.0}; RGBPixel OldLightColor,LightColor = {1.0,1.0,1.0}; RGBPixel BackGround; RGBPixel PixelTresholds; LightSettings CurrentLight,OldLight; MaterialSettings CurrentMaterial,OldMaterial; MaterialSettings CurrentRefMaterial,OldRefMaterial; Image input,output; unsigned char *dinput=NULL,*doutput=NULL; int width,height,channels,modulo; int MainDialogID=-1,ColorDialogID=-1,PointLightDialogID=-1,MaterialDialogID=-1; int DirectionalLightDialogID=-1,SampleDialogID=-1; long light_toggles[3] = { 0,0,1 }; long maxcounter,max_depth=3; long antialiasing=1; long CreateNewImage=0; long Alpha=0,Beta=0; int CleanupStackCount=0; void *CleanupStack[MAX_CLEANUP_STACK]; /**************************************/ /* Some neccesary function prototypes */ /**************************************/ void KillAllDialogs(int mode); void CreateSampleDialog(void); void CreateColorDialog(RGBPixel *color); void CreatePointLightDialog(LightSettings *light); void CreateDirectionalLightDialog(LightSettings *light); void CreateMaterialDialog(MaterialSettings *material); /************************/ /* Convenience routines */ /************************/ int GetLightType(void) { if (light_toggles[0]==1) return(NO_LIGHT); else if (light_toggles[1]==1) return(DIRECTIONAL_LIGHT); else return(POINT_LIGHT); } void InitCleanup(void) { int cnt; for (cnt=0;cntMAX_CLEANUP_STACK) { gimp_message("Lighting: CleanupStack overflow! Terminating..\n"); Cleanup(); KillAllDialogs(0); exit(1); } CleanupStack[CleanupStackCount++]=element; } double DegToRad(double angle) { return((angle/360.0)*(2.0*M_PI)); } double RadToDeg(double angle) { return((angle/(2*M_PI))*360.0); } void RGBAdd(RGBPixel *a,RGBPixel *b) { a->r=a->r+b->r; a->g=a->g+b->g; a->b=a->b+b->b; } void RGBMul(RGBPixel *a,double b) { a->r=a->r*b; a->g=a->g*b; a->b=a->b*b; } void RGBClamp(RGBPixel *a) { if (a->r>1.0) a->r=1.0; if (a->g>1.0) a->g=1.0; if (a->b>1.0) a->b=1.0; if (a->r<0.0) a->r=0.0; if (a->g<0.0) a->g=0.0; if (a->b<0.0) a->b=0.0; } void SetColor(RGBPixel *a,double r,double g,double b) { a->r=r; a->g=g; a->b=b; } double InnerProduct(Vector *a,Vector *b) { return(a->x*b->x+a->y*b->y+a->z*b->z); } Vector CrossProduct(Vector *a,Vector *b) { Vector normal; normal.x=a->y*b->z-a->z*b->y; normal.y=a->z*b->x-a->x*b->z; normal.z=a->x*b->y-a->y*b->x; return(normal); } double VectorLength(Vector *a) { return(sqrt(a->x*a->x+a->y*a->y+a->z*a->z)); } void Normalize(Vector *a) { double len; len=VectorLength(a); if (len!=0.0) { len=1.0/len; a->x=a->x*len; a->y=a->y*len; a->z=a->z*len; } else *a=ZeroVec; } void MulVector(Vector *a,double b) { a->x=a->x*b; a->y=a->y*b; a->z=a->z*b; } void SubVector(Vector *c,Vector *a,Vector *b) { c->x=a->x-b->x; c->y=a->y-b->y; c->z=a->z-b->z; } void SetVector(Vector *a, double x,double y,double z) { a->x=x; a->y=y; a->z=z; } void AddVector(Vector *c,Vector *a,Vector *b) { c->x=a->x+b->x; c->y=a->y+b->y; c->z=a->z+b->z; } /******************************************************************************/ /* Transforms the cartesian vector v(x,y,z) to spherical form S(r,alpha,beta) */ /******************************************************************************/ void VectorToSphere(Vector *v,Vector *s) { double r=VectorLength(v),lu,DP; Vector u; /* Deal with zero sized vectors */ /* ============================ */ if (r==0) { *s=ZeroVec; return; } s->x=r; /* We want the unit vector */ /* ======================= */ MulVector(v,1.0/r); /* Compute angle to the projection in the XY plane */ /* This is actually a the cylindrical portion (0..PI) */ /* ================================================== */ u=*v;u.z=0.0; DP=InnerProduct(&Unit_X,&u); lu=VectorLength(&u); if (lu!=0.0) { s->y=acos(DP/lu); if (u.y<0.0) s->y=s->y+M_PI; } else (s->y=M_PI/2.0); /* Now compute the angle between the positive z-axis and the */ /* given vector. This maps to the entire interval ranging */ /* from 0.0 to 2*PI. */ /* ========================================================= */ DP=InnerProduct(&Unit_Z,v); s->z=acos(DP/r); } /*******************************************************************************/ /* Transforms the spherical vector s from spherical to cartesian form v(x,y,z) */ /*******************************************************************************/ void SphereToVector(Vector *s,Vector *v) { /* Simply snipped from my analytic geometry book :) */ /* ================================================ */ v->x=s->x*sin(s->z)*cos(s->y); v->y=s->x*sin(s->z)*sin(s->y); v->z=s->x*cos(s->z); } /************************************************************/ /* Rotate a given vector in the spherical coordinate system */ /************************************************************/ void RotateVector(Vector *v,double alpha,double beta) { Vector s; VectorToSphere(v,&s); s.y=s.y+alpha; s.z=s.z+beta; SphereToVector(&s,v); } /******************/ /* Implementation */ /******************/ void SetDefaultSettings(void) { SetVector(&CurrentLight.Position, -0.3,-0.5,0.8); SetVector(&CurrentLight.Direction, -1.0,-1.0,1.0); SetVector(&SpherePosition,0.5,0.5,0); SetColor(&PixelTresholds,0.25,0.2,0.4); SphereRadius=0.25; CurrentLight.Intensity = 1.0; CurrentLight.Color=White; CurrentMaterial.AmbientInt = 0.5; CurrentMaterial.DiffuseInt = 1.0; CurrentMaterial.DiffuseRef = 0.45; CurrentMaterial.SpecularRef = 0.5; CurrentMaterial.Highlight = 27.0; } /****************/ /* Main section */ /****************/ RGBPixel PhongShade(Vector *pos,Vector *viewpoint,Vector *normal,Vector *light, RGBPixel *diff_col,RGBPixel *spec_col,int type) { RGBPixel ambientcolor,diffusecolor,specularcolor; double NL,RV; Vector L,NN,V,Normal=*normal; /* Compute ambient intensity */ /* ========================= */ ambientcolor=*diff_col; RGBMul(&ambientcolor,CurrentMaterial.AmbientInt); /* Compute (N*L) term of Phong's equation */ /* ====================================== */ if (type==POINT_LIGHT) SubVector(&L,light,pos); else L=*light; Normalize(&L); NL=2.0*InnerProduct(&Normal,&L); if (NL>=0.0) { /* Compute (R*V)^alpha term of Phong's equation */ /* ============================================ */ SubVector(&V,viewpoint,pos); Normalize(&V); MulVector(&Normal,NL); SubVector(&NN,&Normal,&L); RV=InnerProduct(&NN,&V); RV=pow(RV,CurrentMaterial.Highlight); /* Compute diffuse and specular intensity contribution */ /* =================================================== */ diffusecolor=*diff_col; RGBMul(&diffusecolor,CurrentMaterial.DiffuseRef); RGBMul(&diffusecolor,NL); specularcolor=*spec_col; RGBMul(&specularcolor,CurrentMaterial.SpecularRef); RGBMul(&specularcolor,RV); RGBAdd(&diffusecolor,&specularcolor); RGBMul(&diffusecolor,CurrentMaterial.DiffuseInt); RGBClamp(&diffusecolor); RGBAdd(&ambientcolor,&diffusecolor); } return(ambientcolor); } long int XYToIndex(int x,int y) { return((long int)x*(long int)channels+(long int)y*(long int)modulo); } int CheckBounds(int x,int y) { if (x<0 || y<0 || x>width-1 || y>height-1) return(1); else return(0); } unsigned char PeekMap(unsigned char *DispMap,int x,int y) { long int index; index=(long int)x+(long int)width*(long int)y; return(DispMap[index]); } void PokeMap(unsigned char *DispMap,int x,int y,unsigned char value) { long int index; index=(long int)x+(long int)width*(long int)y; DispMap[index]=value; } RGBPixel Peek(unsigned char *data,int x,int y) { long int index=XYToIndex(x,y); RGBPixel color; color.r=((double)data[index])/255.0; color.g=((double)data[index+1])/255.0; color.b=((double)data[index+2])/255.0; return(color); } void Poke(unsigned char *data,int x,int y,RGBPixel *color) { long int index=XYToIndex(x,y); data[index]=(unsigned char)(255.0*color->r); data[index+1]=(unsigned char)(255.0*color->g); data[index+2]=(unsigned char)(255.0*color->b); } /***********************************************************************/ /* Given the NorthPole, Equator and a third vector (normal) compute */ /* the conversion from spherical oordinates to image space coordinates */ /***********************************************************************/ void SphereToImage(Vector *normal,double *u,double *v) { static double alpha,fac; static Vector CrossP; alpha=acos(-InnerProduct(&NorthPole,normal)); *v=alpha/M_PI; if (*v==0.0 || *v==1.0) *u=0.0; else { fac=InnerProduct(&Equator,normal)/sin(alpha); /* Make sure that we map to -1.0..1.0 (take care of rounding errors) */ /* ================================================================= */ if (fac>1.0) fac=1.0; else if (fac<-1.0) fac=-1.0; *u=acos(fac)/(2.0*M_PI); CrossP=CrossProduct(&NorthPole,&Equator); if (InnerProduct(&CrossP,normal)<0.0) *u=1.0-*u; } } Vector IntToPos(int x,int y) { Vector pos; pos.x=(double)x/(double)width; pos.y=(double)y/(double)height; pos.z=0.0; return(pos); } void PosToInt(double x,double y,int *scr_x,int *scr_y) { *scr_x=(int)(x*(double)width); *scr_y=(int)(y*(double)height); } /***************************************************/ /* Compute intersection point with sphere (if any) */ /***************************************************/ int SphereIntersect(Vector *dir,Vector *viewp,Vector *spos) { static double alpha,beta,tau,s1,s2; static Vector t; SubVector(&t,&SpherePosition,viewp); alpha=InnerProduct(dir,&t); beta=InnerProduct(&t,&t); tau=alpha*alpha-beta+SphereRadius*SphereRadius; if (tau>=0.0) { tau=sqrt(tau); s1=alpha+tau; s2=alpha-tau; if (s2x=viewp->x+s1*dir->x; spos->y=viewp->y+s1*dir->y; spos->z=viewp->z+s1*dir->z; return(1); } else return(0); } /******************************/ /* Rendering stuff below here */ /******************************/ static RGBPixel bilinear(double x, double y, RGBPixel *p) { double m0, m1; double ix, iy; RGBPixel v; x = fmod(x, 1.0); y = fmod(y, 1.0); if (x < 0) x += 1.0; if (y < 0) y += 1.0; ix = 1.0 - x; iy = 1.0 - y; /* Red */ /* === */ m0 = ix * p[0].r + x * p[1].r; m1 = ix * p[2].r + x * p[3].r; v.r = iy * m0 + y * m1; /* Green */ /* ===== */ m0 = ix * p[0].g + x * p[1].g; m1 = ix * p[2].g + x * p[3].g; v.g = iy * m0 + y * m1; /* Blue */ /* ==== */ m0 = ix * p[0].b + x * p[1].b; m1 = ix * p[2].b + x * p[3].b; v.b = iy * m0 + y * m1; return(v); } /* bilinear */ RGBPixel GetImageColor(double u,double v) { int x1, y1, x2, y2; RGBPixel p[4]; PosToInt(u,v,&x1,&y1); if (x1 < 0) x1 = width - (-x1 % width); else x1 = x1 % width; if (y1 < 0) y1 = height - (-y1 % height); else y1 = y1 % height; x2 = (x1 + 1) % width; y2 = (y1 + 1) % height; p[0] = Peek(dinput, x1, y1); p[1] = Peek(dinput, x2, y1); p[2] = Peek(dinput, x1, y2); p[3] = Peek(dinput, x2, y2); return(bilinear(u * width, v * height, p)); } /****************************************************/ /* These routines computes the color of the surface */ /* at a given point, with and without supersamling. */ /****************************************************/ RGBPixel GetRayColor(Vector *pos) { RGBPixel color=BackGround; int LightType=GetLightType(); Vector ray,spos,normal; double vx,vy; SubVector(&ray,pos,&ViewPoint); Normalize(&ray); if (SphereIntersect(&ray,&ViewPoint,&spos)==1) { SubVector(&normal,&spos,&SpherePosition); Normalize(&normal); /* Map image to sphere */ /* =================== */ SphereToImage(&normal,&vx,&vy); color=GetImageColor(vx,vy); /* Compute shading at this point */ /* ============================= */ if (LightType!=NO_LIGHT) color=PhongShade(&spos,&ViewPoint,&normal,&CurrentLight.Position,&color, &CurrentLight.Color,LightType); RGBClamp(&color); } return(color); } /***********************************************************************/ /* This implements adaptive supersampling (sounds pretty cool, huh? ;) */ /* It fires a ray through each corner of a (sub)pixel and checks the */ /* differences in intensity. If the differences are not small enough, */ /* the intensities at the corners is replaced by the supersampled */ /* intensities of their respective quadrants and so on and so forth.. */ /* (Why is recursion always so hard to explain in a few words...) */ /***********************************************************************/ RGBPixel SuperSample(Vector p,double xsize,double ysize,int depth) { RGBPixel Threshold=Black,Max=Black,Min=White,color=Black,cols[4]; Vector pos[4]; int cnt; /* Shoot rays through the corner of this sub-pixel */ /* =============================================== */ SetVector(&pos[0],p.x-xsize,p.y-ysize,0.0); SetVector(&pos[1],p.x+xsize,p.y+ysize,0.0); SetVector(&pos[2],p.x-xsize,p.y+ysize,0.0); SetVector(&pos[3],p.x+xsize,p.y-ysize,0.0); for (cnt=0;cnt<4;cnt++) { cols[cnt]=GetRayColor(&pos[cnt]); if (cols[cnt].r>Max.r) Max.r=cols[cnt].r; if (cols[cnt].g>Max.g) Max.g=cols[cnt].g; if (cols[cnt].b>Max.b) Max.b=cols[cnt].b; if (cols[cnt].rPixelTresholds.r || Threshold.g>PixelTresholds.g || Threshold.b>PixelTresholds.b) { /* *Sigh* We have to subdivide this mess even further */ /* ================================================== */ xsize=xsize/2.0; ysize=ysize/2.0; SetVector(&pos[0],p.x-xsize,p.y-ysize,0.0); SetVector(&pos[1],p.x+xsize,p.y+ysize,0.0); SetVector(&pos[2],p.x-xsize,p.y+ysize,0.0); SetVector(&pos[3],p.x+xsize,p.y-ysize,0.0); cols[0]=SuperSample(pos[0],xsize,ysize,depth+1); cols[1]=SuperSample(pos[1],xsize,ysize,depth+1); cols[2]=SuperSample(pos[2],xsize,ysize,depth+1); cols[3]=SuperSample(pos[3],xsize,ysize,depth+1); } } for (cnt=0;cnt<4;cnt++) RGBAdd(&color,&cols[cnt]); RGBMul(&color,0.25); return(color); } RGBPixel ShadePictureAdaptiveAntialiasing(int x,int y) { RGBPixel color; Vector p; double xsize,ysize; p=IntToPos(x,y); xsize=1.0/(2.0*width); ysize=1.0/(2.0*height); color=SuperSample(p,xsize,ysize,1); return(color); } RGBPixel ShadePictureAntialiasing(int x,int y) { RGBPixel color=Black,col; Vector p,pos[4]; double xadd,yadd; int cnt; p=IntToPos(x,y); xadd=1.0/(4.0*width); yadd=1.0/(4.0*height); SetVector(&pos[0],p.x-xadd,p.y-yadd,0.0); SetVector(&pos[1],p.x+xadd,p.y+yadd,0.0); SetVector(&pos[2],p.x-xadd,p.y+yadd,0.0); SetVector(&pos[3],p.x+xadd,p.y-yadd,0.0); for (cnt=0;cnt<4;cnt++) { col=GetRayColor(&pos[cnt]); RGBAdd(&color,&col); } RGBMul(&color,0.25); return(color); } RGBPixel ShadePicture(int x,int y) { RGBPixel color=Black; int LightType=GetLightType(); Vector ray,pos,spos,normal; double vx,vy; /* Check if the ray through image pos (x,y) hits our sphere */ /* ======================================================== */ pos=IntToPos(x,y); SubVector(&ray,&pos,&ViewPoint); Normalize(&ray); if (SphereIntersect(&ray,&ViewPoint,&spos)==1) { SubVector(&normal,&spos,&SpherePosition); Normalize(&normal); /* Map image to sphere */ /* =================== */ SphereToImage(&normal,&vx,&vy); color=GetImageColor(vx,vy); /* Compute shading at this point */ /* ============================= */ if (LightType!=NO_LIGHT) color=PhongShade(&spos,&ViewPoint,&normal,&CurrentLight.Position,&color, &CurrentLight.Color,LightType); RGBClamp(&color); } return(color); } /*************/ /* Main loop */ /*************/ void ComputeLighting(void) { int xcount,ycount; long counter=0; RGBPixel color; unsigned char r,g,b; width=gimp_image_width(input); height=gimp_image_height(input); maxcounter=(long int)width*(long int)height; channels=gimp_image_channels(input); modulo=channels*width; if (CreateNewImage==1) output=gimp_get_output_image(0); else output=gimp_new_image("Untitled sphere-mapped image",width,height,RGB_IMAGE); if (!output) { gimp_message("Map_Sphere: Unable to create output image!\n"); return; } RotateVector(&Equator,DegToRad((double)Alpha),DegToRad((double)Beta)); RotateVector(&NorthPole,DegToRad((double)Alpha),DegToRad((double)Beta)); gimp_init_progress("Map to sphere"); gimp_background_color(&r,&g,&b); BackGround.r=(double)r/255.0; BackGround.g=(double)g/255.0; BackGround.b=(double)b/255.0; dinput=gimp_image_data(input); doutput=gimp_image_data(output); if (antialiasing==0) { for (ycount=0;ycountAmbientInt); ValID = gimp_new_text(MaterialDialogID,DummyID,buf); gimp_add_callback(MaterialDialogID, ValID, double_callback, &material->AmbientInt); DummyID = gimp_new_column_group(MaterialDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(MaterialDialogID, DummyID, "Diffuse:"); sprintf(buf, "%f", material->DiffuseInt); ValID = gimp_new_text(MaterialDialogID,DummyID,buf); gimp_add_callback(MaterialDialogID, ValID, double_callback, &material->DiffuseInt); FrameID = gimp_new_frame (MaterialDialogID, MainGroupID, "Reflectivity"); FrameGroupID = gimp_new_row_group(MaterialDialogID, FrameID, NORMAL, ""); DummyID = gimp_new_column_group(MaterialDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(MaterialDialogID, DummyID, "Diffuse:"); sprintf(buf, "%f", material->DiffuseRef); ValID = gimp_new_text(MaterialDialogID,DummyID,buf); gimp_add_callback(MaterialDialogID, ValID, double_callback, &material->DiffuseRef); DummyID = gimp_new_column_group(MaterialDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(MaterialDialogID, DummyID, "Specular:"); sprintf(buf, "%f", material->SpecularRef); ValID = gimp_new_text(MaterialDialogID,DummyID,buf); gimp_add_callback(MaterialDialogID, ValID, double_callback, &material->SpecularRef); DummyID = gimp_new_column_group(MaterialDialogID,MainGroupID, NORMAL, ""); gimp_new_label(MaterialDialogID, DummyID, "Highlight:"); sprintf(buf, "%f", material->Highlight); ValID = gimp_new_text(MaterialDialogID,DummyID,buf); gimp_add_callback(MaterialDialogID, ValID, double_callback, &material->Highlight); /* Other callbacks */ /* =============== */ gimp_add_callback(MaterialDialogID, gimp_ok_item_id(MaterialDialogID), material_ok_callback, NULL); gimp_add_callback(MaterialDialogID, gimp_cancel_item_id(MaterialDialogID), material_cancel_callback, NULL); } void CreateDirectionalLightDialog(LightSettings *light) { int MainGroupID,FrameID,FrameGroupID,XValID,YValID,ZValID,DummyID; char buf[100]; DirectionalLightDialogID = gimp_new_dialog("Directional light settings"); MainGroupID = gimp_new_row_group(DirectionalLightDialogID, DEFAULT, NORMAL, ""); FrameID = gimp_new_frame (DirectionalLightDialogID, MainGroupID, "Direction vector"); FrameGroupID = gimp_new_row_group(DirectionalLightDialogID, FrameID, NORMAL, ""); DummyID = gimp_new_column_group(DirectionalLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(DirectionalLightDialogID, DummyID, "X:"); sprintf(buf, "%f", light->Direction.x); XValID = gimp_new_text(DirectionalLightDialogID,DummyID,buf); DummyID = gimp_new_column_group(DirectionalLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(DirectionalLightDialogID, DummyID, "Y:"); sprintf(buf, "%f", light->Direction.y); YValID = gimp_new_text(DirectionalLightDialogID,DummyID,buf); DummyID = gimp_new_column_group(DirectionalLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(DirectionalLightDialogID, DummyID, "Z:"); sprintf(buf, "%f", light->Direction.z); ZValID = gimp_new_text(DirectionalLightDialogID,DummyID,buf); /* Callbacks */ /* ========= */ gimp_add_callback(DirectionalLightDialogID, XValID, double_callback, &light->Direction.x); gimp_add_callback(DirectionalLightDialogID, YValID, double_callback, &light->Direction.y); gimp_add_callback(DirectionalLightDialogID, ZValID, double_callback, &light->Direction.z); gimp_add_callback(DirectionalLightDialogID, gimp_ok_item_id(DirectionalLightDialogID), directional_light_ok_callback, NULL); gimp_add_callback(DirectionalLightDialogID, gimp_cancel_item_id(DirectionalLightDialogID), directional_light_cancel_callback, NULL); } void CreatePointLightDialog(LightSettings *light) { int MainGroupID,FrameID,FrameGroupID,XValID,YValID,ZValID,DummyID; char buf[100]; PointLightDialogID = gimp_new_dialog("Point light settings"); MainGroupID = gimp_new_row_group(PointLightDialogID, DEFAULT, NORMAL, ""); FrameID = gimp_new_frame (PointLightDialogID, MainGroupID, "Position"); FrameGroupID = gimp_new_row_group(PointLightDialogID, FrameID, NORMAL, ""); DummyID = gimp_new_column_group(PointLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(PointLightDialogID, DummyID, "X:"); sprintf(buf, "%f", light->Position.x); XValID = gimp_new_text(PointLightDialogID,DummyID,buf); DummyID = gimp_new_column_group(PointLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(PointLightDialogID, DummyID, "Y:"); sprintf(buf, "%f", light->Position.y); YValID = gimp_new_text(PointLightDialogID,DummyID,buf); DummyID = gimp_new_column_group(PointLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(PointLightDialogID, DummyID, "Z:"); sprintf(buf, "%f", light->Position.z); ZValID = gimp_new_text(PointLightDialogID,DummyID,buf); /* Callbacks */ /* ========= */ gimp_add_callback(PointLightDialogID, XValID, double_callback, &light->Position.x); gimp_add_callback(PointLightDialogID, YValID, double_callback, &light->Position.y); gimp_add_callback(PointLightDialogID, ZValID, double_callback, &light->Position.z); gimp_add_callback(PointLightDialogID, gimp_ok_item_id(PointLightDialogID), pointlight_ok_callback, NULL); gimp_add_callback(PointLightDialogID, gimp_cancel_item_id(PointLightDialogID), pointlight_cancel_callback, NULL); } void CreateColorDialog(RGBPixel *color) { int MainGroupID,ColorFrameID,ColorGroupID,RedScaleID,GreenScaleID,BlueScaleID; ColorDialogID = gimp_new_dialog("Light color"); MainGroupID = gimp_new_row_group(ColorDialogID, DEFAULT, NORMAL, ""); ColorFrameID = gimp_new_frame (ColorDialogID, MainGroupID, "Color"); ColorGroupID = gimp_new_row_group(ColorDialogID, ColorFrameID, NORMAL, ""); gimp_new_label(ColorDialogID, ColorGroupID, "Red:"); RedScaleID = gimp_new_scale (ColorDialogID, ColorGroupID, 0, 255, (long)(255.0*color->r), 0); gimp_new_label(ColorDialogID, ColorGroupID, "Green:"); GreenScaleID = gimp_new_scale (ColorDialogID, ColorGroupID, 0, 255, (long)(255.0*color->g), 0); gimp_new_label(ColorDialogID, ColorGroupID, "Blue:"); BlueScaleID = gimp_new_scale (ColorDialogID, ColorGroupID, 0, 255, (long)(255.0*color->b), 0); /* Callbacks */ /* ========= */ gimp_add_callback (ColorDialogID, RedScaleID, scale_callback, &color->r); gimp_add_callback (ColorDialogID, GreenScaleID, scale_callback, &color->g); gimp_add_callback (ColorDialogID, BlueScaleID, scale_callback, &color->b); gimp_add_callback(ColorDialogID, gimp_ok_item_id(ColorDialogID), color_ok_callback, NULL); gimp_add_callback(ColorDialogID, gimp_cancel_item_id(ColorDialogID), color_cancel_callback, NULL); } void CreateMainDialog(void) { int MainGroupID,LightFrameID,LightGroupID,DirectionalLightID,PointLightID,RadioLightID; int LightSettingsID,LightColorID,PosFrameID,PosGroupID,DummyID,XValID,YValID,ZValID,RadiusID; int SurfaceSettingsID,NoLightID,AlphaID,BetaID,AntialiasingID,OptionsRowID,SettingsRowID; int AntiSettingsID,OptionColID,NewRowID,NewImageID; char buf[100]; MainDialogID = gimp_new_dialog("Map Sphere"); MainGroupID = gimp_new_column_group(MainDialogID, DEFAULT, NORMAL, ""); OptionsRowID = gimp_new_row_group(MainDialogID, MainGroupID, NORMAL, ""); SettingsRowID = gimp_new_row_group(MainDialogID, MainGroupID, NORMAL, ""); DummyID = gimp_new_frame (MainDialogID, OptionsRowID, "Options"); OptionColID = gimp_new_row_group(MainDialogID, DummyID, NORMAL, ""); DummyID = gimp_new_column_group(MainDialogID, OptionColID, NORMAL, ""); AntialiasingID = gimp_new_check_button(MainDialogID, DummyID, "Antialiasing"); AntiSettingsID = gimp_new_push_button(MainDialogID,DummyID,"Settings.."); NewRowID = gimp_new_column_group(MainDialogID, OptionColID, NORMAL, ""); NewImageID = gimp_new_check_button(MainDialogID, NewRowID, "Overwrite source image"); LightFrameID = gimp_new_frame (MainDialogID, OptionsRowID, "Light source"); LightGroupID = gimp_new_row_group(MainDialogID, LightFrameID, NORMAL, ""); RadioLightID = gimp_new_row_group(MainDialogID, LightGroupID, RADIO, ""); NoLightID = gimp_new_radio_button (MainDialogID, RadioLightID, "No light"); DirectionalLightID = gimp_new_radio_button (MainDialogID, RadioLightID, "Directional light"); PointLightID = gimp_new_radio_button (MainDialogID, RadioLightID, "Point light"); LightSettingsID = gimp_new_push_button(MainDialogID,OptionsRowID,"Lightsource settings.."); LightColorID = gimp_new_push_button(MainDialogID,OptionsRowID,"Lightsource color.."); PosFrameID = gimp_new_frame (MainDialogID, SettingsRowID, "Orientation and size"); PosGroupID = gimp_new_row_group(MainDialogID, PosFrameID, NORMAL, ""); DummyID = gimp_new_column_group(MainDialogID,PosGroupID, NORMAL, ""); gimp_new_label(MainDialogID, DummyID, "X pos.:"); sprintf(buf, "%f", SpherePosition.x); XValID = gimp_new_text(MainDialogID,DummyID,buf); DummyID = gimp_new_column_group(MainDialogID,PosGroupID, NORMAL, ""); gimp_new_label(MainDialogID, DummyID, "Y pos.:"); sprintf(buf, "%f", SpherePosition.y); YValID = gimp_new_text(MainDialogID,DummyID,buf); DummyID = gimp_new_column_group(MainDialogID,PosGroupID, NORMAL, ""); gimp_new_label(MainDialogID, DummyID, "Z pos.:"); sprintf(buf, "%f", SpherePosition.z); ZValID = gimp_new_text(MainDialogID,DummyID,buf); DummyID = gimp_new_column_group(MainDialogID,PosGroupID, NORMAL, ""); gimp_new_label(MainDialogID, DummyID, "Radius:"); sprintf(buf, "%f", SphereRadius); RadiusID = gimp_new_text(MainDialogID,DummyID,buf); gimp_new_label(MainDialogID, PosGroupID, "Angle 1:"); AlphaID = gimp_new_scale (MainDialogID, PosGroupID, 0, 360, Alpha, 0); gimp_new_label(MainDialogID, PosGroupID, "Angle 2:"); BetaID = gimp_new_scale (MainDialogID, PosGroupID, 0, 360, Beta, 0); SurfaceSettingsID = gimp_new_push_button(MainDialogID,OptionsRowID,"Image material settings.."); /* Default settings */ /* ================ */ gimp_change_item (MainDialogID, NoLightID, sizeof(light_toggles[0]), &light_toggles[0]); gimp_change_item (MainDialogID, DirectionalLightID, sizeof(light_toggles[1]), &light_toggles[1]); gimp_change_item (MainDialogID, PointLightID, sizeof(light_toggles[2]), &light_toggles[2]); gimp_change_item (MainDialogID, AntialiasingID, sizeof(long), &antialiasing); gimp_change_item (MainDialogID, NewImageID, sizeof(long), &CreateNewImage); /* Callbacks */ /* ========= */ gimp_add_callback (MainDialogID, AntialiasingID, radio_callback, &antialiasing); gimp_add_callback (MainDialogID, NewImageID, radio_callback, &CreateNewImage); gimp_add_callback (MainDialogID, AntiSettingsID, AntiSettings_callback, NULL); gimp_add_callback (MainDialogID, NoLightID, radio_callback, &light_toggles[0]); gimp_add_callback (MainDialogID, DirectionalLightID, radio_callback, &light_toggles[1]); gimp_add_callback (MainDialogID, PointLightID, radio_callback, &light_toggles[2]); gimp_add_callback (MainDialogID, LightSettingsID, LightSettings_callback, NULL); gimp_add_callback (MainDialogID, LightColorID, LightColor_callback, NULL); gimp_add_callback(MainDialogID, XValID, double_callback, &SpherePosition.x); gimp_add_callback(MainDialogID, YValID, double_callback, &SpherePosition.y); gimp_add_callback(MainDialogID, ZValID, double_callback, &SpherePosition.z); gimp_add_callback(MainDialogID, AlphaID, angle_scale_callback, &Alpha); gimp_add_callback(MainDialogID, BetaID, angle_scale_callback, &Beta); gimp_add_callback(MainDialogID, RadiusID, double_callback, &SphereRadius); gimp_add_callback(MainDialogID, SurfaceSettingsID, SurfaceSettings_callback, NULL); gimp_add_callback(MainDialogID, gimp_ok_item_id(MainDialogID), main_ok_callback, NULL); gimp_add_callback(MainDialogID, gimp_cancel_item_id(MainDialogID), main_cancel_callback, NULL); } int main(int argc,char **argv) { /* Standard stuff straight out of Marc's tutorial :) */ /* ================================================= */ if (!gimp_init(argc,argv)) return(0); input=gimp_get_input_image(0); if (!input) return(0); /* We deal only with RGB images */ /* ============================ */ if (gimp_image_type(input)!=RGB_IMAGE) gimp_message("Map_Sphere: On RGB type images only!\n"); else { /* Create a nice dialog and show it */ /* ================================ */ SetDefaultSettings(); InitCleanup(); CreateMainDialog(); if (gimp_show_dialog(MainDialogID)) ComputeLighting(); Cleanup(); } /* Standard The End stuff.. */ /* ======================== */ gimp_free_image(input); gimp_free_image(output); gimp_quit(); return(0); }