Page 1 of 2

Load palette from console

Posted: Thu Jan 13, 2011 3:36 am
by qbism
Lately I've been using GIMP and fimg to tweak the palette just to see what happens. The idea is to change the ambiance. For example, rebalance dark colors toward blue and light colors toward yellow, like sunlight and shadow. Another example- make the palette mostly desaturated for a '300' or 'Sin City' look.

This tutorial allows different palette lmp files to be loaded in the console, "loadpalette mypal" will load mypal.lmp. It's simple but will need adjustment for your particular engine.

What I'm not sure of... should there be a way to load a colormap lmp?

EDIT: Removed ifdef around Q_snprintfz per discussion below.

In r_main.c:

Code: Select all

extern	byte		*host_basepal;

/*
===============
R_LoadPalette
===============
*/

void R_LoadPalette (char *name) //qbism - load a palette lmp
{
    loadedfile_t	*fileinfo;
    char	pathname[MAX_QPATH];

    Q_snprintfz (pathname, sizeof(pathname), "gfx/%s.lmp", name);

    fileinfo = COM_LoadHunkFile (pathname);
    if (!fileinfo)
    {
        Con_Printf("Palette not found.\n");
        return;
    }
    Q_memcpy (host_basepal, fileinfo->data, 768);
    VID_SetPalette (host_basepal);
}

/*
===============
R_LoadPalette_f
===============
*/
void R_LoadPalette_f (void) //qbism - load an alternate palette
{
    if (Cmd_Argc() != 2)
    {
        Con_Printf ("loadpalette <name> : load a color palette\n");
        return;
    }
    R_LoadPalette(Cmd_Argv(1));
}

EDIT: Add regenerate colormap. This will greatly improve lighting quality for your new palette. Add the following code above loadpalette, it is indeed adapted from qlumpy.

Code: Select all

/*
=============================================================================

COLORMAP GRABBING

=============================================================================
*/

/*
===============
BestColor - qbism- from qlumpy
===============
*/
byte BestColor (int r, int g, int b, int start, int stop)
{
   int   i;
   int   dr, dg, db;
   int   bestdistortion, distortion;
   int   bestcolor;
   byte   *pal;

//
// let any color go to 0 as a last resort
//
   bestdistortion = ( (int)r*r + (int)g*g + (int)b*b )*2;
   bestcolor = 0;

   pal = host_basepal + start*3;
   for (i=start ; i<= stop ; i++)
   {
      dr = r - (int)pal[0];
      dg = g - (int)pal[1];
      db = b - (int)pal[2];
      pal += 3;
      distortion = dr*dr + dg*dg + db*db;
      if (distortion < bestdistortion)
      {
         if (!distortion)
            return i;      // perfect match

         bestdistortion = distortion;
         bestcolor = i;
      }
   }

   return bestcolor;
}


/*
==============
GrabColormap - qbism- from qlumpy

filename COLORMAP levels fullbrights
the first map is an identiy 0-255
the final map is all black except for the fullbrights
the remaining maps are evenly spread
fullbright colors start at the top of the palette.
==============
*/
void GrabColormap (void)
{
   int      levels, brights;
   int      l, c;
   float   frac, red, green, blue;
   byte *colmap;

   colmap = host_colormap;

   levels = 32;
   brights = 256;

// identity lump
   for (l=0 ; l<256 ; l++)
      *colmap++ = l;

// shaded levels
   for (l=1;l<levels;l++)
   {
      frac = 1.0 - (float)l/(levels-1);
      for (c=0 ; c<256-brights ; c++)
      {
         red = host_basepal[c*3];
         green = host_basepal[c*3+1];
         blue = host_basepal[c*3+2];

         red = (int)(red*frac+0.5);
         green = (int)(green*frac+0.5);
         blue = (int)(blue*frac+0.5);

//
// note: 254 instead of 255 because 255 is the transparent color, and we
// don't want anything remapping to that
//
         *colmap++ = BestColor(red,green,blue, 0, 254);
      }
      for ( ; c<256 ; c++)
         *colmap++ = c;
   }

   *colmap++ = brights;
}

In R_LoadPalette, Add this above Vid_SetPalette

Code: Select all

GrabColormap();


If you want to add a palette field to the worldspawn, add a check for it in CL_ParseEntityLump.

A further step might be remipping textures using the new palette. So far I haven't noticed anything horrible enough to warrant this intensive step.

Re: Load palette from console

Posted: Thu Jan 13, 2011 4:10 am
by leileilol
qbism wrote:should there be a way to load a colormap lmp?
Sure, but with today's awesome CPUs above 300mhz you can just generate it on the fly - no excuse for that now.

Check the qlumpy source in qtools_gpl.tar.gz for that, and have fun adapting it and throwing cvars in there for it.

The biggest problem are mipmaps - either regenerate those as well or deal with pixely artifacts from anti-aliased pixels meant for the former palette.

