/*********************************************************************************/ /* Lighting 0.37 -- 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.20) */ /* -> 0.23: Fixed a bug in main_ok/cancel_callback(), float->doubles etc. */ /* Added a patch from Quartic to fix some prototype problems and some */ /* other minor stuff. Fixed a bug in the shader. */ /* -> 0.24 RGB images as bump-map now works. More big-fixes. Added "sphere" */ /* type mapping to Image Map Dialog. Applied a couple of patches from */ /* Quartic. Fixed a bug making waves and RGB bump maps now work togh. */ /* -> 0.25 Fixed "horiz. and vert. lines" bug. Some minor speed-ups. Memory- */ /* leaks reported by Quartic corrected. Some source cleanups. Oops, */ /* found and eliminated a serious bug in the refraction code. */ /* -> 0.26 Changed the whole ComputeWaves() function and the appropriate dialog */ /* so that it will be easier to use (and faster, too :) (Quartic) */ /* -> 0.27 Removed a superfluous division in RGBToGray() and changed the */ /* calculation part in GetThickness() to make it more efficient. */ /* (Quartic). Fixed a bug in PhongShade(). Removed "AmbientRef" from */ /* Material Settings dialog (AmbientInt is really enough) (Tom). */ /* -> 0.28 Added the bilinear interpolation stuff from map_sphere.c (Quartic). */ /* -> 0.29 Added spot light type and fixed a few inconsistencies regarding the */ /* z-axis (the z-axis should be positive out of the screen) (Tom). */ /* -> 0.30 Added a line to help compile plug-ins using Ingo's script (Tom). */ /* -> 0.31 Changed the UI a bit to make it fit better in lower resolution */ /* screens. Added "Overwrite source image" option (Tom). */ /* -> 0.32 Added "two-sided shading" option to material dialog. This is dis- */ /* abled as default. Fixed a annoying bug in the shading code and */ /* changed the default light and material settings a bit (Tom). */ /* -> 0.33 Added "skip shading at zero height" to the image mapping-dialog. */ /* This is handy when we want to apply lighting only to the area of */ /* the source image where the corresponding pixel in the bump-map is */ /* non-zero (Tom). */ /* -> 0.34 Implemented height-cacheing for bump-maps on images (not layers). */ /* This should result in a noticable speed-up. Note that this requires */ /* images to be at least 1 pixel wide and 3 high! (Tom). */ /* -> 0.35 Cleaned up GetNormal() a bit and got rid of TriNormal(). This should */ /* result in a minor speed-up, depending on compiler settings (Tom). */ /* -> 0.36 Changed the GIMP tags to MJH style (Tom). */ /* -> 0.37 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.37> @(GIMP_URL) = */ #include #include #include #include "gimp.h" /************/ /* Typedefs */ /************/ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define MAX_CLEANUP_STACK 15 #define DIRECTIONAL_LIGHT 0 #define POINT_LIGHT 1 #define SPOT_LIGHT 2 #define REFRACTION_DISABLED 0 #define REFRACTION_ENABLED 1 #define MAPPED_CONSTANT 0 #define MAPPED_WAVE 1 #define MAPPED_IMAGE 2 #define MAP_LINEAR 0 #define MAP_LOG 1 #define MAP_SINE 2 #define MAP_SPHERE 3 #define WITHOUT_COLOR 0 #define WITH_COLOR 1 #define EPSILON 1.0e-5 #define WITHIN(a, b, c) ((((a) <= (b)) && ((b) <= (c))) ? 1 : 0) #define CHECKBOUNDS(x,y) (x>=0 && y>=0 && xMapToggles[0]==1) return(MAP_LINEAR); else if (map->MapToggles[1]==1) return(MAP_LOG); else if (map->MapToggles[2]==1) return(MAP_SINE); else return(MAP_SPHERE); } 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); } double VectorLength(Vector *a) { return(sqrt(a->x*a->x+a->y*a->y+a->z*a->z)); } 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); } void Normalize(Vector *a) { double len; len=sqrt(a->x*a->x+a->y*a->y+a->z*a->z); 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; } /******************/ /* Implementation */ /******************/ void SetDefaultSettings(void) { SetVector(&CurrentPointLight.Position, 0.5,0.5,2.0); CurrentPointLight.Intensity = 1.0; CurrentPointLight.Color=White; CurrentDirectionalLight=CurrentPointLight; CurrentSpotLight=CurrentPointLight; SetVector(&CurrentDirectionalLight.Direction, -0.5,-0.5,-1.0); SetVector(&CurrentSpotLight.Position, 0.5,-0.25,0.05); SetVector(&CurrentSpotLight.Direction, 0.0,1.0,0.0); CurrentSpotLight.Angle=20; CurrentMaterial.AmbientInt = 0.2; CurrentMaterial.DiffuseInt = 1.0; CurrentMaterial.DiffuseRef = 0.4; CurrentMaterial.SpecularRef = 0.5; CurrentMaterial.Highlight = 10.0; CurrentRefMaterial = CurrentMaterial; CurrentRefMaterial.Highlight = 200.0; SetColor(&CurrentRefMaterial.Color,0.95,0.95,0.95); CurrentSurfaceImageMap.Imagemap=0; CurrentSurfaceImageMap.MapToggles[0]=0; CurrentSurfaceImageMap.MapToggles[1]=0; CurrentSurfaceImageMap.MapToggles[2]=0; CurrentSurfaceImageMap.MapToggles[3]=1; CurrentSurfaceImageMap.MinValue=0.0; CurrentSurfaceImageMap.MaxValue=0.1; CurrentSurfaceImageMap.FitToRange=0; CurrentSurfaceImageMap.SkipZero=0; CurrentRefraction.IOR_Air = 1.0; CurrentRefraction.IOR_Material = 1.52; CurrentRefraction.IOR_MapConstant=1; CurrentRefraction.IOR_MapType=MAPPED_CONSTANT; CurrentRefraction.IOR_Imagemap=CurrentSurfaceImageMap; CurrentRefraction.IOR_Imagemap.Imagemap=0; CurrentRefraction.IOR_Imagemap.MinValue=1.0; CurrentRefraction.IOR_Imagemap.MaxValue=1.65; CurrentRefraction.IOR_Imagemap.FitToRange=0; CurrentRefraction.IOR_Imagemap.SkipZero=1; CurrentRefraction.Thickness = 0.0; CurrentRefraction.Thickness_MapConstant=1; CurrentRefraction.Thickness_MapType=MAPPED_CONSTANT; CurrentRefraction.Thickness_Imagemap=CurrentSurfaceImageMap; CurrentRefraction.Thickness_Imagemap.Imagemap=0; CurrentRefraction.Thickness_Imagemap.MinValue=0.0; CurrentRefraction.Thickness_Imagemap.MaxValue=0.2; CurrentRefraction.Thickness_Imagemap.FitToRange=0; CurrentRefraction.Thickness_Imagemap.SkipZero=1; CurrentRefraction.IOR_Toggles[0]=1; CurrentRefraction.IOR_Toggles[1]=0; CurrentRefraction.IOR_Toggles[2]=0; CurrentRefraction.Thickness_Toggles[0]=0; CurrentRefraction.Thickness_Toggles[1]=0; CurrentRefraction.Thickness_Toggles[2]=1; CurrentSurfaceWave.xcenter = (double) gimp_image_width(input) / 2.0; CurrentSurfaceWave.ycenter = (double) gimp_image_height(input) / 2.0; CurrentSurfaceWave.xwavelength = (double) gimp_image_width(input) / 4.0; CurrentSurfaceWave.ywavelength = CurrentSurfaceWave.xwavelength; CurrentSurfaceWave.amplitude = 1.0; CurrentSurfaceWave.phase = 0; CurrentLayerIORWave=CurrentSurfaceWave; CurrentLayerThicknessWave=CurrentSurfaceWave; } /****************/ /* Main section */ /****************/ /***************************************/ /* Directional and point light variant */ /***************************************/ 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 */ /* ====================================== */ L=*light; if (type==POINT_LIGHT) SubVector(&L,&L,pos); else L.z*=-1.0; Normalize(&L); NL=InnerProduct(&Normal,&L); if (NL>=0.0 || TwoSidedShading==1) { /* Compute (R*V)^alpha term of Phong's equation */ /* ============================================ */ NL=2.0*NL; SubVector(&V,viewpoint,pos); Normalize(&V); MulVector(&Normal,NL); SubVector(&NN,&Normal,&L); RV=InnerProduct(&NN,&V); if (TwoSidedShading==0) { if (RV<0.0) RV=0.0; else RV=pow(RV,CurrentMaterial.Highlight); } else 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); } /**********************/ /* Spot light variant */ /**********************/ RGBPixel PhongShade2(Vector *pos,Vector *viewpoint,Vector *normal,Vector *lightpos,Vector *lightdir, RGBPixel *diff_col,RGBPixel *spec_col) { static RGBPixel ambientcolor,diffusecolor,specularcolor; static double NL,IP,ang; static Vector L,Normal,PL; /* Check if we're in the cone of light */ /* =================================== */ SubVector(&PL,pos,lightpos); Normalize(&PL); Normalize(lightdir); IP=InnerProduct(&PL,lightdir); ang=acos(IP); if (ang>DegToRad(CurrentSpotLight.Angle)) return(*diff_col); /* Compute ambient intensity */ /* ========================= */ ambientcolor=*diff_col; RGBMul(&ambientcolor,CurrentMaterial.AmbientInt); IP=pow(IP,CurrentMaterial.Highlight); Normal=*normal; /* Compute (N*L) term of Phong's equation */ /* ====================================== */ L=*lightpos; SubVector(&L,&L,pos); Normalize(&L); NL=2.0*InnerProduct(&Normal,&L); if (NL>=0.0 || TwoSidedShading==1) { /* Compute ambient, diffuse and specular intensity contribution */ /* ============================================================ */ ambientcolor=*diff_col; RGBMul(&ambientcolor,CurrentMaterial.AmbientInt); diffusecolor=*diff_col; RGBMul(&diffusecolor,CurrentMaterial.DiffuseRef); RGBMul(&diffusecolor,NL); specularcolor=*spec_col; RGBMul(&specularcolor,CurrentMaterial.SpecularRef); RGBMul(&specularcolor,IP); 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); } 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); } Vector IntToPos(int x,int y) { Vector pos; pos.x=(double)x/(double)width; pos.y=(double)y/(double)height; pos.z=CurrentRefraction.Thickness; 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); } void RGBToGray(Image image,unsigned char **map) { unsigned char *data,dval,*themap; int w,h; RGBPixel color; double val; long int maxc,cnt,index; data=gimp_image_data(image); w=gimp_image_width(image); h=gimp_image_height(image); maxc=(long int)w*(long int)h; themap=(unsigned char *)malloc((size_t)maxc*sizeof(unsigned char)); AddToCleanupStack((void *)themap); for (cnt=0;cntMaxValue-MapSettings->MinValue)/255.0)*cval+MapSettings->MinValue; } double GetIOR(int x,int y,unsigned char *map,ImageMapSettings *MapSettings) { return(GetThickness(x,y,map,MapSettings)); } /***********************************************************/ /* Compute a line of height and put it in the given buffer */ /***********************************************************/ void HeightAtLine(int ypos,double *buffer,unsigned char *map,ImageMapSettings *MapSettings) { int cnt; for (cnt=0;cnt based on */ /* the map given. */ /***********************************************/ void GetNormal(int x,int y,unsigned char *map,ImageMapSettings *MapSettings,Vector *normal) { Vector v1,v2,N; int numvecs=0; double val,val1=-1.0,val2=-1.0,val3=-1.0,val4=-1.0; /* Compute surface normal */ /* ====================== */ val=HeightBuffer[1][x]; if (CHECKBOUNDS(x-1,y)) { val1=HeightBuffer[1][x-1]-val; } if (CHECKBOUNDS(x,y-1)) { val2=HeightBuffer[0][x]-val; } if (CHECKBOUNDS(x+1,y)) { val3=HeightBuffer[1][x+1]-val; } if (CHECKBOUNDS(x,y+1)) { val4=HeightBuffer[2][x]-val; } *normal=ZeroVec; if (val1!=-1.0 && val4!=-1.0) { v1.x=nxstep; v1.y=0.0; v1.z=val1; v2.x=0.0; v2.y=ystep; v2.z=val4; N=CrossProduct(&v1,&v2); Normalize(&N); if (N.z<0.0) N.z=-1.0*N.z; AddVector(normal,normal,&N); numvecs++; } if (val1!=-1.0 && val2!=-1.0) { v1.x=nxstep; v1.y=0.0; v1.z=val1; v2.x=0.0; v2.y=nystep; v2.z=val2; N=CrossProduct(&v1,&v2); Normalize(&N); if (N.z<0.0) N.z=-1.0*N.z; AddVector(normal,normal,&N); numvecs++; } if (val2!=-1.0 && val3!=-1.0) { v1.x=0.0; v1.y=nystep; v1.z=val2; v2.x=xstep; v2.y=0.0; v2.z=val3; N=CrossProduct(&v1,&v2); Normalize(&N); if (N.z<0.0) N.z=-1.0*N.z; AddVector(normal,normal,&N); numvecs++; } if (val3!=-1.0 && val4!=-1.0) { v1.x=xstep; v1.y=0.0; v1.z=val3; v2.x=0.0; v2.y=ystep; v2.z=val4; N=CrossProduct(&v1,&v2); Normalize(&N); if (N.z<0.0) N.z=-1.0*N.z; AddVector(normal,normal,&N); numvecs++; } MulVector(normal,1.0/(double)numvecs); Normalize(normal); } /*****************************************************/ /* Compute refracted ray direction. Uses Snells law. */ /*****************************************************/ Vector Refract(Vector *pos,Vector *viewpoint,Vector *normal) { double NI,NInr,a,nr; Vector ray,I; SubVector(&I,viewpoint,pos); nr=CurrentRefraction.IOR_Air/CurrentRefraction.IOR_Material; NI=InnerProduct(normal,&I); NInr=NI*nr; a=NInr-(double)sqrt(1.0-(nr*nr)*(1.0-NI*NI)); ray=*normal; MulVector(&ray,a); MulVector(&I,nr); SubVector(&ray,&ray,&I); Normalize(&ray); return(ray); } 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)); } /*******************************************************************/ /* This routine computes the color of the surface at a given point */ /* (no refraction here). It's a part of the larger Shade() below. */ /*******************************************************************/ void ShadePictureFlat(int x,int y,RGBPixel *color) { Vector pos; int LightType=GetLightType(); /* Get the unaltered color at this point */ /* ===================================== */ color->r=((double)*inptr)/255.0; inptr++; color->g=((double)*inptr)/255.0; inptr++; color->b=((double)*inptr)/255.0; inptr++; /* Yes! (This is gonna be fun) Do normal stuff; */ /* compute normal vector at this point. */ /* ============================================ */ pos=IntToPos(x,y); pos.z=0.0; /* Compute color using Phong's illumination equation */ /* ================================================= */ if (LightType==DIRECTIONAL_LIGHT) *color=PhongShade(&pos,&ViewPoint,&Unit_Z,&CurrentDirectionalLight.Direction, color,&CurrentDirectionalLight.Color,LightType); else if (LightType==POINT_LIGHT) *color=PhongShade(&pos,&ViewPoint,&Unit_Z,&CurrentPointLight.Position, color,&CurrentPointLight.Color,LightType); else *color=PhongShade2(&pos,&ViewPoint,&Unit_Z,&CurrentSpotLight.Position, &CurrentSpotLight.Direction,color,&CurrentSpotLight.Color); RGBClamp(color); } void ShadePictureMapped(int x,int y,RGBPixel *color) { Vector normal,pos; int LightType=GetLightType(); /* Get the unaltered color at this point */ /* ===================================== */ color->r=((double)*inptr)/255.0; inptr++; color->g=((double)*inptr)/255.0; inptr++; color->b=((double)*inptr)/255.0; inptr++; /* Yes! (This is gonna be fun) Do normal stuff; */ /* compute normal vector at this point. */ /* ============================================ */ pos=IntToPos(x,y); GetNormal(x,y,SurfaceMap,&CurrentSurfaceImageMap,&normal); pos.z=HeightBuffer[1][x]; /* Compute color using Phong's illumination equation */ /* ================================================= */ if (LightType==DIRECTIONAL_LIGHT) *color=PhongShade(&pos,&ViewPoint,&normal,&CurrentDirectionalLight.Direction, color,&CurrentDirectionalLight.Color,LightType); else if (LightType==POINT_LIGHT) *color=PhongShade(&pos,&ViewPoint,&normal,&CurrentPointLight.Position, color,&CurrentPointLight.Color,LightType); else *color=PhongShade2(&pos,&ViewPoint,&normal,&CurrentSpotLight.Position, &CurrentSpotLight.Direction,color,&CurrentSpotLight.Color); RGBClamp(color); } RGBPixel ShadePictureInterpolate(Vector *pos) { Vector normal; RGBPixel color; int SurfaceType=GetSurfaceType(),LightType=GetLightType(),x,y; /* Get the unaltered, possibly interpolated, color at this point */ /* ============================================================= */ color=GetImageColor(pos->x,pos->y); /* Check if we're supposed to apply lighting here.. */ /* ================================================ */ if (apply_light_toggles[0]==0) return(color); /* Yes! (This is gonna be fun) Do normal stuff; */ /* compute normal vector at this point. */ /* ============================================ */ pos->z=0.0; if (SurfaceType!=MAPPED_CONSTANT) { PosToInt(pos->x,pos->y,&x,&y); GetNormal(x,y,SurfaceMap,&CurrentSurfaceImageMap,&normal); pos->z=GetThickness(x,y,SurfaceMap,&CurrentSurfaceImageMap); if (CurrentSurfaceImageMap.SkipZero==1 && pos->z<=0.0) return(color); } else normal=Unit_Z; /* Compute color using Phong's illumination equation */ /* ================================================= */ if (LightType==DIRECTIONAL_LIGHT) color=PhongShade(pos,&ViewPoint,&normal,&CurrentDirectionalLight.Direction, &color,&CurrentDirectionalLight.Color,LightType); else if (LightType==POINT_LIGHT) color=PhongShade(pos,&ViewPoint,&normal,&CurrentPointLight.Position, &color,&CurrentPointLight.Color,LightType); else color=PhongShade2(pos,&ViewPoint,&normal,&CurrentSpotLight.Position, &CurrentSpotLight.Direction, &color,&CurrentSpotLight.Color); RGBClamp(&color); return(color); } /***************************************************************/ /* Check if a line starting at 'pos' in the direction of 'dir' */ /* will hit our rectangle at (0.0 .. 1.0,0.0 .. 1.0). If yes, */ /* return the color at that point. If not, return the back- */ /* ground color. */ /***************************************************************/ RGBPixel GetRayColor(Vector *pos,Vector *dir) { double t; Vector newpos; t=pos->z/dir->z; newpos.x=pos->x-t*dir->x; newpos.y=pos->y-t*dir->y; newpos.z=pos->z-t*dir->z; /* Should evaluate to zero */ /* Ok, now we have the intersection point. Check them against our */ /* rectangle and then decide what to do with the color. */ /* ============================================================== */ if (newpos.x>1.0 || newpos.x<0.0 || newpos.y>1.0 || newpos.y<0.0) { /* Outside, so return background color */ /* =================================== */ return(BackGround); } /* Yay, touchdown! Calculate surface color */ /* ======================================= */ return(ShadePictureInterpolate(&newpos)); } /**************************************************************/ /* This calculates the color at a given position, taking into */ /* account mappings, light, refraction and the lot. This is */ /* actually a quasi-raytracer. */ /**************************************************************/ RGBPixel ShadeRefractive(int x,int y) { RGBPixel color,color2; Vector normal,ray,pos; int ThicknessMapping=GetThicknessMapping(),LightType=GetLightType(); double Thickness; /* Check if we're supposed to do anything with the position (thickness) */ /* ==================================================================== */ if (ThicknessMapping==MAPPED_CONSTANT) Thickness=CurrentRefraction.Thickness; else Thickness=GetThickness(x,y,LayerThicknessMap,&CurrentRefraction.Thickness_Imagemap); /* If thickness is zero and the "skip on zero" flag is set, skip the whole thing */ /* ============================================================================= */ pos=IntToPos(x,y); if (CurrentRefraction.Thickness_Imagemap.SkipZero==1 && Thickness<=0.0) color=ShadePictureInterpolate(&pos); else { /* Compute normal at this point on the layer */ /* ========================================= */ if (ThicknessMapping==MAPPED_CONSTANT) normal=Unit_Z; else GetNormal(x,y,LayerThicknessMap,&CurrentRefraction.Thickness_Imagemap,&normal); /* Now, do the same check with the IOR */ /* =================================== */ if (GetIORMapping()!=MAPPED_CONSTANT) CurrentRefraction.IOR_Material=GetIOR(x,y,LayerIORMap,&CurrentRefraction.IOR_Imagemap); /* Check if we're supposed to compute contribution from the light */ /* ============================================================== */ pos.z=Thickness; if (apply_light_toggles[1]==1) { /* Compute the color at this point on the layer */ /* ============================================ */ if (LightType==DIRECTIONAL_LIGHT) color=PhongShade(&pos,&ViewPoint,&normal,&CurrentDirectionalLight.Direction, &CurrentRefMaterial.Color,&CurrentDirectionalLight.Color,LightType); else if (LightType==POINT_LIGHT) color=PhongShade(&pos,&ViewPoint,&normal,&CurrentPointLight.Position, &CurrentRefMaterial.Color,&CurrentPointLight.Color,LightType); else color=PhongShade2(&pos,&ViewPoint,&normal,&CurrentSpotLight.Position, &CurrentSpotLight.Direction,&color,&CurrentSpotLight.Color); RGBClamp(&color); } else color=CurrentRefMaterial.Color; /* Ok, ready to compute refracted ray */ /* ================================== */ ray=Refract(&pos,&ViewPoint,&normal); /* Now, get the color in the direction of the refracted ray */ /* If the ray hits the "picture" return the color at that */ /* point. If not, return the current background color. */ /* ======================================================== */ color2=GetRayColor(&pos,&ray); /* Scale final color */ /* ================= */ color.r*=color2.r; color.g*=color2.g; color.b*=color2.b; } return(color); } /****************************************************/ /* Compute cool wavy surface (z=cos(sqrt(x^2+y^2))) */ /****************************************************/ void ComputeWaves(WaveSettings *wave,unsigned char **DispMap) { int x, y; double xwl, ywl; /* Wavelengths */ double dx, dy; /* Distance to center of wave */ double xscale, yscale; /* Scaling factors for 'circlifying' wave */ double d; double val; double wl; xwl = fabs(wave->xwavelength); ywl = fabs(wave->ywavelength); if (xwl < EPSILON) xwl = EPSILON; if (ywl < EPSILON) ywl = EPSILON; if (xwl < ywl) { xscale = ywl / xwl; yscale = 1.0; wl = ywl; } else if (xwl > ywl) { xscale = 1.0; yscale = xwl / ywl; wl = xwl; } else { xscale = 1.0; yscale = 1.0; wl = xwl; } /* else */ *DispMap=(unsigned char *)malloc(maxcounter*sizeof(unsigned char)); AddToCleanupStack((void *)*DispMap); for (y = 0; y < height; y++) for (x = 0; x < width; x++) { dx = (x - wave->xcenter) * xscale; dy = (y - wave->ycenter) * yscale; d = sqrt(dx * dx + dy * dy); val = wave->amplitude * cos((2.0 * M_PI) * (d / wl + wave->phase)); PokeMap(*DispMap, x, y, (unsigned char) ((val + 1.0) * 127.5)); } /* for */ } /* ComputeWaves */ /************************************************************/ /* Check what mode(s) we're in and compute maps for surface */ /* and refractive layer (if necessary). */ /************************************************************/ void ComputeMaps() { int x,SurfaceType=GetSurfaceType(),IORMapping,ThicknessMapping; double val,c,d; /* Compute Sine, Log ans Spherical transfer function maps */ /* ====================================================== */ c=1.0/255.0; d=1.15*255.0; for (x=0;x<256;x++) { SineMap[x]=(unsigned char)(255.0*(0.5*(sin((M_PI*c*(double)x)-0.5*M_PI)+1.0))); SphereMap[x]=(unsigned char)(255.0*(sqrt(sin(M_PI*(double)x/512.0)))); val=(d*exp(-1.0/(8.0*c*((double)x+5.0)))); if (val>255.0) val=255.0; LogMap[x]=(unsigned char)val; } /* Check out what kind of surface we have defined for the image */ /* ============================================================ */ if (SurfaceType!=MAPPED_CONSTANT) { if (SurfaceType==MAPPED_WAVE) ComputeWaves(&CurrentSurfaceWave,&SurfaceMap); else { CurrentSurfaceImageMap.TheImage=gimp_get_input_image(CurrentSurfaceImageMap.Imagemap); if (gimp_image_type(CurrentSurfaceImageMap.TheImage)==RGB_IMAGE) RGBToGray(CurrentSurfaceImageMap.TheImage,&SurfaceMap); else SurfaceMap=gimp_image_data(CurrentSurfaceImageMap.TheImage); } } /* Now, if we have enabled refraction we have to do some stuff */ /* =========================================================== */ if (GetRefractionMode()==REFRACTION_ENABLED) { /* Check out the IOR settings */ /* ========================== */ IORMapping=GetIORMapping(); if (IORMapping!=MAPPED_CONSTANT) { if (IORMapping==MAPPED_WAVE) ComputeWaves(&CurrentLayerIORWave,&LayerIORMap); else { CurrentRefraction.IOR_Imagemap.TheImage=gimp_get_input_image(CurrentRefraction.IOR_Imagemap.Imagemap); if (gimp_image_type(CurrentRefraction.IOR_Imagemap.TheImage)==RGB_IMAGE) RGBToGray(CurrentRefraction.IOR_Imagemap.TheImage,&LayerIORMap); else LayerIORMap=gimp_image_data(CurrentRefraction.IOR_Imagemap.TheImage); } } /* Check out the thickness settings */ /* ================================ */ ThicknessMapping=GetThicknessMapping(); if (ThicknessMapping!=MAPPED_CONSTANT) { if (ThicknessMapping==MAPPED_WAVE) ComputeWaves(&CurrentLayerThicknessWave,&LayerThicknessMap); else { CurrentRefraction.Thickness_Imagemap.TheImage= gimp_get_input_image(CurrentRefraction.Thickness_Imagemap.Imagemap); if (gimp_image_type(CurrentRefraction.Thickness_Imagemap.TheImage)==RGB_IMAGE) RGBToGray(CurrentRefraction.Thickness_Imagemap.TheImage,&LayerThicknessMap); else LayerThicknessMap=gimp_image_data(CurrentRefraction.Thickness_Imagemap.TheImage); } } } /* Allocate memory to hold our normal and height buffers */ /* ===================================================== */ NormalBuffer[0]=(Vector *)malloc((size_t)width*sizeof(Vector)); NormalBuffer[1]=(Vector *)malloc((size_t)width*sizeof(Vector)); NormalBuffer[2]=(Vector *)malloc((size_t)width*sizeof(Vector)); HeightBuffer[0]=(double *)malloc((size_t)width*sizeof(double)); HeightBuffer[1]=(double *)malloc((size_t)width*sizeof(double)); HeightBuffer[2]=(double *)malloc((size_t)width*sizeof(double)); AddToCleanupStack((void *)NormalBuffer[0]); AddToCleanupStack((void *)NormalBuffer[1]); AddToCleanupStack((void *)NormalBuffer[2]); AddToCleanupStack((void *)HeightBuffer[0]); AddToCleanupStack((void *)HeightBuffer[1]); AddToCleanupStack((void *)HeightBuffer[2]); } /**************/ /* Main loops */ /**************/ void ComputeLightingMapped(void) { int xcount,ycount; long counter=0; double *temp; RGBPixel color; /* Set up the height field cache */ /* ============================= */ HeightAtLine(0,HeightBuffer[0],SurfaceMap,&CurrentSurfaceImageMap); HeightAtLine(0,HeightBuffer[1],SurfaceMap,&CurrentSurfaceImageMap); HeightAtLine(1,HeightBuffer[2],SurfaceMap,&CurrentSurfaceImageMap); /* If we can skip zero-heights, we can save time */ /* ============================================= */ if (CurrentSurfaceImageMap.SkipZero==1) { for (ycount=0;ycount0.0) { ShadePictureMapped(xcount,ycount,&color); *outptr=(unsigned char)(255.0*color.r); outptr++; *outptr=(unsigned char)(255.0*color.g); outptr++; *outptr=(unsigned char)(255.0*color.b); outptr++; } else { *outptr++=*inptr++; *outptr++=*inptr++; *outptr++=*inptr++; } counter++; if ((counter % width)==0) gimp_do_progress(counter,maxcounter); } temp=HeightBuffer[0]; HeightBuffer[0]=HeightBuffer[1]; HeightBuffer[1]=HeightBuffer[2]; HeightBuffer[2]=temp; if (ycountImagemap); DummyID = gimp_new_column_group(ImageMapDialogID,MainGroupID, NORMAL, ""); gimp_new_label(ImageMapDialogID, DummyID, "MinValue:"); sprintf(buf, "%f", map->MinValue); ValID = gimp_new_text(ImageMapDialogID,DummyID,buf); gimp_add_callback(ImageMapDialogID, ValID, double_callback, &map->MinValue); DummyID = gimp_new_column_group(ImageMapDialogID,MainGroupID, NORMAL, ""); gimp_new_label(ImageMapDialogID, DummyID, "MaxValue:"); sprintf(buf, "%f", map->MaxValue); ValID = gimp_new_text(ImageMapDialogID,DummyID,buf); gimp_add_callback(ImageMapDialogID, ValID, double_callback, &map->MaxValue); FrameID = gimp_new_frame (ImageMapDialogID, MainGroupID, "Map function"); RowID = gimp_new_row_group(ImageMapDialogID,FrameID, RADIO, ""); LinearID = gimp_new_radio_button (ImageMapDialogID, RowID, "Linear"); gimp_change_item (ImageMapDialogID, LinearID, sizeof(long), &map->MapToggles[0]); gimp_add_callback (ImageMapDialogID, LinearID, radio_callback, &map->MapToggles[0]); LogID = gimp_new_radio_button (ImageMapDialogID, RowID, "Logarithmic"); gimp_change_item (ImageMapDialogID, LogID, sizeof(long), &map->MapToggles[1]); gimp_add_callback (ImageMapDialogID, LogID, radio_callback, &map->MapToggles[1]); SineID = gimp_new_radio_button (ImageMapDialogID, RowID, "Sinusoidal"); gimp_change_item (ImageMapDialogID, SineID, sizeof(long), &map->MapToggles[2]); gimp_add_callback (ImageMapDialogID, SineID, radio_callback, &map->MapToggles[2]); SphereID = gimp_new_radio_button (ImageMapDialogID, RowID, "Spherical"); gimp_change_item (ImageMapDialogID, SphereID, sizeof(long), &map->MapToggles[3]); gimp_add_callback (ImageMapDialogID, SphereID, radio_callback, &map->MapToggles[3]); FitToggleID = gimp_new_check_button(ImageMapDialogID, MainGroupID, "Autostretch to fit value range"); gimp_change_item (ImageMapDialogID, FitToggleID, sizeof(long), &map->FitToRange); gimp_add_callback (ImageMapDialogID, FitToggleID, radio_callback, &map->FitToRange); ZeroSkipID = gimp_new_check_button(ImageMapDialogID, MainGroupID, "Skip shading at zero height"); gimp_change_item (ImageMapDialogID, ZeroSkipID, sizeof(long), &map->SkipZero); gimp_add_callback (ImageMapDialogID, ZeroSkipID, radio_callback, &map->SkipZero); /* Other callbacks */ /* =============== */ gimp_add_callback(ImageMapDialogID, gimp_ok_item_id(ImageMapDialogID), image_map_ok_callback, NULL); gimp_add_callback(ImageMapDialogID, gimp_cancel_item_id(ImageMapDialogID), image_map_cancel_callback, NULL); } void CreateRefractionDialog(RefractionSettings *ref) { int MainGroupID,FrameID,FrameGroupID,ValID,DummyID,RowID; int ConstantID,MappedID,MapSettingsID,WaveID,MaterialID; char buf[100]; RefractionDialogID = gimp_new_dialog("Refraction settings"); MainGroupID = gimp_new_row_group(RefractionDialogID, DEFAULT, NORMAL, ""); MaterialID = gimp_new_push_button(RefractionDialogID,MainGroupID,"Material settings.."); gimp_add_callback (RefractionDialogID, MaterialID, RefractionMaterial_callback, NULL); FrameID = gimp_new_frame (RefractionDialogID, MainGroupID, "Index of refraction"); FrameGroupID = gimp_new_row_group(RefractionDialogID, FrameID, NORMAL, ""); DummyID = gimp_new_column_group(RefractionDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(RefractionDialogID, DummyID, "IOR Air:"); sprintf(buf, "%f", ref->IOR_Air); ValID = gimp_new_text(RefractionDialogID,DummyID,buf); gimp_add_callback(RefractionDialogID, ValID, double_callback, &ref->IOR_Air); DummyID = gimp_new_column_group(RefractionDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(RefractionDialogID, DummyID, "IOR Layer:"); sprintf(buf, "%f", ref->IOR_Material); ValID = gimp_new_text(RefractionDialogID,DummyID,buf); gimp_add_callback(RefractionDialogID, ValID, double_callback, &ref->IOR_Material); RowID = gimp_new_row_group(RefractionDialogID,FrameGroupID, RADIO, ""); ConstantID = gimp_new_radio_button (RefractionDialogID, RowID, "Uniform mapping"); gimp_change_item (RefractionDialogID, ConstantID, sizeof(long), &ref->IOR_Toggles[0]); gimp_add_callback (RefractionDialogID, ConstantID, radio_callback, &ref->IOR_Toggles[0]); WaveID = gimp_new_radio_button (RefractionDialogID, RowID, "Wavy mapping"); gimp_change_item (RefractionDialogID, WaveID, sizeof(long), &ref->IOR_Toggles[1]); gimp_add_callback (RefractionDialogID, WaveID, radio_callback, &ref->IOR_Toggles[1]); MappedID = gimp_new_radio_button (RefractionDialogID, RowID, "Mapped from image"); gimp_change_item (RefractionDialogID, MappedID, sizeof(long), &ref->IOR_Toggles[2]); gimp_add_callback (RefractionDialogID, MappedID, radio_callback, &ref->IOR_Toggles[2]); MapSettingsID = gimp_new_push_button(RefractionDialogID,FrameGroupID,"Map settings.."); gimp_add_callback (RefractionDialogID, MapSettingsID, IOR_map_callback, NULL); FrameID = gimp_new_frame (RefractionDialogID, MainGroupID, "Layer thickness"); FrameGroupID = gimp_new_row_group(RefractionDialogID, FrameID, NORMAL, ""); DummyID = gimp_new_column_group(RefractionDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(RefractionDialogID, DummyID, "Default thickness:"); sprintf(buf, "%f", ref->Thickness); ValID = gimp_new_text(RefractionDialogID,DummyID,buf); gimp_add_callback(RefractionDialogID, ValID, double_callback, &ref->Thickness); RowID = gimp_new_row_group(RefractionDialogID,FrameGroupID, RADIO, ""); ConstantID = gimp_new_radio_button (RefractionDialogID, RowID, "Uniform mapping"); gimp_change_item (RefractionDialogID, ConstantID, sizeof(long), &ref->Thickness_Toggles[0]); gimp_add_callback (RefractionDialogID, ConstantID, radio_callback, &ref->Thickness_Toggles[0]); WaveID = gimp_new_radio_button (RefractionDialogID, RowID, "Wavy mapping"); gimp_change_item (RefractionDialogID, WaveID, sizeof(long), &ref->Thickness_Toggles[1]); gimp_add_callback (RefractionDialogID, WaveID, radio_callback, &ref->Thickness_Toggles[1]); MappedID = gimp_new_radio_button (RefractionDialogID, RowID, "Mapped from image"); gimp_change_item (RefractionDialogID, MappedID, sizeof(long), &ref->Thickness_Toggles[2]); gimp_add_callback (RefractionDialogID, MappedID, radio_callback, &ref->Thickness_Toggles[2]); MapSettingsID = gimp_new_push_button(RefractionDialogID,FrameGroupID,"Map settings.."); gimp_add_callback (RefractionDialogID, MapSettingsID, thickness_map_callback, NULL); /* Other callbacks */ /* =============== */ gimp_add_callback(RefractionDialogID, gimp_ok_item_id(RefractionDialogID), refraction_ok_callback, NULL); gimp_add_callback(RefractionDialogID, gimp_cancel_item_id(RefractionDialogID), refraction_cancel_callback, NULL); } void CreateWaveDialog(WaveSettings *wave) { int main_group_id, frame_id, group_id, value_id, column_id; char buf[100]; WaveDialogID = gimp_new_dialog("Wave settings"); main_group_id = gimp_new_row_group(WaveDialogID, DEFAULT, NORMAL, ""); /* Center of wave */ /* ============== */ frame_id = gimp_new_frame(WaveDialogID, main_group_id, "Center of wave"); group_id = gimp_new_row_group(WaveDialogID, frame_id, NORMAL, ""); column_id = gimp_new_column_group(WaveDialogID, group_id, NORMAL, ""); gimp_new_label(WaveDialogID, column_id, "X:"); sprintf(buf, "%f", wave->xcenter); value_id = gimp_new_text(WaveDialogID, column_id, buf); gimp_add_callback(WaveDialogID, value_id, double_callback, &wave->xcenter); column_id = gimp_new_column_group(WaveDialogID, group_id, NORMAL, ""); gimp_new_label(WaveDialogID, column_id, "Y:"); sprintf(buf, "%f", wave->ycenter); value_id = gimp_new_text(WaveDialogID, column_id, buf); gimp_add_callback(WaveDialogID, value_id, double_callback, &wave->ycenter); /* Wavelength */ /* ========== */ frame_id = gimp_new_frame(WaveDialogID, main_group_id, "Wavelength"); group_id = gimp_new_row_group(WaveDialogID, frame_id, NORMAL, ""); column_id = gimp_new_column_group(WaveDialogID, group_id, NORMAL, ""); gimp_new_label(WaveDialogID, column_id, "X:"); sprintf(buf, "%f", wave->xwavelength); value_id = gimp_new_text(WaveDialogID, column_id, buf); gimp_add_callback(WaveDialogID, value_id, double_callback, &wave->xwavelength); column_id = gimp_new_column_group(WaveDialogID, group_id, NORMAL, ""); gimp_new_label(WaveDialogID, column_id, "Y:"); sprintf(buf, "%f", wave->ywavelength); value_id = gimp_new_text(WaveDialogID, column_id, buf); gimp_add_callback(WaveDialogID, value_id, double_callback, &wave->ywavelength); /* Amplitude and phase */ /* =================== */ frame_id = gimp_new_frame(WaveDialogID, main_group_id, ""); group_id = gimp_new_row_group(WaveDialogID, frame_id, NORMAL, ""); gimp_new_label(WaveDialogID, group_id, "Amplitude:"); value_id = gimp_new_scale(WaveDialogID, group_id, 0, 100, (long) (wave->amplitude * 100.0), 2); gimp_add_callback(WaveDialogID, value_id, wave_scale_callback, &wave->amplitude); gimp_new_label(WaveDialogID, group_id, "Phase:"); value_id = gimp_new_scale(WaveDialogID, group_id, 0, 100, (long) (wave->phase * 100.0), 2); gimp_add_callback(WaveDialogID, value_id, wave_scale_callback, &wave->phase); /* Buttons */ /* ======= */ gimp_add_callback(WaveDialogID, gimp_ok_item_id(WaveDialogID), wave_ok_callback, NULL); gimp_add_callback(WaveDialogID, gimp_cancel_item_id(WaveDialogID), wave_cancel_callback, NULL); } void CreateMaterialDialog(MaterialSettings *material, int mode) { int MainGroupID,FrameID,FrameGroupID,ValID,DummyID,ShadingID; char buf[100]; MaterialDialogID = gimp_new_dialog("Material settings"); MainGroupID = gimp_new_row_group(MaterialDialogID, DEFAULT, NORMAL, ""); ShadingID = gimp_new_check_button(MaterialDialogID, MainGroupID, "Two-sided shading"); gimp_change_item (MaterialDialogID, ShadingID, sizeof(long), &TwoSidedShading); gimp_add_callback (MaterialDialogID, ShadingID, radio_callback, &TwoSidedShading); FrameID = gimp_new_frame (MaterialDialogID, MainGroupID, "Intensity levels"); FrameGroupID = gimp_new_row_group(MaterialDialogID, FrameID, NORMAL, ""); DummyID = gimp_new_column_group(MaterialDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(MaterialDialogID, DummyID, "Ambient:"); sprintf(buf, "%f", material->AmbientInt); 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); if (mode==WITH_COLOR) { /* Add a color button to the dialog */ /* ================================ */ DummyID = gimp_new_push_button(MaterialDialogID,MainGroupID,"Material color.."); gimp_add_callback (MaterialDialogID, DummyID, material_color_callback, &material->Color); } /* 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 CreateSpotLightDialog(LightSettings *light) { int MainGroupID,FrameID,FrameGroupID,XDValID,YDValID,ZDValID,DummyID; int XPValID,YPValID,ZPValID,AngleID; char buf[100]; SpotLightDialogID = gimp_new_dialog("Spot light settings"); MainGroupID = gimp_new_row_group(SpotLightDialogID, DEFAULT, NORMAL, ""); FrameID = gimp_new_frame (SpotLightDialogID, MainGroupID, "Direction vector"); FrameGroupID = gimp_new_row_group(SpotLightDialogID, FrameID, NORMAL, ""); DummyID = gimp_new_column_group(SpotLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(SpotLightDialogID, DummyID, "X:"); sprintf(buf, "%f", light->Direction.x); XDValID = gimp_new_text(SpotLightDialogID,DummyID,buf); DummyID = gimp_new_column_group(SpotLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(SpotLightDialogID, DummyID, "Y:"); sprintf(buf, "%f", light->Direction.y); YDValID = gimp_new_text(SpotLightDialogID,DummyID,buf); DummyID = gimp_new_column_group(SpotLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(SpotLightDialogID, DummyID, "Z:"); sprintf(buf, "%f", light->Direction.z); ZDValID = gimp_new_text(SpotLightDialogID,DummyID,buf); FrameID = gimp_new_frame (SpotLightDialogID, MainGroupID, "Position"); FrameGroupID = gimp_new_row_group(SpotLightDialogID, FrameID, NORMAL, ""); DummyID = gimp_new_column_group(SpotLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(SpotLightDialogID, DummyID, "X:"); sprintf(buf, "%f", light->Position.x); XPValID = gimp_new_text(SpotLightDialogID,DummyID,buf); DummyID = gimp_new_column_group(SpotLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(SpotLightDialogID, DummyID, "Y:"); sprintf(buf, "%f", light->Position.y); YPValID = gimp_new_text(SpotLightDialogID,DummyID,buf); DummyID = gimp_new_column_group(SpotLightDialogID,FrameGroupID, NORMAL, ""); gimp_new_label(SpotLightDialogID, DummyID, "Z:"); sprintf(buf, "%f", light->Position.z); ZPValID = gimp_new_text(SpotLightDialogID,DummyID,buf); gimp_new_label(SpotLightDialogID, MainGroupID, "Spread angle:"); AngleID = gimp_new_scale (SpotLightDialogID, MainGroupID, 0, 180, light->Angle, 0); /* Callbacks */ /* ========= */ gimp_add_callback(SpotLightDialogID, XDValID, double_callback, &light->Direction.x); gimp_add_callback(SpotLightDialogID, YDValID, double_callback, &light->Direction.y); gimp_add_callback(SpotLightDialogID, ZDValID, double_callback, &light->Direction.z); gimp_add_callback(SpotLightDialogID, XPValID, double_callback, &light->Position.x); gimp_add_callback(SpotLightDialogID, YPValID, double_callback, &light->Position.y); gimp_add_callback(SpotLightDialogID, ZPValID, double_callback, &light->Position.z); gimp_add_callback (SpotLightDialogID, AngleID, angle_callback, &light->Angle); gimp_add_callback(SpotLightDialogID, gimp_ok_item_id(SpotLightDialogID), spotlight_ok_callback, NULL); gimp_add_callback(SpotLightDialogID, gimp_cancel_item_id(SpotLightDialogID), spotlight_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,SpotLightID; int SurfaceFrameID,SurfaceGroupID,FlatID,WavyID,MappedID,RefractionToggleID,NewImageID; int LightSettingsID,LightColorID,SurfaceSettingsID,RefractionSettingsID,RowID,ColID; int MainSurfaceGroupID,MappingSettingsID,RadioLightID,LightToggle1ID,LightToggle2ID; int SettingsFrameID,SettingsRowID; MainDialogID = gimp_new_dialog("Lighting Effects"); MainGroupID = gimp_new_row_group(MainDialogID, DEFAULT, NORMAL, ""); LightFrameID = gimp_new_frame (MainDialogID, MainGroupID, "Options"); ColID = gimp_new_column_group(MainDialogID, LightFrameID, NORMAL, ""); RowID = gimp_new_row_group(MainDialogID, ColID, NORMAL, ""); NewImageID = gimp_new_check_button(MainDialogID, RowID, "Overwrite source image"); RefractionToggleID = gimp_new_check_button(MainDialogID, RowID, "Use a refractive layer"); RowID = gimp_new_row_group(MainDialogID, ColID, NORMAL, ""); LightToggle1ID = gimp_new_check_button(MainDialogID, RowID, "Apply light to image"); LightToggle2ID = gimp_new_check_button(MainDialogID, RowID, "Apply light to layer"); ColID = gimp_new_column_group(MainDialogID, MainGroupID, NORMAL, ""); RowID = gimp_new_row_group(MainDialogID, ColID, NORMAL, ""); LightFrameID = gimp_new_frame (MainDialogID, RowID, "Light source"); LightGroupID = gimp_new_row_group(MainDialogID, LightFrameID, NORMAL, ""); RadioLightID = gimp_new_row_group(MainDialogID, LightGroupID, RADIO, ""); DirectionalLightID = gimp_new_radio_button (MainDialogID, RadioLightID, "Directional light"); PointLightID = gimp_new_radio_button (MainDialogID, RadioLightID, "Point light"); SpotLightID = gimp_new_radio_button (MainDialogID, RadioLightID, "Spot light"); RowID = gimp_new_row_group(MainDialogID, ColID, NORMAL, ""); SurfaceFrameID = gimp_new_frame (MainDialogID, RowID, "Image normal mapping"); MainSurfaceGroupID = gimp_new_column_group(MainDialogID, SurfaceFrameID, NORMAL, ""); SurfaceGroupID = gimp_new_row_group(MainDialogID, MainSurfaceGroupID, RADIO, ""); FlatID = gimp_new_radio_button (MainDialogID, SurfaceGroupID, "No mapping"); WavyID = gimp_new_radio_button (MainDialogID, SurfaceGroupID, "Wavy mapping"); MappedID = gimp_new_radio_button (MainDialogID, SurfaceGroupID, "Bump mapping"); SettingsFrameID = gimp_new_frame (MainDialogID, MainGroupID, "Settings"); SettingsRowID = gimp_new_row_group(MainDialogID, SettingsFrameID, NORMAL, ""); ColID = gimp_new_column_group(MainDialogID, SettingsRowID, NORMAL, ""); RowID = gimp_new_row_group(MainDialogID, ColID, NORMAL, ""); LightSettingsID = gimp_new_push_button(MainDialogID,RowID,"Lightsource settings.."); LightColorID = gimp_new_push_button(MainDialogID,RowID,"Lightsource color.."); RowID = gimp_new_row_group(MainDialogID, ColID, NORMAL, ""); MappingSettingsID = gimp_new_push_button(MainDialogID,RowID,"Image mapping settings.."); SurfaceSettingsID = gimp_new_push_button(MainDialogID,RowID,"Image material settings.."); RefractionSettingsID = gimp_new_push_button(MainDialogID,SettingsRowID,"Refraction settings.."); /* Default settings */ /* ================ */ gimp_change_item (MainDialogID, NewImageID, sizeof(long), &CreateNewImage); gimp_change_item (MainDialogID, RefractionToggleID, sizeof(long), &refractionmode); gimp_change_item (MainDialogID, DirectionalLightID, sizeof(light_toggles[1]), &light_toggles[0]); gimp_change_item (MainDialogID, PointLightID, sizeof(light_toggles[2]), &light_toggles[1]); gimp_change_item (MainDialogID, SpotLightID, sizeof(light_toggles[3]), &light_toggles[2]); gimp_change_item (MainDialogID, LightToggle1ID, sizeof(apply_light_toggles[0]), &apply_light_toggles[0]); gimp_change_item (MainDialogID, LightToggle2ID, sizeof(apply_light_toggles[1]), &apply_light_toggles[1]); gimp_change_item (MainDialogID, FlatID, sizeof(surface_toggles[0]), &surface_toggles[0]); gimp_change_item (MainDialogID, WavyID, sizeof(surface_toggles[1]), &surface_toggles[1]); gimp_change_item (MainDialogID, MappedID, sizeof(surface_toggles[2]), &surface_toggles[2]); /* Callbacks */ /* ========= */ gimp_add_callback (MainDialogID, NewImageID, radio_callback, &CreateNewImage); gimp_add_callback (MainDialogID, DirectionalLightID, radio_callback, &light_toggles[0]); gimp_add_callback (MainDialogID, PointLightID, radio_callback, &light_toggles[1]); gimp_add_callback (MainDialogID, SpotLightID, radio_callback, &light_toggles[2]); gimp_add_callback (MainDialogID, LightToggle1ID, radio_callback, &apply_light_toggles[0]); gimp_add_callback (MainDialogID, LightToggle2ID, radio_callback, &apply_light_toggles[1]); gimp_add_callback (MainDialogID, FlatID, radio_callback, &surface_toggles[0]); gimp_add_callback (MainDialogID, WavyID, radio_callback, &surface_toggles[1]); gimp_add_callback (MainDialogID, MappedID, radio_callback, &surface_toggles[2]); gimp_add_callback (MainDialogID, RefractionToggleID, radio_callback, &refractionmode); gimp_add_callback (MainDialogID, RefractionSettingsID, RefractionSettings_callback, NULL); gimp_add_callback (MainDialogID, LightSettingsID, LightSettings_callback, NULL); gimp_add_callback (MainDialogID, LightColorID, LightColor_callback, NULL); gimp_add_callback (MainDialogID, SurfaceSettingsID, SurfaceSettings_callback, NULL); gimp_add_callback (MainDialogID, MappingSettingsID, SurfaceMapping_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("Lighting Effects: 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); }