colormaps: Generate colormaps programatically.

This replaces the statically generated colormaps with equivalent
colormap lumps that are generated by the colormap.py script (which now
has parameters to control the generated colormap).
This commit is contained in:
Simon Howard 2013-12-22 17:07:17 +00:00
parent 301ad35237
commit 90fed22627
16 changed files with 200 additions and 68 deletions

2
.gitignore vendored
View file

@ -3,7 +3,7 @@ deutex.log
graphics/titlepic/freedm.gif
graphics/titlepic/freedoom.gif
graphics/titlepic/ultdoom.gif
lumps/cph/misc-lumps/colormap.lmp
lumps/cph/misc-lumps/*map.lmp
lumps/cph/misc-lumps/playpal.lmp
lumps/freedoom.lmp
lumps/freedm.lmp

View file

@ -1 +1 @@
cyb/bloodmap.lmp
cph/misc-lumps/bloodmap.lmp

View file

@ -1 +1 @@
cyb/bluemap.lmp
cph/misc-lumps/bluemap.lmp

View file

@ -1,20 +1,51 @@
all : playpal.lmp colormap.lmp
COLORMAPS = colormap.lmp \
bloodmap.lmp bluemap.lmp lavamap.lmp nukemap.lmp \
fogmap.lmp mfademap.lmp
playpal.lmp : playpal-base.lmp playpal.py
all: playpal.lmp $(COLORMAPS)
playpal.lmp: playpal-base.lmp playpal.py
./playpal.py playpal-base.lmp > playpal.lmp
colormap.lmp : playpal.lmp colormap.py
colormap.lmp: playpal.lmp colormap.py
./colormap.py playpal.lmp > colormap.lmp
SOURCES=playpal.py colormap.py COPYING Makefile
VERSION=0.0.1
# Blue colormap applies a full blue tint. The brightness here is
# adjusted slightly because the default is too dark.
dist : doom-misc-lumps-$(VERSION).tar.gz
bluemap.lmp: playpal.lmp colormap.py
./colormap.py --tint_color='#0000ff' --tint_pct=100 \
--tint_bright=0.6 playpal.lmp > bluemap.lmp
doom-misc-lumps-$(VERSION).tar.gz : $(SOURCES)
tar czf $@ $(SOURCES)
# "Fog" effect maps that darken to a color other than black:
fogmap.lmp: playpal.lmp colormap.py
./colormap.py --dark_color='#505050' playpal.lmp > fogmap.lmp
mfademap.lmp: playpal.lmp colormap.py
./colormap.py --dark_color='#2b230f' playpal.lmp > mfademap.lmp
# "Liquid" colormaps that apply a tint. With a liquid effect we also
# usually want to use --dark_color as well, because we want to darken
# to something like the liquid color instead of black.
bloodmap.lmp: playpal.lmp colormap.py
./colormap.py --tint_color='#ff0000' --dark_color='#500000' \
--tint_pct=100 playpal.lmp > bloodmap.lmp
# TODO: The current lava effect is just very bright orange. Before the
# change to programatically-generated lavamap we had a better effect.
lavamap.lmp: playpal.lmp colormap.py
./colormap.py --tint_color='#ff6000' --tint_pct=80 \
--tint_bright=0.8 --dark_color='#ff6000' \
playpal.lmp > lavamap.lmp
nukemap.lmp: playpal.lmp colormap.py
./colormap.py --tint_color='#00ff00' --tint_pct=70 \
--dark_color='#005000' playpal.lmp > nukemap.lmp
clean:
rm -f playpal.lmp colormap.lmp doom-misc-lumps-$(VERSION).tar.gz
rm -f playpal.lmp $(COLORMAPS)
.PHONY: clean
.PHONY : dist clean

View file

@ -1,13 +1,7 @@
#!/usr/bin/env python
#
# Takes PLAYPAL as input (filename is the only parameter)
# Produces a light graduated COLORMAP on stdout
# O(n^2)
#
# This is a Python version of Colin's original Perl script.
#
# Copyright (C) 2001 Colin Phipps <cphipps@doomworld.com>
# Copyright (C) 2008 Simon Howard
# Copyright (C) 2008, 2013 Simon Howard
# Parts copyright (C) 1999 by id Software (http://www.idsoftware.com/)
#
# This program is free software; you can redistribute it and/or modify
@ -23,14 +17,44 @@
# 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
#
#
# Takes PLAYPAL as input (filename is the only parameter)
# Produces a light graduated COLORMAP on stdout
# O(n^2)
#
# This was originally a Perl script by Colin Phipps; it was converted
# to Python and now is a more generic tool for generating all kinds of
# COLORMAP effects.
#
import os
import sys
import struct
# Return palette read from named file
# Parameters affecting colormap generation:
# "Darkness" is this color, which is usually black, but can be
# overridden (RGB 0-255):
dark_color = (0, 0, 0)
# Color to tint the colormap (RGB 0.0-1.0):
tint_color = (255, 255, 255)
# Fractional balance between tint and normal color. 0 is no tint applied,
# 1.0 is full tint.
tint_frac = 0
# Fudge factor to adjust brightness when calculating 'tinted' version
# of colors. Larger values are brighter but may cause color clipping.
# A value of 0.33 is a straight-average of the RGB channels. Maximum
# sensible value is 1.0, though it can be overdriven for fancy
# brightness effects.
tint_bright = 0.5
def read_palette(filename):
"""Read palette from file and return a list of tuples containing
RGB values."""
f = open(filename, "rb")
colors = []
@ -43,17 +67,20 @@ def read_palette(filename):
return colors
def square(x):
return x * x
# Return closest palette entry to the given RGB triple
def search_palette(colors, target):
def search_palette(palette, target):
"""Search the given palette and find the nearest matching
color to the given color, returning an index into the
palette of the color that best matches."""
best_diff = None
best_index = None
for i in range(len(colors)):
color = colors[i]
def square(x):
return x * x
for i in range(len(palette)):
color = palette[i]
diff = square(target[0] - color[0]) \
+ square(target[1] - color[1]) \
@ -65,35 +92,78 @@ def search_palette(colors, target):
return best_index
def generate_colormap(colors, transform_function):
def generate_colormap(colors, palette):
"""Given a list of colors, translate these into indexes into
the given palette, finding the nearest color where an exact
match cannot be found."""
result = []
for color in colors:
transformed_color = transform_function(color)
transformed_index = search_palette(colors, transformed_color)
result.append(transformed_index)
index = search_palette(palette, color)
result.append(index)
return result
def generate_darkened_colormap(colors, factor):
def tint_colors(colors, tint, bright=0.5):
"""Given a list of colors, tint them a particular color."""
darken_function = lambda c: ( c[0] * factor, \
c[1] * factor, \
c[2] * factor )
result = []
for c in colors:
# I've experimented with different methods of calculating
# intensity, but this seems to work the best. This is basically
# doing an average of the full channels, but a straight
# average causes the picture to get darker - eg. (0,0,255)
# maps to (87,87,87). So we have a controllable brightness
# factor that allows the brightness to be adjusted.
intensity = min((c[0] + c[1] + c[2]) * bright, 255) / 255.0
result.append((
tint[0] * intensity,
tint[1] * intensity,
tint[2] * intensity,
))
return generate_colormap(colors, darken_function)
return result
def blend_colors(colors1, colors2, factor=0.5):
"""Blend the two given lists of colors, with 'factor' controlling
the mix between the two. factor=0 is exactly colors1, while
factor=1 is exactly colors2. Returns a list of blended colors."""
result = []
for index, c1 in enumerate(colors1):
c2 = colors2[index]
result.append((
c2[0] * factor + c1[0] * (1 - factor),
c2[1] * factor + c1[1] * (1 - factor),
c2[2] * factor + c1[2] * (1 - factor),
))
return result
def invert_colors(colors):
"""Given a list of colors, translate them to inverted monochrome."""
result = []
for color in colors:
average = (color[0] + color[1] + color[2]) / 3
inverse = 255 - average
result.append((inverse, inverse, inverse))
return result
def solid_color_list(color):
"""Generate a 256-entry palette where all entries are the
same color."""
return [color] * 256
def output_colormap(colormap):
"""Output the given palette to stdout."""
for c in colormap:
x = struct.pack("B", c)
os.write(sys.stdout.fileno(), x)
def inverse_color(color):
average = (color[0] + color[1] + color[2]) / 3
inverse = 255 - average
return (inverse, inverse, inverse)
def print_palette(colors):
for y in range(16):
for x in range(16):
@ -103,25 +173,65 @@ def print_palette(colors):
print()
if len(sys.argv) < 2:
print("Usage: %s <base filename> > output-file.lmp" % sys.argv[0])
def parse_color_code(s):
"""Parse a color code in HTML color code format, into an RGB
3-tuple value."""
if not s.startswith('#') or len(s) != 7:
raise Exception('Not in HTML color code form: %s' % s)
return (int(s[1:3], 16), int(s[3:5], 16), int(s[5:7], 16))
def set_parameter(name, value):
"""Set configuration value, from command line parameters."""
global dark_color, tint_color, tint_frac, tint_bright
if name == 'dark_color':
dark_color = parse_color_code(value)
elif name == 'tint_color':
tint_color = parse_color_code(value)
elif name == 'tint_pct':
tint_frac = int(value) / 100.0
elif name == 'tint_bright':
tint_bright = float(value)
else:
raise Exception("Unknown parameter: '%s'" % name)
# Parse command line.
playpal_filename = None
for arg in sys.argv[1:]:
if arg.startswith('--') and '=' in arg:
key, val = arg[2:].split('=', 2)
set_parameter(key, val)
else:
playpal_filename = arg
if playpal_filename is None:
print("Usage: %s playpal.lmp > output-file.lmp" % sys.argv[0])
sys.exit(1)
colors = read_palette(sys.argv[1])
palette = read_palette(playpal_filename)
colors = palette
#print_palette(colors)
#sys.exit(0)
# Apply tint, if enabled.
# The tint is intentionally applied *before* the darkening effect is
# applied. This allows us to darken to a different color than the tint
# color, if so desired.
if tint_frac > 0:
colors = blend_colors(palette,
tint_colors(colors, tint_color, tint_bright),
tint_frac)
# Main color ranges
# Generate colormaps for different darkness levels, by blending between
# the default colors and a palette where every entry is the "dark" color.
dark = solid_color_list(dark_color)
for i in range(32):
darken_factor = (32 - i) / 32.0
colormap = generate_darkened_colormap(colors, darken_factor)
output_colormap(colormap)
darkened_colors = blend_colors(dark, colors, darken_factor)
output_colormap(generate_colormap(darkened_colors, palette))
# Print inverse color map
inverse_colormap = generate_colormap(colors, inverse_color)
output_colormap(inverse_colormap)
# Inverse color map for invulnerability effect.
inverse_colors = invert_colors(palette)
output_colormap(generate_colormap(inverse_colors, palette))

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -1 +1 @@
oblivion/fogmap.lmp
cph/misc-lumps/fogmap.lmp

Binary file not shown.

View file

@ -1 +1 @@
cyb/lavamap.lmp
cph/misc-lumps/lavamap.lmp

View file

@ -1 +1 @@
jond/mfademap.lmp
cph/misc-lumps/mfademap.lmp

View file

@ -1 +1 @@
cyb/nukemap.lmp
cph/misc-lumps/nukemap.lmp

Binary file not shown.

View file

@ -1,8 +0,0 @@
fogmap.lmp - a colormap that fades to grey, should be placed between C_START and C_END then invoked using linedef #242, meh.
created by oblivion (thats me) on july 29 2002 using my own short little C program.
copyrights: public domain, cuz i said so.
i am uzi666@juno.com