Posted: Thu Jan 13, 2011 4:26 am
by Mexicouger
This could be used to attach to models too correct? Like load a palette for a certain model?

This is a pretty good Tutorial!

Posted: Thu Jan 13, 2011 4:28 am
by Sajt
Mexicouger wrote:This could be used to attach to models too correct? Like load a palette for a certain model?
Nope, it replaces the global palette.

Posted: Thu Jan 13, 2011 5:38 am
by Mexicouger
Oh well. Good tutorial nonetheless

Re: Load palette from console

Posted: Thu Jan 13, 2011 6:31 am
by andrewj
qbism wrote:#ifdef __linux__
sprintf (pathname, sizeof(pathname), "gfx/%s.lmp\0", name);
#else
Q_snprintfz (pathname, sizeof(pathname), "gfx/%s.lmp\0", name);
#endif
I'm curious, why does Linux need special treatment there?

Posted: Thu Jan 13, 2011 9:54 am
by Spike
because someone forgot to #define Q_snprintfz snprintf for linux.
of course, if you try compiling the code, the linux version will crash due to seeing an int where it expected a string, because its got sprintf instead of snprintf. so mneh.
note that windows' _snprintf function is not compatible with linux/bsd's snprintf as it doesn't guarentee null termination. I think the return values are different too, but those are rarely used.

Posted: Thu Jan 13, 2011 11:29 am
by mh
Spike wrote:because someone forgot to #define Q_snprintfz snprintf for linux.
of course, if you try compiling the code, the linux version will crash due to seeing an int where it expected a string, because its got sprintf instead of snprintf. so mneh.
note that windows' _snprintf function is not compatible with linux/bsd's snprintf as it doesn't guarentee null termination. I think the return values are different too, but those are rarely used.
Which is probably why Q_snprintfz is used for non-Linux in this code (which won't compile either as Q_snprintfz doesn't exist in stock ID Quake, but that's beside the point). Maybe the question should be "why does non-Linux need special treatment there?" instead?

Posted: Thu Jan 13, 2011 6:04 pm
by qbism
Regarding Q_snprintfz, I should have mentioned that, but good explanation above. MicroSoft's _snprintf does not guarantee null termintation as gcc's snprintf does.

I haven't tried to compile this in Linux, the #ifdef is patterned after similar (ancient) parts of the source. But it would probably be better to deal with that in the definition of Q_snprintfz instead of every single time it's called. If that's even possible. Currently I've been using mingw which includes gcc . I don't know if gcc in Linux would have the same behavior. If it did, that #ifdef could be removed.
leileilol wrote:The biggest problem are mipmaps - either regenerate those as well or deal with pixely artifacts from anti-aliased pixels meant for the former palette.
Hadn't thought of that, but noticed it when the colors stray too far from original.

Posted: Thu Jan 13, 2011 9:19 pm
by Baker
Very interesting tutorial. :D
#ifdef __linux__
sprintf (pathname, sizeof(pathname), "gfx/%s.lmp\0", name);
#else
Q_snprintfz (pathname, sizeof(pathname), "gfx/%s.lmp\0", name);

#endif
Should be ...
Q_snprintfz (pathname, sizeof(pathname), "gfx/%s.lmp", name);
You can kill the #ifdef and kill the "\0" ... Q_snprintfz is platform neutral and null terminates so the Linux #ifdef is unnecessary and Q_snprintfz null terminates so adding a "\0" to the string is not only redundant, but is actually entirely unhelpful because if the resulting string length > sizeof(pathname) it would be truncated from the result anyway (!!).

Posted: Fri Jan 14, 2011 12:40 am
by qbism
Baker wrote:kill the #ifdef and kill the "\0" ... Q_snprintfz is platform neutral
Excellent, corrected above! I don't know why I never removed the \0 before. :D

Posted: Fri Jan 14, 2011 3:12 am
by Sajt
Baker wrote:You can kill the #ifdef and kill the "\0" ... Q_snprintfz is platform neutral and null terminates so the Linux #ifdef is unnecessary and Q_snprintfz null terminates so adding a "\0" to the string is not only redundant, but is actually entirely unhelpful because if the resulting string length > sizeof(pathname) it would be truncated from the result anyway (!!).
Actually, the \0 doesn't make a difference. It's baffling that someone would put it in there.

Posted: Fri Jan 14, 2011 3:56 am
by Baker
Okay\0

Posted: Fri Jan 14, 2011 5:38 pm
by Ranger366
I already wanted to implent something similar to my engine, that a map has the ability to have an own palette file. But i stopped it.

Posted: Sat Jan 15, 2011 3:24 am
by qbism
Ranger366 wrote:I already wanted to implent something similar to my engine, that a map has the ability to have an own palette file.
Look at how skyboxes are implemented.