quakeforge/tools/texpaint/texturepaint.c

1542 lines
33 KiB
C
Raw Normal View History

/* Texture Paint
* Plug-in for the GIMP
*
* Copyright (C) 1998 Uwe Maurer <uwe_maurer@t-online.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "texturepaint.h"
#include "config.h"
#include "Pixmaps/begin.xpm"
#include "Pixmaps/end.xpm"
#include "Pixmaps/stop.xpm"
#include "Pixmaps/play.xpm"
Dialog *dialog;
int max_tex_size=64;
int red_bits,green_bits,blue_bits;
int red_shift,green_shift,blue_shift; // 8-red_bits
/* GIMP PLUGIN*/
/*******************************************************************************/
MAIN ()
static void query(void);
static void run(gchar *name,
gint nparams,
GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
GimpPlugInInfo PLUG_IN_INFO=
{
NULL,
NULL,
query,
run
};
/*******************************************************************************/
static int round_size(int size,gboolean floor)
{
static int s[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192};
int i;
if (size>=max_tex_size) return max_tex_size;
if (floor)
{
for (i=0;i<=12;i++)
{
if (s[i]==size) return size;
if (s[i]<size && size<s[i+1]) return s[i];
}
}
else
{
for (i=0;i<=12;i++)
{
if (s[i]==size) return size;
if (s[i]<size && size<s[i+1]) return s[i+1];
}
}
return max_tex_size;
}
static void set_idle(ModelInfo *mdl)
{
if (mdl->update_texture || mdl->playing)
{
if (mdl->idle<0)
mdl->idle=gtk_idle_add_priority(10,(GtkFunction)model_draw,mdl);
}
else
{
if (mdl->idle>=0)
{
gtk_idle_remove(mdl->idle);
mdl->idle=-1;
}
}
}
void set_parameter(ModelInfo *mdl)
{
GLint v;
v=(GTK_TOGGLE_BUTTON(dialog->linear)->active) ? GL_LINEAR : GL_NEAREST;
glBindTexture(GL_TEXTURE_2D, mdl->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, v);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, v);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
v=(GTK_TOGGLE_BUTTON(dialog->fastest)->active) ? GL_FASTEST : GL_NICEST;
glHint(GL_PERSPECTIVE_CORRECTION_HINT,v);
}
/* radiobuttons */
void on_button_toggled(GtkToggleButton *button,gpointer data)
{
if (dialog->mdl==NULL) return;
begingl(dialog->mdl->glarea);
set_parameter(dialog->mdl);
dialog->mdl->update_texture=GTK_TOGGLE_BUTTON(dialog->update)->active;
set_idle(dialog->mdl);
model_draw(dialog->mdl);
endgl(dialog->mdl->glarea);
}
void on_update_clicked(GtkButton *button,gpointer data)
{
ModelInfo *mdl;
if (data) mdl=(ModelInfo *)data;
else if (dialog->mdl) mdl=dialog->mdl;
else return;
if (get_texture(mdl)<0)
{
gimp_message("Select a correct texture first.");
}
model_draw(mdl);
}
static void init_special_texture(ModelInfo *mdl,int w,int h)
{
guchar *image;
int x,pos;
int red_mask,green_mask,blue_mask;
begingl(mdl->glarea);
red_mask= (1<<red_bits)-1;
green_mask= (1<<green_bits)-1;
blue_mask= (1<<blue_bits)-1;
image=g_malloc(max_tex_size*3);
pos=0;
for (x=1;x<=max_tex_size;x++)
{
image[pos++]=((x) & red_mask)<<red_shift;
image[pos++]=((x>>red_bits) & green_mask)<<green_shift;
image[pos++]=((x>>(red_bits+green_bits)) & blue_mask)<<blue_shift;
}
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glGenTextures(1,&mdl->texture_s);
glBindTexture(GL_TEXTURE_2D,mdl->texture_s);
glTexImage2D(GL_TEXTURE_2D,0,3,w,1,0,
GL_RGB,GL_UNSIGNED_BYTE,image);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glGenTextures(1,&mdl->texture_t);
glBindTexture(GL_TEXTURE_2D,mdl->texture_t);
glTexImage2D(GL_TEXTURE_2D,0,3,1,h,0,
GL_RGB,GL_UNSIGNED_BYTE,image);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
g_free(image);
endgl(mdl->glarea);
}
gint model_draw(ModelInfo *mdl)
{
gdouble frametime;
DEBUG("%s",mdl->name);
if (mdl->drawing)
{
return TRUE;
}
mdl->drawing=TRUE;
if (mdl->update_texture)
{
if (get_texture(mdl)<0)
{
mdl->update_texture=FALSE;
set_idle(mdl);
if (dialog->mdl==mdl)
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(dialog->update),0);
}
}
if (mdl->playing)
{
frametime=g_timer_elapsed(mdl->timer,NULL);
g_timer_start(mdl->timer);
if (mdl->last > mdl->first)
{
mdl->frame+=mdl->fps*frametime;
if (mdl->frame>(gdouble)mdl->last) mdl->frame=mdl->first;
if (mdl->frame<(gdouble)mdl->first) mdl->frame=mdl->first;
}
else if (mdl->last<mdl->first)
{
mdl->frame-=mdl->fps*frametime;
if (mdl->frame<(gdouble)mdl->last) mdl->frame=mdl->first;
if (mdl->frame>(gdouble)mdl->first) mdl->frame=mdl->first;
}
else
{
mdl->frame=mdl->first;
}
if (mdl==dialog->mdl)
{
gtk_adjustment_set_value(GTK_ADJUSTMENT(gtk_range_get_adjustment(GTK_RANGE(dialog->cur_frame))),mdl->frame);
}
}
display_model(mdl,DRAW_NORMAL);
mdl->drawing=FALSE;
return TRUE;
}
static void query(void)
{
static GimpParamDef args[]=
{
{GIMP_PDB_INT32,"run_mode","Interactive, non-interactive"},
};
gint nargs = sizeof(args) / sizeof(args[0]);
gimp_install_procedure("plug_in_texture_paint",
"Paint on a Quake 1/2 Model",
"",
"Uwe Maurer <uwe_maurer@t-online.de>",
"Uwe Maurer, Lionel Ulmer, Janne L<>f, Trey Harrison",
"1998",
"<Toolbox>/Xtns/Texture Paint",
"",
GIMP_EXTENSION,
nargs,0,
args,NULL);
}
int get_texture(ModelInfo *mdl)
{
guchar *tex;
gint w,h;
GimpPixelRgn rgn;
GimpDrawable *drawable;
gint32 drawable_id;
DEBUG("%s",mdl->name);
if (!check_texture(mdl->tex_image))
{
glDisable(GL_TEXTURE_2D);
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
return -1;
}
drawable_id=gimp_image_get_active_layer(mdl->tex_image);
drawable=gimp_drawable_get(drawable_id);
w=gimp_drawable_width(drawable->id);
h=gimp_drawable_height(drawable->id);
tex=g_malloc(3*w*h);
gimp_pixel_rgn_init(&rgn,drawable,0,0,w,h,FALSE,FALSE);
gimp_pixel_rgn_get_rect(&rgn,tex,0,0,w,h);
BindTexture(mdl,tex,w,h);
g_free(tex);
gimp_drawable_detach(drawable);
glEnable(GL_TEXTURE_2D);
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
return 0;
}
void on_load_clicked(GtkButton *button,gpointer data)
{
gtk_widget_show(dialog->fileselection);
}
void on_cancel_clicked(GtkButton *button,gpointer data)
{
gtk_widget_hide(dialog->fileselection);
}
void on_ok_clicked(GtkButton *button,gpointer data)
{
char *filename;
Model *mdl;
FILE *fp;
int len;
gtk_widget_hide(dialog->fileselection);
filename=gtk_file_selection_get_filename(
GTK_FILE_SELECTION(dialog->fileselection));
if (!filename || (len=strlen(filename))<=4)
{
gimp_message("Can't open Model!");
return;
}
if (g_strcasecmp(&filename[len-4],".pak")==0)
{
open_pak_file(filename);
}
else if ((g_strcasecmp(&filename[len-4],".md2") == 0) || (g_strcasecmp(&filename[len-4],".mdl") == 0))
{
fp=fopen(filename,"rb");
if (fp==NULL || (mdl=model_load(filename,fp))==NULL)
{
gimp_message("Can't open file!");
fclose(fp);
return;
}
fclose(fp);
model_new(filename,mdl);
}
else
{
gimp_message("Unknown file!");
}
}
gboolean on_fileselection_delete(GtkWidget *w,GdkEvent *ev,gpointer data)
{
gtk_widget_hide(dialog->fileselection);
return TRUE;
}
void set_image_item(gint32 image_id)
{
GtkWidget *menu,*item;
GList *list;
gint pos,i;
gint32 image;
gint32 drawable;
char str[100];
int w,h,bpp;
DEBUG("%i",image_id);
gtk_widget_set_sensitive(dialog->scale,(image_id>=0));
str[0]='\0';
if (image_id>=0)
{
drawable=gimp_image_get_active_layer(image_id);
if (drawable>=0)
{
w=gimp_drawable_width(drawable);
h=gimp_drawable_height(drawable);
bpp=gimp_drawable_bpp(drawable);
sprintf(str,"%ix%i, %i bpp",w,h,bpp);
if (!check_texture(image_id))
{
strcat(str,": no texture");
}
else
{
strcat(str,": texture ok");
}
dialog->oldwidth=round_size(w,FALSE);
dialog->oldheight=round_size(h,FALSE);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->width),dialog->oldwidth);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->height),dialog->oldheight);
}
else strcpy(str,"no active layer");
}
if (dialog->mdl)
DEBUG("%s %i",dialog->mdl->name,dialog->mdl->tex_image);
if (dialog->mdl && dialog->mdl->tex_image!=image_id)
{
dialog->mdl->tex_image=image_id;
get_texture(dialog->mdl);
model_draw(dialog->mdl);
}
gtk_label_set(GTK_LABEL(dialog->info),str);
menu=gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->images_menu));
list=gtk_container_children(GTK_CONTAINER(menu));
pos=0;
for (i=0;list;list=list->next,i++)
{
item=GTK_WIDGET(list->data);
image=GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),IMAGE_KEY));
if (image==image_id) pos=i;
}
gtk_option_menu_set_history(GTK_OPTION_MENU(dialog->images_menu),pos);
}
static void update_value(GtkAdjustment *a,gpointer data)
{
int n;
ModelInfo *mdl;
char *name;
n=GPOINTER_TO_INT(data);
mdl=dialog->mdl;
if (!mdl) return;
switch(n)
{
case 1:
mdl->frame=a->value;
model_draw(mdl);
name=mdl->model->frames[(int)mdl->frame].name;
gtk_label_set(GTK_LABEL(dialog->frame_info2),name);
break;
case 2: mdl->first=(int)a->value; break;
case 3: mdl->last=(int)a->value; break;
case 4: mdl->fps=a->value; break;
}
}
void on_anim_clicked(GtkButton *button,gpointer data)
{
int n;
ModelInfo *mdl;
n=GPOINTER_TO_INT(data);
mdl=dialog->mdl;
if (!mdl) return;
switch(n)
{
case 1:
mdl->playing=TRUE;
set_idle(mdl);
gtk_widget_set_sensitive(dialog->cur_frame,FALSE);
g_timer_start(mdl->timer);
break;
case 2:
mdl->playing=FALSE;
gtk_widget_set_sensitive(dialog->cur_frame,TRUE);
set_idle(mdl);
break;
case 3:
mdl->frame=mdl->first;
gtk_adjustment_set_value(GTK_ADJUSTMENT
(gtk_range_get_adjustment(GTK_RANGE(dialog->cur_frame))),mdl->frame);
break;
case 4:
mdl->frame=mdl->last;
gtk_adjustment_set_value(GTK_ADJUSTMENT
(gtk_range_get_adjustment(GTK_RANGE(dialog->cur_frame))),mdl->frame);
break;
}
}
void set_model_item(ModelInfo *model)
{
ModelInfo *mdl,*m;
int i,pos;
GtkWidget *menu,*item;
GList *list;
char str[256];
GtkObject *adjustment;
if (model) DEBUG("%s",model->name);
menu=gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->models_menu));
list=gtk_container_children(GTK_CONTAINER(menu));
pos=-1;
mdl=NULL;
for (i=0;list;list=list->next,i++)
{
item=GTK_WIDGET(list->data);
m=gtk_object_get_data(GTK_OBJECT(item),MODEL_KEY);
if (m==model)
{
pos=i;
mdl=m;
}
}
DEBUG("%i",pos);
dialog->mdl=mdl;
if (mdl)
{
gtk_option_menu_set_history(GTK_OPTION_MENU(dialog->models_menu),pos);
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(dialog->update),
dialog->mdl->update_texture);
update_images_menu(dialog->mdl->tex_image);
gtk_widget_set_sensitive(dialog->frame_info,TRUE);
gtk_widget_set_sensitive(dialog->frame_info2,TRUE);
gtk_widget_set_sensitive(dialog->cur_frame,TRUE);
gtk_widget_set_sensitive(dialog->start_frame,TRUE);
gtk_widget_set_sensitive(dialog->end_frame,TRUE);
gtk_widget_set_sensitive(dialog->fps,TRUE);
sprintf(str,"%i frames",(int)mdl->model->numframes);
gtk_label_set(GTK_LABEL(dialog->frame_info),str);
adjustment=gtk_adjustment_new(mdl->frame,0,mdl->model->numframes-1,1,1,0);
gtk_range_set_adjustment(GTK_RANGE(dialog->cur_frame),GTK_ADJUSTMENT(adjustment));
gtk_signal_connect(GTK_OBJECT(adjustment),"value_changed",
GTK_SIGNAL_FUNC(update_value),GINT_TO_POINTER(1));
update_value(GTK_ADJUSTMENT(adjustment),GINT_TO_POINTER(1));
adjustment=gtk_adjustment_new(mdl->first,0,mdl->model->numframes-1,1,1,0);
gtk_range_set_adjustment(GTK_RANGE(dialog->start_frame),GTK_ADJUSTMENT(adjustment));
gtk_signal_connect(GTK_OBJECT(adjustment),"value_changed",
GTK_SIGNAL_FUNC(update_value),GINT_TO_POINTER(2));
update_value(GTK_ADJUSTMENT(adjustment),GINT_TO_POINTER(2));
adjustment=gtk_adjustment_new(mdl->last,0,mdl->model->numframes-1,1,1,0);
gtk_range_set_adjustment(GTK_RANGE(dialog->end_frame),GTK_ADJUSTMENT(adjustment));
gtk_signal_connect(GTK_OBJECT(adjustment),"value_changed",
GTK_SIGNAL_FUNC(update_value),GINT_TO_POINTER(3));
update_value(GTK_ADJUSTMENT(adjustment),GINT_TO_POINTER(3));
adjustment=gtk_adjustment_new(15,.1,30,1,1,0);
gtk_range_set_adjustment(GTK_RANGE(dialog->fps),GTK_ADJUSTMENT(adjustment));
gtk_signal_connect(GTK_OBJECT(adjustment),"value_changed",
GTK_SIGNAL_FUNC(update_value),GINT_TO_POINTER(4));
update_value(GTK_ADJUSTMENT(adjustment),GINT_TO_POINTER(4));
}
else
{
gtk_widget_set_sensitive(dialog->frame_info,FALSE);
gtk_widget_set_sensitive(dialog->frame_info2,FALSE);
gtk_widget_set_sensitive(dialog->cur_frame,FALSE);
gtk_widget_set_sensitive(dialog->start_frame,FALSE);
gtk_widget_set_sensitive(dialog->end_frame,FALSE);
gtk_widget_set_sensitive(dialog->fps,FALSE);
update_images_menu(-1);
}
}
static void model_activate(GtkWidget *widget)
{
ModelInfo *mdl;
mdl=gtk_object_get_data(GTK_OBJECT(widget),MODEL_KEY);
set_model_item(mdl);
}
void update_models_menu(ModelInfo *mdl)
{
GtkWidget *menu,*item;
GList *list;
GtkWidget *option_menu;
char *name;
if (mdl) DEBUG("%s",mdl->name);
menu=gtk_menu_new();
list=dialog->models_list;
option_menu=dialog->models_menu;
if (g_list_length(list)==0)
{
item=gtk_menu_item_new_with_label("none");
gtk_widget_show(item);
gtk_menu_append(GTK_MENU(menu),item);
gtk_widget_set_sensitive(option_menu,FALSE);
}
else
{
gtk_widget_set_sensitive(option_menu,TRUE);
for (;list;list=list->next)
{
name=((ModelInfo*)list->data)->name;
item=gtk_menu_item_new_with_label(name);
gtk_object_set_data(GTK_OBJECT(item),MODEL_KEY,list->data);
gtk_signal_connect(GTK_OBJECT(item),"activate",GTK_SIGNAL_FUNC(model_activate),NULL);
gtk_widget_show(item);
gtk_menu_append(GTK_MENU(menu),item);
}
}
gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu),menu);
set_model_item(mdl);
}
static gboolean check_size(int w,int h)
{
int i;
for (i=1;i<=max_tex_size;i*=2)
{
if (i==w) break;
}
if (i>max_tex_size) return FALSE;
for (i=1;i<=max_tex_size;i*=2)
{
if (i==h) break;
}
if (i>max_tex_size) return FALSE;
return TRUE;
}
gboolean check_texture(gint32 image)
{
gint32 drawable_id;
gint bpp,w,h;
if (image<0) return FALSE;
drawable_id=gimp_image_get_active_layer(image);
if (drawable_id<0) return FALSE;
bpp=gimp_drawable_bpp(drawable_id);
if (bpp!=3) return FALSE;
w=gimp_drawable_width(drawable_id);
h=gimp_drawable_height(drawable_id);
return check_size(w,h);
}
static void image_activate(GtkWidget *item)
{
gint32 image;
image=GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),IMAGE_KEY));
set_image_item(image);
}
void update_images_menu(gint32 image_id)
{
gint32 *images;
gint nimages,i,pos;
GtkWidget *item;
GtkWidget *menu;
gchar *name;
gint32 old_image;
DEBUG("%i",image_id);
menu=gtk_menu_new();
images=gimp_image_list(&nimages);
item=gtk_menu_item_new_with_label("none");
gtk_object_set_data(GTK_OBJECT(item),IMAGE_KEY,GINT_TO_POINTER(-1));
gtk_signal_connect(GTK_OBJECT(item),"activate",GTK_SIGNAL_FUNC(image_activate),NULL);
gtk_widget_show(item);
gtk_menu_append(GTK_MENU(menu),item);
pos=0;
item=GTK_OPTION_MENU(dialog->images_menu)->menu_item;
if (item) old_image=GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),IMAGE_KEY));
else old_image=-1;
for (i=0;i<nimages;i++)
{
name=gimp_image_get_filename(images[i]);
item=gtk_menu_item_new_with_label(name);
gtk_signal_connect(GTK_OBJECT(item),"activate",GTK_SIGNAL_FUNC(image_activate),NULL);
gtk_object_set_data(GTK_OBJECT(item),IMAGE_KEY,GINT_TO_POINTER(images[i]));
gtk_widget_show(item);
gtk_menu_append(GTK_MENU(menu),item);
if (images[i]==old_image) pos=i+1;
}
g_free(images);
gtk_option_menu_set_menu(GTK_OPTION_MENU(dialog->images_menu),menu);
gtk_option_menu_set_history(GTK_OPTION_MENU(dialog->images_menu),pos);
set_image_item(image_id);
}
void on_images_menu_enter(GtkButton *widget,gpointer data)
{
if (dialog->mdl)
update_images_menu(dialog->mdl->tex_image);
else
update_images_menu(-1);
}
void on_scale_clicked(GtkButton *b,gpointer data)
{
gint w,h;
GtkWidget *item;
gint32 image_id;
GimpParam *return_val;
gint nreturn_val;
gboolean new_display;
w=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dialog->width));
h=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dialog->height));
item=GTK_OPTION_MENU(dialog->images_menu)->menu_item;
image_id=GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),IMAGE_KEY));
new_display=FALSE;
if (GTK_TOGGLE_BUTTON(dialog->new_image)->active)
{
return_val=gimp_run_procedure("gimp_channel_ops_duplicate",
&nreturn_val,GIMP_PDB_IMAGE,image_id,GIMP_PDB_END);
if (return_val[0].data.d_status!=GIMP_PDB_SUCCESS)
{
gimp_destroy_params(return_val,nreturn_val);
gimp_message("gimp_channel_ops_duplicate failed!");
return;
}
gimp_destroy_params(return_val,nreturn_val);
image_id=return_val[1].data.d_image;
new_display=TRUE;
}
if (image_id<0)
{
gimp_message("Select an image first.");
return;
}
return_val=gimp_run_procedure("gimp_image_scale",
&nreturn_val,
GIMP_PDB_IMAGE,image_id,
GIMP_PDB_INT32,w,
GIMP_PDB_INT32,h,
GIMP_PDB_END);
if (return_val[0].data.d_status!=GIMP_PDB_SUCCESS)
{
gimp_destroy_params(return_val,nreturn_val);
gimp_message("gimp_image_scale failed!");
return;
}
gimp_destroy_params(return_val,nreturn_val);
if (gimp_image_base_type(image_id)!=GIMP_RGB)
{
return_val=gimp_run_procedure("gimp_convert_rgb",
&nreturn_val,
GIMP_PDB_IMAGE,image_id,
GIMP_PDB_END);
if (return_val[0].data.d_status!=GIMP_PDB_SUCCESS)
{
gimp_destroy_params(return_val,nreturn_val);
gimp_message("gimp_convert_rgb failed!");
return;
}
gimp_destroy_params(return_val,nreturn_val);
}
update_images_menu(image_id);
if (dialog->mdl)
{
get_texture(dialog->mdl);
model_draw(dialog->mdl);
}
if (new_display)
{
gimp_display_new(image_id);
gimp_displays_flush();
}
}
void on_base_texture_clicked(GtkButton *b,gpointer data)
{
gint w,h;
gint32 image_id;
gint32 layer_id;
GimpDrawable *drawable;
guchar backgr[3],foregr[3];
GimpParam *return_val;
gint nreturn_val;
gdouble points[4];
int j,i,k;
Model *mdl;
gchar brush[1024];
gdouble opacity;
gint32 mode;
if (dialog->mdl==NULL) return;
mdl=dialog->mdl->model;
gimp_palette_get_background(&backgr[0],&backgr[1],&backgr[2]);
gimp_palette_get_foreground(&foregr[0],&foregr[1],&foregr[2]);
return_val=gimp_run_procedure("gimp_brushes_get_brush",
&nreturn_val,GIMP_PDB_END);
if (return_val[0].data.d_status == GIMP_PDB_SUCCESS)
{
strncpy(brush,return_val[1].data.d_string,sizeof(brush));
}
else
{
brush[0]='\0';
}
gimp_destroy_params(return_val,nreturn_val);
return_val=gimp_run_procedure("gimp_brushes_get_opacity",
&nreturn_val,GIMP_PDB_END);
if (return_val[0].data.d_status==GIMP_PDB_SUCCESS)
{
opacity=return_val[1].data.d_float;
}
else
{
opacity=100;
}
gimp_destroy_params(return_val,nreturn_val);
return_val=gimp_run_procedure("gimp_brushes_get_paint_mode",
&nreturn_val,GIMP_PDB_END);
if (return_val[0].data.d_status==GIMP_PDB_SUCCESS)
{
mode=return_val[1].data.d_int32;
}
else
{
mode=GIMP_NORMAL_MODE;
}
gimp_destroy_params(return_val,nreturn_val);
gimp_palette_set_foreground(255,255,255);
gimp_palette_set_background(0,0,0);
w=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dialog->width));
h=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dialog->height));
image_id=gimp_image_new(w,h,GIMP_RGB);
gimp_image_set_filename(image_id,"Base Texture");
layer_id=gimp_layer_new(image_id,"Base Texture",w,h,GIMP_RGB_IMAGE,100,GIMP_NORMAL_MODE);
gimp_image_add_layer(image_id,layer_id,0);
drawable=gimp_drawable_get(layer_id);
gimp_drawable_fill(layer_id,0);
return_val=gimp_run_procedure("gimp_brushes_set_brush",&nreturn_val,
GIMP_PDB_STRING,"Circle (01)",GIMP_PDB_END);
gimp_destroy_params(return_val,nreturn_val);
return_val=gimp_run_procedure("gimp_brushes_set_paint_mode",&nreturn_val,
GIMP_PDB_INT32,GIMP_NORMAL_MODE,GIMP_PDB_END);
gimp_destroy_params(return_val,nreturn_val);
return_val=gimp_run_procedure("gimp_brushes_set_opacity",&nreturn_val,
GIMP_PDB_FLOAT,(gdouble)100.0,GIMP_PDB_END);
gimp_destroy_params(return_val,nreturn_val);
for (k=0;k<mdl->num_tris;k++)
{
j=2;
for (i=0;i<3;j=i++)
{
points[0]=(double)mdl->tri[k].tex[j][0]*w;
points[1]=(double)mdl->tri[k].tex[j][1]*h;
points[2]=(double)mdl->tri[k].tex[i][0]*w;
points[3]=(double)mdl->tri[k].tex[i][1]*h;
return_val=gimp_run_procedure("gimp-paintbrush",
&nreturn_val,
GIMP_PDB_IMAGE,image_id,
GIMP_PDB_DRAWABLE,layer_id,
GIMP_PDB_FLOAT,(gdouble)0.0,
GIMP_PDB_INT32,4,
GIMP_PDB_FLOATARRAY,&points[0],
GIMP_PDB_END);
gimp_destroy_params(return_val,nreturn_val);
}
}
gimp_drawable_detach(drawable);
gimp_display_new(image_id);
gimp_image_clean_all(image_id);
gimp_displays_flush();
gimp_palette_set_background(backgr[0],backgr[1],backgr[2]);
gimp_palette_set_foreground(foregr[0],foregr[1],foregr[2]);
return_val=gimp_run_procedure("gimp_brushes_set_brush",&nreturn_val,
GIMP_PDB_STRING,brush,GIMP_PDB_END);
gimp_destroy_params(return_val,nreturn_val);
return_val=gimp_run_procedure("gimp_brushes_set_paint_mode",&nreturn_val,
GIMP_PDB_INT32,mode,GIMP_PDB_END);
gimp_destroy_params(return_val,nreturn_val);
return_val=gimp_run_procedure("gimp_brushes_set_opacity",&nreturn_val,
GIMP_PDB_FLOAT,opacity,GIMP_PDB_END);
gimp_destroy_params(return_val,nreturn_val);
}
static void on_spin_button_changed(GtkSpinButton *button,gpointer data)
{
int val,oldval;
oldval=val=gtk_spin_button_get_value_as_int(button);
switch(GPOINTER_TO_INT(data))
{
case 1:
if (val<dialog->oldwidth)
val=round_size(val,TRUE);
else
val=round_size(val,FALSE);
dialog->oldwidth=val;
break;
case 2:
if (val<dialog->oldheight)
val=round_size(val,TRUE);
else
val=round_size(val,FALSE);
dialog->oldheight=val;
break;
}
if (val!=oldval) gtk_spin_button_set_value(button,val);
}
void init_dialog(void)
{
GtkWidget *w;
gchar version[256];
if (dialog) g_free(dialog);
dialog=g_malloc(sizeof(dialog[0]));
memset(dialog,0,sizeof(dialog[0]));
dialog->widget=create_dialog();
dialog->fileselection=create_fileselection();
dialog->info=get_widget(dialog->widget,"info");
dialog->nearest=get_widget(dialog->widget,"nearest");
dialog->linear=get_widget(dialog->widget,"linear");
dialog->fastest=get_widget(dialog->widget,"fastest");
dialog->nicest=get_widget(dialog->widget,"nicest");
dialog->update=get_widget(dialog->widget,"update");
dialog->images_menu=get_widget(dialog->widget,"images_menu");
dialog->models_menu=get_widget(dialog->widget,"models_menu");
dialog->scale=get_widget(dialog->widget,"scale");
dialog->new_image=get_widget(dialog->widget,"new_image");
dialog->frame_info=get_widget(dialog->widget,"frame_info");
dialog->frame_info2=get_widget(dialog->widget,"frame_info2");
dialog->cur_frame=get_widget(dialog->widget,"cur_frame");
dialog->start_frame=get_widget(dialog->widget,"start_frame");
dialog->end_frame=get_widget(dialog->widget,"end_frame");
dialog->fps=get_widget(dialog->widget,"fps");
dialog->update_time=get_widget(dialog->widget,"update_time");
dialog->width=get_widget(dialog->widget,"width");
dialog->height=get_widget(dialog->widget,"height");
gtk_signal_connect(GTK_OBJECT(dialog->width),"changed",
GTK_SIGNAL_FUNC(on_spin_button_changed),GINT_TO_POINTER(1));
gtk_signal_connect(GTK_OBJECT(dialog->height),"changed",
GTK_SIGNAL_FUNC(on_spin_button_changed),GINT_TO_POINTER(2));
w=get_widget(dialog->widget,"anim_hbox");
add_anim_button(w,1,play_xpm);
add_anim_button(w,2,stop_xpm);
add_anim_button(w,3,begin_xpm);
add_anim_button(w,4,end_xpm);
sprintf(version,"Texture Paint %s",PACKAGE_VERSION);
gtk_label_set(GTK_LABEL(get_widget(dialog->widget,"version")),version);
dialog->paint_image=-1;
dialog->texture_drawable=-1;
gtk_widget_show(dialog->widget);
update_models_menu(NULL);
}
void on_dialog_destroy(GtkObject *obj,gpointer data)
{
gtk_main_quit();
}
void on_glwindow_destroy(GtkObject *obj,gpointer data)
{
ModelInfo *mdl;
mdl=gtk_object_get_data(obj,MODEL_KEY);
if (mdl==NULL) return; /* should never happen*/
if (mdl->idle>=0) gtk_idle_remove(mdl->idle);
mdl->model->destroy(mdl->model);
g_free(mdl->name);
if (mdl->tex_s) g_free(mdl->tex_s);
if (mdl->tex_t) g_free(mdl->tex_t);
g_timer_destroy(mdl->timer);
g_free(mdl);
dialog->models_list=g_list_remove(dialog->models_list,mdl);
if (dialog->models_list)
update_models_menu((ModelInfo *)dialog->models_list->data);
else
update_models_menu(NULL);
}
void on_close_clicked(GtkButton *button,gpointer data)
{
gtk_main_quit();
}
static void run(gchar *name,
gint nparams,
GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GimpParam val[1];
gint argc;
gchar **argv;
argc=1;
argv=g_new(gchar *,1);
argv[0]=g_strdup("Texture Paint");
gtk_init(&argc, &argv);
gtk_rc_parse(gimp_gtkrc());
gdk_set_use_xshm(gimp_use_xshm());
val[0].type=GIMP_PDB_STATUS;
val[0].data.d_status=GIMP_PDB_SUCCESS;
*nreturn_vals=1;
*return_vals=val;
init_dialog();
#if 0
{
FILE *f;
Model *mdl;
f=fopen("/home/bob/coding/qview/data/players/male/tris.md2","rb");
mdl = ModelLoad(f);
fclose(f);
model_new("test1",mdl);
f=fopen("/home/bob/coding/qview/data/players/male/tris.md2","rb");
mdl = ModelLoad(f);
fclose(f);
model_new("test2",mdl);
}
#endif
gtk_main();
}
static void get_size(ModelInfo *mdl,int *w,int *h)
{
GLint data[4];
begingl(mdl->glarea);
glGetIntegerv(GL_VIEWPORT,data);
*w=data[2];
*h=data[3];
endgl(mdl->glarea);
}
static void get_image(ModelInfo *mdl,guchar **image,int w,int h,int bpp)
{
guchar *buf,*tmp;
int y;
int stride;
begingl(mdl->glarea);
buf=g_malloc(w*h*bpp);
tmp=g_malloc(w*h*3);
glPixelStorei(GL_PACK_ALIGNMENT,1);
glReadPixels(0,0,w,h,GL_RGB,GL_UNSIGNED_BYTE,tmp);
endgl(mdl->glarea);
if (bpp==3)
{
stride=w*3;
for (y=0;y<h;y++)
{
memcpy(&buf[y*stride],&tmp[(h-y-1)*stride],stride);
}
}
else
{ int x;
for (y=0;y<h;y++)
for (x=0;x<w;x++)
{
buf[y*w+x]=tmp[((h-y-1)*w+x)*3];
}
}
g_free(tmp);
*image=buf;
}
static void copy_to_drawable(ModelInfo *mdl,gint32 drawable_id,int w,int h)
{
GimpPixelRgn pr;
GimpDrawable *drawable;
guchar *image;
int bpp;
bpp=gimp_drawable_bpp(drawable_id);
get_image(mdl,&image,w,h,bpp);
drawable=gimp_drawable_get(drawable_id);
gimp_pixel_rgn_init(&pr,drawable,0,0,w,h,TRUE,FALSE);
gimp_pixel_rgn_set_rect(&pr,image,0,0,w,h);
gimp_drawable_flush(drawable);
gimp_drawable_detach(drawable);
g_free(image);
}
static void decode(gint16 **data,guchar *buf,int w,int h)
{
int x,y;
int val;
int pos;
gint16 *dest;
dest=g_malloc(w*h*sizeof(gint16));
pos=0;
for (y=0;y<h;y++)
{
for (x=0;x<w;x++)
{
val=(guint)buf[pos]>>red_shift;
val+=((guint)buf[pos+1]>>green_shift)<<red_bits;
val+=((guint)buf[pos+2]>>blue_shift)<<(red_bits+green_bits);
dest[x+y*w]=val;
pos+=3;
}
}
*data=dest;
}
static void save_st(ModelInfo *mdl,int w,int h)
{
guchar *buffer;
mdl->oldw=w;
mdl->oldh=h;
if (mdl->tex_s) g_free(mdl->tex_s);
display_model(mdl,DRAW_COORD_S);
get_image(mdl,&buffer,w,h,3);
decode(&mdl->tex_s,buffer,w,h);
g_free(buffer);
if (mdl->tex_t) g_free(mdl->tex_t);
display_model(mdl,DRAW_COORD_T);
get_image(mdl,&buffer,w,h,3);
decode(&mdl->tex_t,buffer,w,h);
g_free(buffer);
}
void on_paint_clicked(GtkButton *button,gpointer data)
{
gint w,h;
gint32 image_id;
gint32 display_id;
gint32 layer_id,mask_id;
gint32 bglayer_id;
guchar r,g,b;
gint32 *images;
gint nimages;
gint32 *layers;
gint nlayers;
gint i;
gboolean new_image;
ModelInfo *mdl;
if (data) mdl=(ModelInfo *)data;
else if (dialog->mdl) mdl=dialog->mdl;
else return;
DEBUG("%s",mdl->name);
if (get_texture(mdl)<0)
{
gimp_message("Select a texture image first.");
return;
}
layer_id=gimp_image_get_active_layer(mdl->tex_image);
w=gimp_drawable_width(layer_id);
h=gimp_drawable_height(layer_id);
init_special_texture(mdl,w,h); // FIXME oldw==w
get_size(mdl,&w,&h);
save_st(mdl,w,h);
new_image=TRUE;
if (mdl->paint_image>=0)
{
images=gimp_image_list(&nimages);
for (i=0;i<nimages;i++)
{
if (mdl->paint_image==images[i])
{
new_image=FALSE;
break;
}
}
g_free(images);
}
if (new_image)
{
image_id=gimp_image_new(w,h,GIMP_RGB);
mdl->paint_image=image_id;
gimp_image_set_filename(image_id,"3D Paint");
}
else
{
image_id=mdl->paint_image;
layers=gimp_image_get_layers(image_id,&nlayers);
for (i=0;i<nlayers;i++)
{
gimp_image_remove_layer(image_id,layers[i]);
}
g_free(layers);
if (gimp_image_width(image_id)!=w ||
gimp_image_height(image_id)!=h)
{
gimp_image_resize(image_id,w,h,0,0);
}
}
layer_id=gimp_layer_new(image_id,"Texture",w,h,GIMP_RGB_IMAGE,100,GIMP_NORMAL_MODE);
display_model(mdl,DRAW_NORMAL);
copy_to_drawable(mdl,layer_id,w,h);
gimp_layer_add_alpha(layer_id);
mask_id=gimp_layer_create_mask(layer_id,1);
display_model(mdl,DRAW_WHITE);
copy_to_drawable(mdl,mask_id,w,h);
bglayer_id=gimp_layer_new(image_id,"Background",w,h,GIMP_RGB_IMAGE,100,GIMP_NORMAL_MODE);
gimp_palette_get_background(&r,&g,&b);
gimp_palette_set_background(77,102,153);
gimp_drawable_fill(bglayer_id,0);
gimp_palette_set_background(r,g,b);
gimp_image_add_layer(image_id,layer_id,1);
gimp_image_add_layer_mask(image_id,layer_id,mask_id);
gimp_image_add_layer(image_id,bglayer_id,2);
gimp_image_set_active_layer(image_id,layer_id);
gimp_layer_set_edit_mask(layer_id,FALSE);
if (new_image)
{
display_id=gimp_display_new(image_id);
}
gimp_image_clean_all(image_id);
gimp_displays_flush();
display_model(mdl,DRAW_NORMAL);
}
void on_generate_clicked(GtkButton *button,gpointer data)
{
gint32 drawable_id,image_id;
guchar *src;
guchar *dest;
gint32 *buffer;
GimpPixelRgn pr;
GimpDrawable *drawable;
gint w,h;
gint src_w,src_h;
gint x,y;
gint s,t;
gint bpp;
gint pos1,pos2;
gint div;
ModelInfo *mdl;
char str[256];
if (data) mdl=(ModelInfo *)data;
else if (dialog->mdl) mdl=dialog->mdl;
else return;
if (mdl->paint_image<0)
{
gimp_message("Click on the \"3D Paint\" button first.");
return;
}
image_id=mdl->paint_image;
drawable_id=gimp_image_get_active_layer(image_id);
if (drawable_id<0)
{
gimp_message("Can't get active layer!");
return;
}
src_w=gimp_drawable_width(drawable_id);
src_h=gimp_drawable_height(drawable_id);
bpp=gimp_drawable_bpp(drawable_id);
if (src_w!=mdl->oldw || src_h!=mdl->oldh || bpp!=4)
{
sprintf(str,"The image must be %ix%i (4 bpp)!",mdl->oldw,mdl->oldh);
gimp_message(str);
return;
}
if (!check_texture(mdl->tex_image))
{
gimp_message("check_texture() failed!");
return;
}
drawable=gimp_drawable_get(drawable_id);
src=g_malloc(4*src_w*src_h);
gimp_pixel_rgn_init(&pr,drawable,0,0,src_w,src_h,FALSE,FALSE);
gimp_pixel_rgn_get_rect(&pr,src,0,0,src_w,src_h);
gimp_drawable_flush(drawable);
gimp_drawable_detach(drawable);
drawable_id=gimp_image_get_active_layer(mdl->tex_image);
drawable=gimp_drawable_get(drawable_id);
w=gimp_drawable_width(drawable->id);
h=gimp_drawable_height(drawable->id);
dest=g_malloc(3*w*h);
buffer=g_malloc(w*h*sizeof(gint32[4]));
memset(buffer,0,w*h*sizeof(gint32[4]));
gimp_pixel_rgn_init(&pr,drawable,0,0,w,h,TRUE,FALSE);
gimp_pixel_rgn_get_rect(&pr,dest,0,0,w,h);
for (y=0;y<src_h;y++)
{
for (x=0;x<src_w;x++)
{
s=mdl->tex_s[x+y*src_w]-1;
t=mdl->tex_t[x+y*src_w]-1;
if (s>0 && t>0)
{
pos1=(s+t*w)*4;
pos2=(x+y*src_w)*4;
buffer[pos1]+=src[pos2];
buffer[pos1+1]+=src[pos2+1];
buffer[pos1+2]+=src[pos2+2];
buffer[pos1+3]++;
}
}
}
for (pos1=pos2=y=0;y<h;y++)
{
for (x=0;x<w;x++)
{
div=buffer[pos1+3];
if (div)
{
dest[pos2]=buffer[pos1]/div;
dest[pos2+1]=buffer[pos1+1]/div;
dest[pos2+2]=buffer[pos1+2]/div;
}
pos1+=4;
pos2+=3;
}
}
gimp_pixel_rgn_set_rect(&pr,dest,0,0,w,h);
gimp_drawable_flush(drawable);
gimp_drawable_detach(drawable);
begingl(mdl->glarea);
BindTexture(mdl,dest,w,h);
endgl(mdl->glarea);
display_model(mdl,DRAW_NORMAL);
g_free(src);
g_free(dest);
g_free(buffer);
gimp_drawable_update(drawable_id,0,0,w,h);
gimp_displays_flush();
}