Full Code of nrdvana/CmdlineGL for AI

master 0fb62ff64bc9 cached
60 files
232.9 KB
75.2k tokens
75 symbols
1 requests
Download .txt
Showing preview only (248K chars total). Download the full file or copy to clipboard to get everything.
Repository: nrdvana/CmdlineGL
Branch: master
Commit: 0fb62ff64bc9
Files: 60
Total size: 232.9 KB

Directory structure:
gitextract_pgnbvikv/

├── .gitignore
├── Changes
├── LICENSE
├── Makefile
├── README.md
├── configure
├── doc/
│   ├── Design.txt
│   ├── Fonts.html
│   └── Intro.html
├── script/
│   ├── Makefile.in
│   ├── build-cmdhash.pl
│   ├── build-consthash.pl
│   ├── build-constlist.sh
│   ├── build-dist.pl
│   ├── build-manual.pl
│   ├── build-version.sh
│   ├── config.h.in
│   ├── configure.ac
│   └── dev-rules.mak
├── share/
│   ├── CmdlineGL.lib
│   ├── examples/
│   │   ├── BrowseFonts.sh
│   │   ├── FlightSim.sh
│   │   ├── ImgCube.sh
│   │   ├── ModelViewer.sh
│   │   ├── Pyramids.sh
│   │   ├── Robot.sh
│   │   └── SpinText.sh
│   ├── lib-bash/
│   │   ├── CmdlineGL.lib
│   │   ├── Cube.lib
│   │   ├── Geom.lib
│   │   ├── LaserBeam.lib
│   │   ├── LinInterpolate.lib
│   │   ├── ModelViewer.lib
│   │   ├── RenderLoop.lib
│   │   ├── Ship.lib
│   │   ├── Timing.lib
│   │   └── Trig.lib
│   └── lib-sh/
│       └── CmdlineGL.lib
├── src/
│   ├── ConstList.Win32.txt
│   ├── ConstList.works_for_me
│   ├── Contained_RBTree.c
│   ├── Contained_RBTree.h
│   ├── Font.c
│   ├── Font.h
│   ├── Global.c
│   ├── Global.h
│   ├── ImageLoader.c
│   ├── ImageLoader.h
│   ├── ParseGL.c
│   ├── ParseGL.h
│   ├── ProcessInput.c
│   ├── ProcessInput.h
│   ├── Server.c
│   ├── Server.h
│   ├── SymbolHash.c
│   ├── SymbolHash.h
│   ├── Version.h
│   ├── manual.head.pod
│   └── manual.tail.pod
└── test/
    └── 01-version.t

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/build/
/src/*.autogen.*
/src/ConstList.txt
/script/configure
/script/config-defs.h.in
/script/autom4te.cache
/dist/


================================================
FILE: Changes
================================================
Version 2.1.0 - 2024-01-02
  * New option "--geometry"
  * Documentation improvements

Version 2.0.0 - 2018-03-25
  * Replace homebrew font support with ftgl library API
  * Rewrote most parsing code to eliminate some buggy edge cases
  * New quoting syntax for command arguments
  * New Divisor syntax and cglPushDivisor replace cglFixedPt
  * Each type of named object is now in its own namespace
  * Use SDL_image in cglLoadImage for broader image format support
  * Stricter parsing of OpenGL calls with open-end argument lists
  * Default projection sets model farther from near clipping plane
  * Option to disable default projection matrix or viewport
  * Notification for window status events
  * Manual page
  * Converted static hash table generator to perl
  * Overhaul of build scripts, mostly borrowed from daemonproxy

Version 1.1.0 - 2006-04-05
  * Added font support
  * Added version numbering
  * Many more examples and bug fixes

Version 1.0.0 - 2006-01-27
  * Converted from GLUT to SDL
  * Able to compile on Win32


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2018, M Conrad
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: Makefile
================================================
MAKE = make
PROVE = prove
PERL = perl

all: build
	$(MAKE) -C build all

clean: build
	$(MAKE) -C build clean

install: build
	$(MAKE) -C build install

build:
	[ -f script/configure ] || { cd script; autoheader; autoconf; }
	[ -d build ] || ./configure

.PHONY: test all clean dist install


================================================
FILE: README.md
================================================
CmdlineGL
=========

CmdlineGL is an interpreter for a "text-friendly" variation of a subset of the
OpenGL 1.4 API, Glut API, and FTGL "C" API.

It reads API calls on stdin, and writes events on stdout, while reporting
errors to stderr.  You can find a description of the available commands and
events in the manual page, and get a list of the functions and constants
supported by a particular build of CmdlineGL using the
``--showcmds`` and ``--showconstants`` options.

Dependencies
============

Before you can build CmdlineGL, you need: SDL, SDL_image, OpenGL, GLU, FTGL,
and the development headers for each.  On Debian/Ubuntu/Mint, these are:

    sudo apt-get install \
        libsdl1.2-dev libsdl-image1.2-dev \
        libgl1-mesa-dev libftgl-dev

and on Fedora they are

    sudo yum install \
        SDL-devel SDL_image-devel \
        mesa-libGL-devel mesa-libGLU-devel ftgl-devel

The autoconf script is still very new, so it might not detect locations of
headers and libraries on other systems.  Patches are welcome.

Building and Installing
=======================

This is almost but not quite a standard autoconf distribution.  All the
autoconf files live in the ./script directory, and the top level project has
a pass-through Makefile and ./configure script that set up an out-of-tree
(well, technically still in the project tree) build in a directory named
"./build".  Anyway, you can just run

    make && sudo make install

from the root of the project and maybe everything will just work.  If you are
running from the distribution tarball, this will create a "production build",
but if you are running it from a git checkout, it will create a "dev build"
with assertions enabled and some extra logging.

You can also invoke autoconf directly:

    mkdir work
    cd work
    $PROJDIR/script/configure [OPTIONS]
    make
    make install

Usage
=====

See the manual page (built during 'make' process) for details about the commands
supported. There is also the [online manual], and [examples directory].

[examples directory]: ./share/examples
[online manual]: https://www.nrdvana.net/cmdlinegl/release/current/CmdlineGL.html


================================================
FILE: configure
================================================
#! /bin/sh

# This configure script is just a place-holder that sets up an out-of-tree
# build in a directory named "build".  The real autoconf configure script is
# located in ./script/
#
# When checked out of git, this enables options "--enable-dev" and
# "--enable-debug" by default.  When distributed as a tarball, this script is
# altered to remove those options.

# if run from outside proj root, inform user whats going on
if [ ! -d script -o ! -d src ]; then
	echo "For out-of-tree builds, run ./script/configure directly."
	exit 1;
fi

mkdir -p build && cd build && ../script/configure --enable-dev --enable-debug "$@"


================================================
FILE: doc/Design.txt
================================================


	New design:

		Server.c
			Everything to do with setting up the window and watching file descriptors.

		ProcessInput.c
			Everything to do with reading input from a file descriptor, and sending it to the ParseGL
			module for execution.

		ParseGL.c
			Everything to do with taking a parsed line of text, and determining whether it can be
			executed, and executing it.

		SymbolHash.c
			All the hash table stuff.


================================================
FILE: doc/Fonts.html
================================================
<html>
<head>
	<title>Working with CmdlineGL Fonts</title>
</head>

<body>
	<h3>Preface</h3>
	<p>In CmdlineGL, I wanted some simple kind of font system that wouldn't be dependant on
	libraries installed on the system, and also not dependant on which SDL modules were
	installed.  So, I went and pulled an old idea (which I've probably seen somewhere)
	making use of control pixels in a bitmap to denote the size/spacing of font characters
	and wrote it up.
	<p>This font system allows you to &quot;make a font&quot; using a plain old bitmap editor,
	or even convert a font from trueType to bitmap by using the text-tool in those same editors
	and then marking the boundaries of the characters.  It expects the ascii characters from 0x20
	(space) up to 0x7F (DEL), 96 characters in total.  This is likely only usable by
	English-speaking folks, unfortunately.  I'd love to extend it to handle a variable number
	of characters, and read UTF8 unicode, but I didn't see any easy way to do this.
	Maybe people with more experience in this area would like to contribute.
	
	<h3>Using the commands</h3>
	<p>I added 2 &quot;GL-ish&quot; commands to work with fonts:
	cglNewFont, and cglText.
	<p>cglNewFont takes three parameters: the type of font, the symbolic name for it, and the file name.
	The type currently must be CGL_BMPFONT, the symbolic name can be any <a href="NamedObjects.html">valid name</a>,
	and the file name is any valid system-dependant file path.

	<p>cglText takes two parameters as well: the name of the font to use, and the text string to render with it.
	As of now, the rendering is very dumb, and only renders lines as they are given.
	I might, in a future version, start handling things like tabs and newlines, or even
	add some options for wrapping.
</body>


================================================
FILE: doc/Intro.html
================================================
<html>
<head>
	<style>
	BODY { background-color:black; color:#55FF55; font-family:"Courier New", monospace, sans-serif; }
	H1,H2 { color:white; font-weight:bold; }
	H4 { color:white; font-weight:normal; }
	A { color:#AAFFAA; text-decoration:underline; }
	A.hover { color:white; }
	DIV.code { color:white; background-color:#555555; border:1px solid blue; margin:0.5em; padding:0.5em; }
	DIV.warning { color:#FFBBBB; background-color:#222222; border:2px solid red; margin:0.5em; padding:0.5em; }
	SPAN.note { color:gray; font-size:70%; font-style:italic; }
	</style>
</head>
<body>
	<center>
	<h2>Introducing</h2>
 	<h1>OpenGL Bindings for BASH</h1>
	</center>
	<h2>"What???"</h2>
	<p>A while back, I came accross a game called <a href="http://www.frozen-bubble.org">Frozen Bubble</a>, which is a <a href="http://www.snood.com">Snood</a>
	clone written in Perl using the OpenGL bindings.  I was rather offended by the
	whole concept, and said something like "Arg! Thats almost as bad as writing a 3D
	game in Bash!".  So, this December during winter break, when I was trying to think
	of something to do for <i>Mike's Abuse of Technology</i> #6....
	</p>
	<table border="0" cellspacing="0" cellpadding="0"><tr><td><h2>"Oh, God!&nbsp;&nbsp;No!!"</h2></td><td><span class="note">&nbsp;&nbsp;&lt;-- (common actual quote)</span></td></tr></table>
	<p>But here it is!  Bwa-Ha-Ha! All the normal languages have OpenGL support,
	and some scripting languages like Perl, Python, and PHP have OpenGL bindings,
 	and now Bash does too!
	</p>
	<h2>"BUT WHY, DAMNIT? WHY??"</h2>
	<p>If you have to ask why, you are not a member of the intended audience.  Please
	go on about your business and accept my apologies for this distraction.
	<br/><span class="note">(tribute to Bob Zimbinski <a href="#credits">[1]</a>)</span>
	</p>
	<h2>"But how?  Bash doesn't support modules..."</h2>
	<p>Right.  So I gave it an interface just like all the others it uses:
	a simple command that can be given parameters and fed commands on standatd input.</p>
	When invoked, CmdlineGL starts a server which creates an OpenGL window, and then either
	reads input from a file or from standard-in.  To deliver commands to it, a script can
	pipe standard-out to the program, or write to a fifo, or other creative methods.
	The script sends lines of text to the server that tell it to execute any of the
	supported OpenGL, GLU, or GLUT API calls.  CmdlineGL supports textures, display lists,
	quadrics, and just about all of the usual gl calls.
	</p>
	<h2>"This sounds <a href="http://www.catb.org/esr/jargon/html/B/Bad-and-Wrong.html">Bad and Wrong</a>"</h2>
	<p>I'd argue for Bad and Right.  Keep reading.
	</p>
	<h2>"So, how bad does the performance suck?"</h2>
	<p>Its definately slower than games written in C, but might not be as slow as you think:
	CmdlineGL is written in straight C with as little overhead as possible, to the point
	of doing all text operations on the same character buffer filled by the call to 'read',
	and sacraficing reusability for performance (and wow, was it ever liberating!).
	It also uses a statically defined hash table to map command names to function pointers.
	To top things off, it uses a <a href="http://gauss.ececs.uc.edu/RedBlack/redblack.html">Red/Black Tree</a> to manage the names of objects
	like display lists, quadrics, textures, and fonts.
	</p>
	<p>Because of the support for display lists, any part of your game that doesn't move
	can be compiled into the OpenGL end and replayed as a single line of text.
	And, in a 3D application, most of the time is spent drawing pixels and texturing them,
	and this is probably done by the graphics card,
	so there's plenty of CPU time left over for running data through a pipe and a socket,
	and running string-to-float conversions,
 	and repeatedly forking hundreds of times per frame per second,
	especially with the speed of today's computers ;-)
	</p>
	<h2>"And you actually wrote a game using this kluge?"</h2>
	<p>Well, mostly just demos, but a game engine of sorts, yes.
	<br><span class="note">(I never finished the flight simulator)</span>
	</p>
	<p>Lets review some of the obstacles
	involved with a real-time game, and our old friend/nemesis Bash:
	<ul>
	<li><h4>Bash needs to receive key/mouse activity from the server.  There are no RPCs</h4>
		<p>The only natural way bash has to receive feedback from the user is a text stream.
		One other possibility is for it to call a special program which would contact the
		server, get some information, and give it back to bash in the form of a integer result
		code, but this seemed particularly slow.  So, I came up with the idea that the server
		could simply write user actions to stdout, and this could be piped to the stdin of the
		script.
		</p>
		<p>I formed this cyclic stream using a fifo:</p>
<div class="code">
$ mkfifo /tmp/CmdlineGL_fifo<br/>
$ Script.sh &lt; /tmp/CmdlineGL_fifo | CmdlineGL &gt; /tmp/CmdlineGL_fifo
</div>
		<p>And then the rest was simple:  script reads user input from stdin, script generates
		frame of output commands to stdout, and repeat.
		</p>
	</li>
	<li><h4>Bash does not support asynchronous IO</h4>
		<p>The next problem of course, was that if the user doesn't type anything, a call
 		to "read" will block.
		Read actually does have a timeout feature, but in granularity of seconds,
		and a timeout of 0 doesn't seem to work.  So, I just changed my design a little
		to ensure that there is always a line to read from stdin. Thus the existance of the
		"cglEcho" command.  At the beginning of the script, and after reading user input on
		each frame, I write "cglEcho&nbsp;_____".  CmdlineGL sees the echo command, and echoes
		the argument back to me.  Then, next time I read through stdin I stop after seeing
		"_____" because it means I've processed a whole frame's worth of user input.
		</p>
		<p>The cglEcho command actually became obsolete, with the arrival of cglGetTime, as you
		will see in my next point.  I left cglEcho as it was, though.
		</p>
	</li>
	<li><h4>Keeping pace with the processor</h4>
		<p>One of the more classic video game problems is making sure that the game runs the
		same speed on all machines.  When playing a video, you need to keep the frame rate
		steady, and when running a game engine you need to make sure that the framerate
		doesn't affect the speed of the gameplay.
		</p>
		<p>Since Bash doesn't really have a way of keeping time (other than 'date', haha)
		I decided to put frame-limiting and time counting on the CmdlineGL side.
		When CmdlineGL starts it marks the current time, and thereafter can subtract that from
		the current time to get elapsed game-time.  There are two ways to take advantage
		of game-time:  The first is using "cglSync&nbsp;T" which tells CmdlineGL to wait until
		time T before processing any more commands.  The second is "cglGetTime", which causes
		CmdlineGL to write the current time (in milliseconds) to stdout.  The script can then
		process time differences to adjust the progress it displays in each frame.
		</p>
		<p>Additionally, there is a cglSleep function, which causes CmdlineGL to sleep for a
		number of milliseconds.  This would be useful in animated text movies to put a pause
		at certian points regardless of the frame rate.
		</p>
	</li>
	<li><h4>Math.. or lack thereof</h4>
		<p>So, one of the first major obstacles I hit when porting my walking robot demo from C
		to bash was the lack of floating point support.
		It seemed obvious that calling bc for all my math operations would be a bit slow.
		At first, I tried writing functions that would string-manipulate a floating point number
		to fixed point, perform the calculation, and then string-manipulate back.  This involved
		a lot of back-quote substitutions, and was slow.  So, I needed a way to use integers.
		Why not just use glScale?  Well, mainly because that won't scale the angles of the
		glRotate command, and the robot was looking a little jerky with whole degree rotations.
		</p>
		<p>So... against my instincts for good design, I added a 'feature' to CmdlineGL that
		causes it to look for fixed-point numbers any place it would normally expect floating point.
		Implementing this was rather simple, though: I made a "cglFixedPt &lt;x&gt;" function that
		takes a parameter X, calculates 1/X, and multiplies all future floating point numbers
		by this amount.  The fact that the script is sending integers is then irrelevant.
		</p>
		<p>
	</li>
	</ul>
	<h2>"How much time did you waste on this??"</h2>
	<p>Less time than I wasted on World of Warcraft</p>
	<h2>"So, what can I do to... 'Repay' you for this?"</h2>
	<p>If you were thinking along the lines of thanks: bug fixes, implementing more of
	the OpenGL commands, bug reports, money, pizza, sending Morrowgrain to Zellin
	on Earthen Ring (if you don't know what that means, don't worry about it), etc.
	</p>
	<p>If you were thinking more along the lines of revenge, you could try using CmdlineGL
 	to write a multiplayer racing game (like a Mario Cart clone or something) in Prolog.
	</p>
	<h2>"Where's the Windows version?"</h2>
	<p>The Windows version is the same as the Linux version.
	You'll just need to edit the source code a little before you can run it.  ;-)
	</p>
	<p>At the moment, the unix-specific things in this code are the asynchronous IO,
	the fifo's, and the timing.  If you replace all the 'open' and 'read' with CreateFile and
	ReadFile, comment out the fifo stuff, and replace gettimeofday and usleep with GetCurrentTime
	and Sleep (remember that it'll change microseconds to milliseconds), it just might work on
	Windows.  Then all you need is bash, which can be gotten from Cygwin.  You will still need
	some way of creating the stdin/stdout loop between the script and CmdlineGL, and I'm not
	going to even investigate that.
	</p>
	<p>I really can't see any reason why you'd want to do this on Windows (unless
	perhaps your graphics card isn't accelerated under X).  One alternative is to
	install Cygwin and then export the display of your script to the windows box.
	That only works of you have two machines, of course.  Anyway, it's your time,
	but if you get a windows version working I'll gladly host it.
	</p>
	<h2 id="credits">Credits</h2>
	<ul><li>Bob Zimbinski, for <a href="https://web.archive.org/web/20100127215948/http://webpages.mr.net/bobz/ttyquake/">inspiring the format</a> of this page.</li>
	<li>Jeff Molofee, of <a href="http://nehe.gamedev.net/">nehe.gamedev.net</a>, for providing an excellent OpenGL resource to the community.</li>
	<li>All the fellow nerds at (the former) UC's Labratory for Recreational Computing whose looks of pure horror and disgust inspired me to finish the project.</li>
	</ul>
</body>
</html>


================================================
FILE: script/Makefile.in
================================================
SHELL = /bin/sh
PERL = perl
POD2MAN = pod2man
PROVE = prove
GZIP = gzip
INSTALL = install
CURRENT_UNIX_TIMESTAMP = $(shell date "+%s")

all: CmdlineGL CmdlineGL.1

.SUFFIXES:
.SUFFIXES: .c .o

prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
projroot= @srcdir@/..
srcdir = @srcdir@/../src
scriptdir = @srcdir@
docdir = @srcdir@/../doc
datarootdir = @datarootdir@
sysconfdir = @sysconfdir@
localstatedir = @localstatedir@
runstatedir = $(localstatedir)/run
mandir = @mandir@

OBJ_FILES=SymbolHash.o Server.o ProcessInput.o ParseGL.o Global.o Contained_RBTree.o IntConstHash.autogen.o CmdHash.autogen.o ImageLoader.o Font.o
AUTOGEN_SRC=IntConstHash.autogen.c CmdHash.autogen.c Version.autogen.c ConstList.txt
COMMAND_SOURCES:=$(shell grep -l COMMAND $(srcdir)/*.c | sort -r )

CFLAGS = @CFLAGS@ -MMD -MP -Wall
CPPFLAGS = @CPPFLAGS@ -I. -I$(srcdir)
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@

DEP_FILES := $(shell touch _empty.d; echo *.d)
include $(DEP_FILES) @dev_include_makefile@

%.autogen.o: %.autogen.c
	$(CC) -o $@ $(CPPFLAGS) -c $< $(CFLAGS)

%.o: $(srcdir)/%.c
	$(CC) -o $@ $(CPPFLAGS) -c $< $(CFLAGS)

CmdlineGL: $(AUTOGEN_SRC) $(OBJ_FILES)
	$(CC) -o $@ $(CPPFLAGS) -DCURRENT_UNIX_TIMESTAMP=$(CURRENT_UNIX_TIMESTAMP) $(OBJ_FILES) Version.autogen.c $(CFLAGS) $(LDFLAGS) $(LIBS)

# no deps for ConstList.txt because users might need to edit it by hand
ConstList.txt:
	@echo "-> Attempting to get the useful #define'd constants from your GL headers..."
	PROJROOT="$(projroot)" CPP="$(CPP)" CFLAGS="$(CFLAGS)" CPPFLAGS="$(CPPFLAGS)" $(scriptdir)/build-constlist.sh <$(srcdir)/ConstList.works_for_me >$@.tmp && mv $@.tmp $@

IntConstHash.autogen.o: IntConstHash.autogen.c

IntConstHash.autogen.c: ConstList.txt $(scriptdir)/build-consthash.pl
	@echo "-> Regenerating IntConstHash.autogen.c from the names in ConstList.txt"
	$(PERL) $(scriptdir)/build-consthash.pl <ConstList.txt >$@.tmp && mv $@.tmp $@

CmdHash.autogen.o: CmdHash.autogen.c

CmdHash.autogen.c: $(COMMAND_SOURCES) $(scriptdir)/build-cmdhash.pl
	@echo "-> Regenerating CmdHash.autogen.c from the \"COMMAND\" functions in *.c"
	cat $(COMMAND_SOURCES) | $(PERL) $(scriptdir)/build-cmdhash.pl >$@.tmp && mv $@.tmp $@

Version.autogen.c: $(projroot)/Changes $(scriptdir)/build-version.sh
	PROJROOT="$(projroot)" $(scriptdir)/build-version.sh >$@.tmp && mv $@.tmp $@

CmdlineGL.lib: $(srcdir)/

CmdlineGL.1: $(srcdir)/manual.head.pod $(COMMAND_SOURCES) $(srcdir)/manual.tail.pod Version.autogen.c
	$(PERL) $(scriptdir)/build-manual.pl --as=man --version="`sed -n '/CGLVER_String=/s/.*="\(.*\)".*/\1/p' Version.autogen.c`" $^ >$@.tmp && mv $@.tmp $@

CmdlineGL.html: CmdlineGL.1
	man ./$^ | man2html >$@.tmp && mv $@.tmp $@

CmdlineGL.1.gz: CmdlineGL.1
	$(GZIP) < CmdlineGL.1 > $@.tmp && mv $@.tmp $@

install: CmdlineGL CmdlineGL.1.gz
	$(INSTALL) -d "$(DESTDIR)$(bindir)/"
	$(INSTALL) -d "$(DESTDIR)$(mandir)/man1/"
	$(INSTALL) -d "$(DESTDIR)$(datarootdir)/CmdlineGL/"
	$(INSTALL) -m 755 CmdlineGL "$(DESTDIR)$(bindir)/"
	$(INSTALL) -m 644 CmdlineGL.1.gz "$(DESTDIR)$(mandir)/man1/"
	touch "$(DESTDIR)$(datarootdir)/CmdlineGL/something" && rm -r "$(DESTDIR)$(datarootdir)/CmdlineGL/"*
	cp -r "$(projroot)/share/"* "$(DESTDIR)$(datarootdir)/CmdlineGL/"
	sed -e 's|@share_path@|$(DESTDIR)$(datarootdir)/CmdlineGL|g' <'$(projroot)/share/CmdlineGL.lib' >'$(DESTDIR)$(datarootdir)/CmdlineGL/CmdlineGL.lib'
	ln -sf "$(DESTDIR)$(datarootdir)/CmdlineGL/CmdlineGL.lib" "$(DESTDIR)$(bindir)/"

Makefile: config.status $(scriptdir)/Makefile.in $(scriptdir)/config.h.in $(scriptdir)/config-defs.h.in
	./config.status && touch Makefile

config.status: $(scriptdir)/configure $(scriptdir)/config-defs.h.in
	$(scriptdir)/configure $$(./config.status --config)

test: CmdlineGL
	$(PROVE) -j4 $(srcdir)/../test

clean:
	rm -f -- *.o
	rm -f -- *.d
	rm -f -- CmdlineGL*
	rm -f -- *.tmp

.PHONY: install test clean


================================================
FILE: script/build-cmdhash.pl
================================================
#! /usr/bin/env perl

=head1 DESCRIPTION

Generates static hash table of commands by parsing C source.

=cut

use strict;
use warnings;

my @commands;

while (<STDIN>) {
	# Look for COMMAND(cmd, "args")
	push @commands, { name => $1, arg_format => $2 }
		if ($_ =~ m|^\s*COMMAND\s*\(\s*(\S+)\s*,\s*"(\S*)"\s*\)|);
}
@commands= sort { $a->{name} cmp $b->{name} } @commands;

# table size is 1.5 x number of entries rounded up to power of 2.
my $mask= int(2 * @commands);
$mask |= $mask >> 1;
$mask |= $mask >> 2;
$mask |= $mask >> 4;
$mask |= $mask >> 8;
$mask |= $mask >> 16;
my $scan_dist= 1;
my $table_size= $mask+1+$scan_dist;

sub build_table {
	my ($mul, $shift)= @_;
	my @table= (0) x $table_size;
	name: for my $ci (0..$#commands) {
		my $bucket= hash_fn($commands[$ci]{name}, $mul, $shift);
		for (0..$scan_dist) {
			if (!$table[$bucket+$_]) {
				$table[$bucket+$_]= $ci+1; # 1-based
				next name;
			}
		}
		return undef;
	}
	return \@table;
}

sub find_collisionless_hash_params {
	# pick factors for the hash function until each command has a unique bucket
	for (my $mul= 1; $mul < $table_size*$table_size; $mul++) {
		for (my $shift= 1; $shift < 11; $shift++) {
			my $table= build_table($mul, $shift);
			return ( $table, $mul, $shift )
				if $table;
		}
	}
	die "No value of \$shift / \$mul results in unique codes for each command\n";
}

my ($table, $mul, $shift)= find_collisionless_hash_params();

my $cmd_prototypes= join '', map "extern bool cmd_$_->{name}(struct ParseParamsResult *parsed);\n", @commands;
my $i= 1;
my $list_items=     join '', map sprintf(qq|/* %4d */ { "%s", "%s", cmd_%s },\n|, $i++, $_->{name}, $_->{arg_format}, $_->{name}), @commands;
my $hash_entries= '';
for (0..$#$table) {
	$hash_entries .= "\n" if $_ && !($_ & 0xF);
	$hash_entries .= sprintf(" %4d,", $table->[$_]);
}

# This must be kept in sync with the C version.
# Use a mask similar to a 32-bit register's effect to make sure Perl
# behaves the same way as C regardless of Perl's integer width, and
# then tell C to explicitly use 32-bit math.
sub hash_fn {
	my ($string, $mul, $shift)= @_;
	use integer;
	my $i32_mask= (1<<(32-$shift))-1;
	my $x= 0;
	for (unpack( 'C' x length($string), $string )) {
		$x= ((($x * $mul) >> $shift) & $i32_mask) + $_;
	}
	return $x & $mask;
}

print <<END;
// File generated by $0
//
// ${\scalar @commands} commands
// table size is $table_size, mul is $mul, shift is $shift
#include "config.h"
#include <inttypes.h>
#include <string.h>
#include "ProcessInput.h"
#include "SymbolHash.h"

int CmdHashFunc(const char *name) {
	uint32_t x= 0;
	while (*name)
		x= ((x * $mul) >> $shift) + (*name++ & 0xFF);
	return x & $mask;
}

$cmd_prototypes

const int CmdListCount= ${\scalar @commands};
const CmdListEntry CmdList[]= {
	{ NULL, NULL, NULL },
$list_items
	{ NULL, NULL, NULL },
};
const int CmdHashTableSize= $table_size;
const uint16_t CmdHashTable[]= {
$hash_entries
	0
};

const CmdListEntry *GetCmd(const char *Name) {
	int code= CmdHashFunc(Name);
	int lim= code + $scan_dist + 1;
	/* scan forward at most $scan_dist table entries looking for the given Name.
	 * No need to wrap, because the table is longer than the hash function mask. */
	while (code < lim) {
		if (CmdHashTable[code] && strcmp(CmdList[CmdHashTable[code]].Name, Name) == 0)
			return &CmdList[CmdHashTable[code]];
		code++;
	}
	return NULL;
}

END


================================================
FILE: script/build-consthash.pl
================================================
#! /usr/bin/env perl

=head1 DESCRIPTION

Generates static hash table of commands by parsing C source.

=cut

use strict;
use warnings;

my %consts;

while (<STDIN>) {
	$_ =~ /(\w+)/ and $consts{$1}= 1;
}
my @constlist= sort keys %consts;

# table size is 2 x number of entries rounded up to power of 2.
my $mask= int(2 * @constlist);
$mask |= $mask >> 1;
$mask |= $mask >> 2;
$mask |= $mask >> 4;
$mask |= $mask >> 8;
$mask |= $mask >> 16;
my $scan_dist= 4;
my $table_size= $mask+1+$scan_dist;

sub build_table {
	my ($mul, $shift)= @_;
	my @table= (0) x $table_size;
	name: for my $ci (0..$#constlist) {
		my $bucket= hash_fn($constlist[$ci], $mul, $shift);
		for (0..$scan_dist) {
			if (!$table[$bucket+$_]) {
				$table[$bucket+$_]= $ci+1; # 1-based index
				next name;
			}
		}
		return undef;
	}
	return \@table;
}

sub find_collisionless_hash_params {
	# pick factors for the hash function until each command has a unique bucket
	for (my $mul= 1; $mul < $table_size*$table_size; $mul++) {
		for (my $shift= 1; $shift < 5; $shift++) {
			my $table= build_table($mul, $shift);
			return ( $table, $mul, $shift )
				if $table;
		}
	}
	die "No value of \$shift / \$mul results in unique codes for each command\n";
}

my ($table, $mul, $shift)= find_collisionless_hash_params();

my $i= 1;
my $const_entries= join("\n", map sprintf(' /* %4d */ { "%s", (long)(%s) },', $i++, $_, $_), @constlist);

my $hash_entries= '';
for (0..$#$table) {
	$hash_entries .= "\n" if $_ && !($_ & 0xF);
	$hash_entries .= sprintf(" %4d,", $table->[$_]);
}

# This must be kept in sync with the C version.
# Use a mask similar to a 32-bit register's effect to make sure Perl
# behaves the same way as C regardless of Perl's integer width, and
# then tell C to explicitly use 32-bit math.
sub hash_fn {
	my ($string, $mul, $shift)= @_;
	use integer;
	my $i32_mask= (1<<(32-$shift))-1;
	my $result= 0;
	$result= ((($result * $mul) >> $shift) & $i32_mask) + ($_ << 4)
		for unpack( 'C' x length($string), $string );
	return $result & $mask;
}

print <<END;
// File generated by $0
//
// ${\scalar @constlist} constants
// table size is $table_size, mul is $mul, shift is $shift, scan_dist is $scan_dist
#define INCLUDE_SDL
#define INCLUDE_GL
#include "config.h"
#include "ProcessInput.h"
#include "SymbolHash.h"

int IntConstHashFunc(const char *name) {
	uint32_t x= 0;
	while (*name)
		x= ((x * $mul) >> $shift) + ((*name++ & 0xFF) << 4);
	
	return x & $mask;
}

const int IntConstListCount= ${\scalar @constlist};
const IntConstListEntry IntConstList[]= {
	{ NULL, 0 },
$const_entries
	{ NULL, 0 }
};
const int IntConstHashTableSize= $table_size;
const uint16_t IntConstHashTable[]= {
$hash_entries
};

const IntConstListEntry *GetIntConst(const char *Name) {
	int code= IntConstHashFunc(Name);
	int lim= code + $scan_dist + 1;
	/* scan forward at most $scan_dist table entries looking for the given Name.
	 * No need to wrap, because the table is longer than the hash function mask. */
	while (code < lim) {
		if (IntConstHashTable[code] && strcmp(IntConstList[IntConstHashTable[code]].Name, Name) == 0)
			return &IntConstList[IntConstHashTable[code]];
		code++;
	}
	return NULL;
}

END


================================================
FILE: script/build-constlist.sh
================================================
#! /bin/sh
set -eu

[ -n "${CPP}" ] && [ -n "$PROJROOT" ] \
	|| { echo "Require variables PROJROOT, CPP, CFLAGS, CPPFLAGS" >&2; exit 1; }

{
	echo "#define INCLUDE_SDL";
	echo "#define INCLUDE_GL";
	echo "#include \"config.h\"";
	while read ConstName; do
			echo "#ifdef $ConstName";
			echo "validconst_$ConstName";
			echo "#endif";
	done;
} | ${CPP} ${CFLAGS} ${CPPFLAGS} - | grep validconst_ \
  | sed -e 's/validconst_\(.*\)$/\1/'

(
echo
echo "  ConstList.txt has been generated by taking my own machine's valid list of"
echo "  constants and running it through your preprocessor.  Hopefully it works"
echo "  for you.  If not, try grepping your GL header files for #define GL*, and"
echo "  put the name of every constant you'd like to have at runtime into the"
echo "  ConstList.txt file."
echo "  Note that CmdlineGL can only use integer constants, and not all GL_* are."
echo "  Also beware: 'make clean' will remove it!"
echo
) >&2


================================================
FILE: script/build-dist.pl
================================================
#! /usr/bin/perl
use strict;
use warnings;
open(STDERR, '>&STDOUT');

# Run a command like 'make', but capture the output, and show the output if it fails.
sub run {
	my ($out, $cmd_fh);
	local $/= undef;
	unless (open($cmd_fh, '-|', @_) and do { $out= <$cmd_fh>; close $cmd_fh }) {
		$|= 1;
		print $out;
		my $exitreason= $? == -1? "exec: $!" : $? & 0x7F? "died on signal ".($?&0x7F) : "exited with code ".($?>>8);
		die "Command failed: \"".join('" "', @_)."\": $exitreason\n";
	}
	$out;
}

my $proj_root= $ENV{PROJROOT};
-d "$proj_root/src" and -d "$proj_root/script"
	or die "Incorrect PROJROOT \"$proj_root\"\n";

# Verify we have all changes checked in

my $uncommitted= run('git',"--git-dir=$proj_root/.git","--work-tree=$proj_root",'status','--porcelain');
$uncommitted =~ /\S/
	and die "Uncommitted git changes!";

# Git HEAD should be tagged same as Changes file

chomp(my $git_head= run('git','log','-n','1','--format=format:%H%d'));
$git_head =~ /tag: v([^,) ]+)/ or die "HEAD lacks a tag: \"$git_head\"\n";
my $git_ver= $1;
open(my $changes_fh, '<', "$proj_root/Changes") or die "open(Changes): $!";
my $changes_ver_line= <$changes_fh>;
$changes_ver_line =~ /Version ([^. ]+\.[^. ]+\.[^. ]+) / or die "Unexpected format in Changes: \"$changes_ver_line\"\n";
my $changes_ver= $1;

$git_ver eq $changes_ver or die "Version Mismatch between git ($git_ver) and Changes ($changes_ver)\n";

# Clone project into a temporary directory

run('rm', '-rf', "$proj_root/dist/next");
mkdir "$proj_root/dist";
mkdir "$proj_root/dist/next";
my $dest_dir= "$proj_root/dist/next";

my $out;
$out= run('git',"--work-tree=$dest_dir","--git-dir=$proj_root/.git",'checkout','.');

# Remove debug and dev configuration that we added by default

run('sed', '-i', '-e', 's/--enable-debug/--disable-debug/', "$dest_dir/configure");

# Test that we can compile and build it

run('make','-C',$dest_dir,'all');
run('make','-C',"$dest_dir/build",'test');

# If that worked, wipe the build dir and zip it!

my $distname= "CmdlineGL-$changes_ver";
run('rm', '-rf', "$dest_dir/build");
rename $dest_dir, "$proj_root/dist/$distname" or die "rename to \"$proj_root/dist/$distname\" failed: $!";
run('tar', '-C', "$proj_root/dist", '-cjf', "$proj_root/dist/$distname.tar.bz2", $distname);

print "\nBuilt $proj_root/dist/$distname.tar.bz2\n\n";



================================================
FILE: script/build-manual.pl
================================================
#! /usr/bin/env perl
use strict;
use warnings;
use Pod::Man;
use Getopt::Long;

GetOptions(
	'as=s'      => \(my $opt_as),
	'version=s' => \(my $opt_version),
	'help|h'    => sub { print "Usage:  $0 [--as=pod|man] header.pod *.c footer.pod\n\n"; exit 1; },
) or die;

# Merge all identically named head1 and head2 sections.  This allows multiple source files
# to contribute to the same section of documentation without needing to rearrange the code
# to match, and lets the manual.head.pod determine where everything will go in the document.
my @head1;
my %head1_by_name;
my $current_h1;
my $current_h2;
my $current_pod;
while (<>) { # there are nicer ways to do this, but I'm aiming for no non-core deps
	if ($_ =~ m{^(/\*)?=(\w+)\s*(.*?)\s*$}) {
		$_= substr($_, 2) if $1;
		if ($2 eq 'head1') {
			$current_h2= undef;
			$current_h1= $head1_by_name{$3} ||= do {
				my $h1= { name => $3, pod => '', head2 => [], head2_by_name => {} };
				push @head1, $h1;
				$h1;
			};
			$current_pod= \$current_h1->{pod};
		}
		elsif ($2 eq 'head2') {
			defined $current_h1 or die "got head2 before head1";
			$current_h2= $current_h1->{head2_by_name}{$3} ||= do {
				my $h2= { name => $3, pod => '', items => [] };
				push @{ $current_h1->{head2} }, $h2;
				$h2;
			};
			$current_pod= \$current_h2->{pod};
		}
		elsif ($2 eq 'item' and $current_h1->{name} eq 'COMMANDS' && defined $current_h2) { # only capture =item if it was part of COMMAND section
			my $i= { name => $3, pod => '' };
			push @{ $current_h2->{items} }, $i;
			$current_pod= \$i->{pod};
		}
		# if "cut", end the current part
		elsif ($2 eq 'cut') {
			$current_pod= undef;
		}
		elsif (!defined $current_pod) {
			die "Found POD directive with no current head1/head2/item: $_";
		}
		else {
			$$current_pod .= $_;
		}
	}
	elsif ($current_pod) {
		$$current_pod .= $_;
	}
}

# Now re-flatten the sections into a document
my $doc= '';
for my $h1 (@head1) {
	$doc .= "=head1 $h1->{name}\n\n$h1->{pod}\n\n";
	for my $h2 (@{ $h1->{head2} }) {
		$doc .= "=head2 $h2->{name}\n\n$h2->{pod}\n\n";
		if (@{ $h2->{items} }) {
			$doc .= "=over\n\n";
			for my $item (@{ $h2->{items} }) {
				$doc .= "=item $item->{name}\n\n$item->{pod}\n\n";
			}
			$doc .= "=back\n\n";
		}
	}
}

# remove redundant empty lines
$doc =~ s/\n\n\n+/\n\n/g;

# Either output POD or run it through Pod::Man
if (lc($opt_as) eq 'pod') {
	print $doc;
}
elsif (lc($opt_as) eq 'man') {
	open( my $doc_fh, '<', \$doc ) or die;
	Pod::Man->new(
		name => 'CmdlineGL',
		release => $opt_version,
		section => 1,
		center => 'General Commands',
		errors => 'die',
	)->parse_from_file($doc_fh);
}
elsif (lc($opt_as) eq 'html') {
	require Pod::Html;
	require File::Temp;
	my $tmp= File::Temp->new;
	$tmp->print($doc);
	$tmp->seek(0,0);
	my $html_tmp= File::Temp->new;
	Pod::Html::pod2html("--infile=$tmp", "--outfile=$html_tmp", "--title=CmdlineGL");
	$html_tmp->seek(0,0);
	local $/= undef;
	my $html= <$html_tmp>;
	my $html_head= <<END;
<!DOCTYPE html>
<html>
<head>
  <title>CmdlineGL</title>
  <link rel="stylesheet" href="yamb.css" type="text/css" />
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <style>
  html { margin: 0 2%; }
  body { margin: .5em auto 2em auto; max-width: 60em; }
  dl { margin: 0 .5em 0 0; }
  dt { margin: 0; font-family: "Consolas", "Inconsolata", "Courier New", monospace; }
  dd { margin: .5em 1em 1em 2em; }
  ul, ol { padding-left: 2.5em; list-style-position: outside; }
  #index { float: right; margin: 2em 1em;}
  #index, #index ul { list-style-type: none; padding-left: 1em; }
  code { max-width: 50em; }
  pre { margin: .5em 0; }
  </style>
</head>
END
	$html =~ s,.*?</head>,$html_head,s;
	print $html;
}
else {
	die "Unknown format $opt_as"
}


================================================
FILE: script/build-version.sh
================================================
#! /bin/sh

[ -n "$PROJROOT" ] || { echo "Require environment var PROJROOT" >&2; exit 1; }

echo " -> Checking Version" >&2
version_parts=$(grep -i version $PROJROOT/Changes | head -n 1 | sed -ne 's/Version *\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)[^0-9]*\([-0-9]*\)/\1 \2 \3 \4/p')
read major minor release date <<END
$version_parts
END
extra=''
if [ -z "$major" -o -z "$date" ]; then
	echo "Can't get version from ./Changes" >&2
	exit 1
fi
echo "    Changes version is $major.$minor.$release ($date)" >&2

if [ -d "$PROJROOT/.git" ]; then
	export GIT_DIR="$PROJROOT/.git"
	export GIT_WORK_TREE="$PROJROOT"
	head_hash=$(git log -n 1 --format=format:%h)
	tag_hash=$(git log -n 1 --format=format:%h v$major.$minor.$release -- 2>/dev/null)
	git_dirty=$(git status --porcelain | wc -l)
	if [ "$git_dirty" -gt 0 -o "$tag_hash" != "$head_hash" ]; then
		echo "    Git state differs from ./Changes" >&2
		extra="$head_hash"
		date="$(git log -n 1 --format=format:%ci | sed -e 's/ /T/;s/ //')"
		if [ "$git_dirty" -gt 0 ]; then
			extra="$extra+changes"
			date=`date "+%Y-%m-%dT%H:%M:%S"`;
		fi
	fi
fi

echo "    Version is $major $minor $release $extra $date" >&2;

[ -n "$extra" ] && suffix="-$extra" || suffix='';

echo " -> Regenerating Version.c" >&2
cat <<END
/* Generated by build-version.sh */

const int CGLVER_Major=$major;
const int CGLVER_Minor=$minor;
const int CGLVER_Release=$release;
const char* CGLVER_Extra="$extra";
const char* CGLVER_Date="$date";
const char* CGLVER_String="$major.$minor.$release$suffix";

END


================================================
FILE: script/config.h.in
================================================
/* This header pulls in everything system-dependent.  No other files in the project
 * should depend on any header other than this and the headers in the src/ directory.
 */

/* contains autoconf -D equivalents */
#include <config-defs.h>

/* ------------------------------------
 * Basics
 */

#ifdef _WIN32
#  define WIN32_LEAN_AND_MEAN
#  include <windows.h>
#else
#  ifndef _GNU_SOURCE
#    define _GNU_SOURCE
#  endif
#  include <sys/un.h>
#  include <sys/types.h>
#  include <sys/stat.h>
#  include <unistd.h>
#  include <fcntl.h>
#  include <errno.h>
#endif

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>

/* ------------------------------------
 * Caller can set INCLUDE_GL before config.h to pull in all the GL headers
 */

#include <@gl_header@>
#include <@glu_header@>

/* These were missing on some platforms and this worked to solve it... */
#ifndef GL_BGR
#  define GL_BGR GL_BGR_EXT
#  define GL_BGRA GL_BGRA_EXT
#endif

#ifdef HAVE_LIBFTGL
#  include <@ftgl_header@>
#endif

/* ------------------------------------
 * Caller can set INCLUDE_SDL before config.h to pull in SDL headers
 */

#ifdef INCLUDE_SDL
#  include <@sdl_header@>
#  ifdef HAVE_LIBSDL_IMAGE
#    include <@sdl_image_header@>
#  endif
#endif

/* ------------------------------------
 * Now muck around with things we shouldn't, since we're not including
 * any more system headers beyond this point.
 */

#ifndef NULL
#  define NULL ((void*)0)
#endif

#if HAVE_STDBOOL
#  include <stdbool.h>
#else
#  ifndef bool
#    define bool int
#  endif
#  ifndef true
#    define true 1
#  endif
#  ifndef false
#    define false 0
#  endif
#endif


================================================
FILE: script/configure.ac
================================================
AC_PREREQ([2.68])
AC_INIT([CmdlineGL])
AC_CONFIG_SRCDIR([config.h.in])
AC_CONFIG_HEADER([config-defs.h])

AC_ARG_ENABLE(debug,  AS_HELP_STRING([debug], [enable assertions and debug symbols]),
 [ if test "$enableval" != "no"; then CFLAGS="$CFLAGS -O0 -g3"; else CFLAGS="$CFLAGS -O2 -DNDEBUG"; fi; ],
 [ CFLAGS="$CFLAGS -O0 -g3"; ])

AC_ARG_ENABLE(dev,    AS_HELP_STRING([dev], [enable source generators (requires perl)]),
 [ if test "$enableval" != "no"; then dev_include_makefile="\$(scriptdir)/dev-rules.mak"; fi; ],
 [ dev_include_makefile=""; ])
AC_SUBST(dev_include_makefile)

# Checks for programs.
AC_PROG_CC

# Checks for libraries.
AC_CHECK_LIB(GL,glBegin)
AC_CHECK_LIB(GLU,gluNewQuadric)
AC_CHECK_LIB(ftgl,ftglCreateTextureFont)
LDFLAGS="${LDFLAGS} `sdl-config --libs`"
AC_CHECK_LIB(SDL,SDL_Init)
AC_CHECK_LIB(SDL_image,IMG_Load)

# Checks for header files.
AC_PATH_X
if test "X${x_includes}" != "X"; then
	CFLAGS="${CFLAGS} -I${x_includes}"
fi
AC_HEADER_STDC
AC_CHECK_HEADERS([fcntl.h stdlib.h unistd.h stdbool.h stdint.h])
AC_CHECK_HEADERS([GL/gl.h GL/glu.h],[AC_SUBST(gl_header,GL/gl.h) AC_SUBST(glu_header,GL/glu.h)],[AC_CHECK_HEADERS([OpenGL/gl.h OpenGL/glu.h],[AC_SUBST(gl_header,OpenGL/gl.h) AC_SUBST(glu_header,OpenGL/glu.h)],[AC_MSG_ERROR([Can't find OpenGL headers])])])
CFLAGS="${CFLAGS} `sdl-config --cflags`"
AC_CHECK_HEADERS([SDL/SDL.h],[AC_SUBST(sdl_header,SDL/SDL.h)],[AC_MSG_ERROR([Can't find SDL header (apt-get install libsdl1.2-dev)])])
AC_CHECK_HEADERS([SDL/SDL_image.h],[AC_SUBST(sdl_image_header,SDL/SDL_image.h)],[AC_MSG_ERROR([Can't find SDL_image header (apt-get install libsdl-image1.2-dev)])])
CFLAGS="${CFLAGS} -I/usr/include/freetype2"
CPPFLAGS="${CPPFLAGS} -I/usr/include/freetype2"
AC_CHECK_HEADERS([FTGL/ftgl.h],[AC_SUBST(ftgl_header,FTGL/ftgl.h)],[AC_MSG_ERROR([Can't find FTGL header (apt-get install libftgl-dev)])])

# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
AC_C_CONST
AC_C_INLINE

# Checks for library functions.
AC_CHECK_FUNCS([snprintf])

AC_CONFIG_FILES([Makefile config.h])
AC_OUTPUT


================================================
FILE: script/dev-rules.mak
================================================
# These rules are only needed for authoring CmdlineGL and don't need to be
# active in the release tarball.  I wanted the auto-generated C files to
# go here as well, but the hash tables depend on the available libraries
# on the host system, so can't be generated in advance.

$(scriptdir)/config-defs.h.in: $(scriptdir)/configure.ac
	cd $(scriptdir) && autoheader && touch $(scriptdir)/config-defs.h.in

$(scriptdir)/configure: $(scriptdir)/configure.ac
	cd $(scriptdir) && autoconf

autogen_files: $(scriptdir)/config-defs.h.in $(scriptdir)/configure

dist: $(srcdir)/../.git
	env PROJROOT=$(srcdir)/.. $(PERL) $(scriptdir)/build-dist.pl

.PHONY: autogen_files dist


================================================
FILE: share/CmdlineGL.lib
================================================
#! /bin/bash
# This script is a trampoline to detect shells and versions
# ...but only Bash 3+ is supported at the moment.

# First, make sure this script is being sourced
case "$0" in
*/CmdlineGL.lib)
	echo "Usage: source /path/to/CmdlineGL.lib"
	exit 1
	;;
esac

# Then figure out where the lib path is
CmdlineGL_SharePath="@share_path@" # set by "make install"
if [ ! -d "@share_path@" ]; then
	# Running from project dir, before "make install"
	CmdlineGL_SharePath="${BASH_SOURCE%/*}"
fi
if [ ! -d "$CmdlineGL_SharePath/lib-bash" ]; then
	echo "Can't determine CmdlineGL share path (checked for '$CmdlineGL_SharePath/lib-bash')"
	return 1
fi

# Force safe defaults for paths.  Caller can modify these afterward.
CmdlineGL_TexPath="$CmdlineGL_SharePath/textures"
CmdlineGL_FontPath="$CmdlineGL_SharePath/fonts"
# Then load the lib appropriate for this shell.  Right now bash is the only supported shell.
if [ -n "$BASH_VERSION" ]; then
	CmdlineGL_LibPath="$CmdlineGL_SharePath/lib-bash"
	source "$CmdlineGL_SharePath/lib-bash/CmdlineGL.lib"
else
	echo "You must source this libary from bash."
	echo "No other shells are currently supported."
	return 1;
fi


================================================
FILE: share/examples/BrowseFonts.sh
================================================
#! /bin/bash
#
#  This example displays each font in the /usr/share directory by
#  rendering it as an extruded font.  Iterate through the list with
#  '[' and ']' keys.
#
text="$1";
set -eu

source "${BASH_SOURCE%/*}/../CmdlineGL.lib" || die "Can't find CmdlineGL.lib (${BASH_SOURCE%/*}/../CmdlineGL.lib)";
CmdlineGL_LoadLib RenderLoop ModelViewer

fonts=( `find /usr/share -name '*.ttf' | grep -i mono` )
font_n=${#fonts[@]}
font_i=

swap_font() {
	if [[ -n "$font_i" ]]; then ftglDestroyFont font1; else font_i=0; true; fi
	echo "$font_i/$font_n  ${fonts[font_i]}"
	# Load font file and configure font rendering parameters
	ftglCreateExtrudeFont font1 "${fonts[font_i]}"
	ftglSetFontFaceSize   font1 72 72
	ftglSetFontDepth      font1 20
}

next_font() {
	if (( font_i + 1 < font_n )); then
		let ++font_i
		swap_font
	fi
}
prev_font() {
	if (( font_i > 0 )); then
		let font_i--
		swap_font
	fi
}

Init() {
	# Initialize CmdlineGL for rendering only (no input or feedback)
	glEnable GL_NORMALIZE GL_DEPTH_TEST GL_CULL_FACE
	glShadeModel GL_SMOOTH

	# set up lighting (otherwise no change as it rotates)
	glEnable GL_LIGHTING GL_LIGHT0
	glLight GL_LIGHT0 GL_AMBIENT .8 .8 .8 0
	glLight GL_LIGHT0 GL_DIFFUSE 1 .8 .8 0
	glLight GL_LIGHT0 GL_SPECULAR .8 .8 .8 0
	glLight GL_LIGHT0 GL_POSITION 10 10 10 1
	swap_font
}

RenderLoop_Render() {
	ModelViewer_Update
	
	glLoadIdentity
	ModelViewer_ApplyMatrix
	
	glTranslate -40 0 0
	glScale 1/40
	glColor 0.5 0.5 0.5 1
	ftglRenderFont font1 "${fonts[font_i]}" FTGL_RENDER_ALL
	
	glFlush
	cglSwapBuffers
	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT
}

RenderLoop_DispatchEvent() {
	if ! ModelViewer_DispatchEvent "$@"; then
		if [[ "$1" == "K" && "$2" == "+" && "$3" == q ]]; then
			RenderLoop_Done=1
		elif [[ "$1" == "K" && "$2" == "+" && "$3" == ']' ]]; then
			next_font
		elif [[ "$1" == "K" && "$2" == "+" && "$3" == '[' ]]; then
			prev_font
		else
			true
		fi
	fi
}

CmdlineGL_Start rw || die "Can't init CmdlineGL"
Init
RenderLoop_Run
cglQuit


================================================
FILE: share/examples/FlightSim.sh
================================================
#! /bin/bash
[ -n "$BASH_VERSION" ] || exec bash $0

# Define our handy die function
die() { echo "$@" >&2; exit 2; }
set -u
# Load bash libraries
source "${BASH_SOURCE%/*}/../CmdlineGL.lib" || die "Can't find CmdlineGL.lib (${BASH_SOURCE%/*}/../CmdlineGL.lib)";

CmdlineGL_LoadLib RenderLoop Geom Ship LaserBeam Cube
FixedPt=$Geom_FixedPt

let MAX_LASER_TRAVEL=100*$Geom_FixedPt
LASER_SPEED=100
SHOOT_PERIOD=200
MAX_LASERS=100
HighestInitLaser=-1

Init() {
	echo "Loading models..." >&2
	LaserBeam_InitGfx
	Ship_InitGfx
	Cube_InitGfx
	BuildCubeField

	echo "Initializing game state..." >&2
	SetLights
	glEnable GL_NORMALIZE
	glEnable GL_DEPTH_TEST
	glEnable GL_CULL_FACE
	glShadeModel GL_SMOOTH
	glFog GL_FOG_MODE GL_LINEAR
	glFog GL_FOG_COLOR '#333333'
	glClearColor '#333333'
	glFog GL_FOG_START 10
	glFog GL_FOG_END 100
	glFog GL_FOG_DENSITY .1
	glEnable GL_FOG
	glEnable GL_LIGHTING

	InitCoordSys Ship
	InitCoordSys Cam
	Ship_IV_Scale -$Geom_FixedPt
	Ship_JV_Scale -$Geom_FixedPt
	Ship_KV_Scale -$Geom_FixedPt
	InitVec CamTrail 0 0 0
	ShipSpeed=1
	((CamDist=6*Geom_FixedPt))
	CamFollowHeight=1
	ResetCam
	InpAimLf=0
	InpAimRt=0
	InpAimUp=0
	InpAimDn=0
	InpAccel=0
	InpDeaccel=0
	InpShoot=0
	LaserCount=0
	ShootCount=0
	NextGun=0
	let LastShoot=Timing_T
}

SetLights() {
	glLoadIdentity
	glEnable GL_LIGHTING
	glEnable GL_COLOR_MATERIAL
	glEnable GL_LIGHT0
	glLight GL_LIGHT0 GL_AMBIENT  .8 .8 .8 0
 	glLight GL_LIGHT0 GL_DIFFUSE  1  .8 .8 0
	glLight GL_LIGHT0 GL_SPECULAR .8 .8 .8 0
	glLight GL_LIGHT0 GL_POSITION 10 10 10 1
}

BuildCubeField() {
	glNewList CubeField GL_COMPILE
	glShadeModel GL_FLAT
	glTranslate -22.5 -22.5 -22.5
	for (( x=0; x<10; x++)); do
		for (( y=0; y<10; y++)); do
			for (( z=0; z<10; z++)); do
				Cube
				glTranslate 0 0 5
			done
			glTranslate 0 0 -50
			glTranslate 0 5 0
		done
		glTranslate 0 -50 0
		glTranslate 5 0 0
	done
	glShadeModel GL_SMOOTH
	glEndList
}

DrawCoordinates() {
	glBegin GL_LINES
	glColor "#FF0000"
	glVertex 1 0 0
	glVertex 0 0 0
	glColor "#00FF00"
	glVertex 0 1 0
	glVertex 0 0 0
	glColor "#0000FF"
	glVertex 0 0 1
	glVertex 0 0 0
	glEnd
}

InitLaser() {
	InitCoordSys ${1}
	let ${1}_Travel=0
}

AddLaser() {
	let idx=LaserCount++
	if ((idx>HighestInitLaser)); then InitLaser Laser${idx}; ((HighestInitLaser=idx)); fi
	CoordSys_Clone Laser${idx} $4
	((Laser${idx}_Pos_x=$1, Laser${idx}_Pos_y=$2, Laser${idx}_Pos_z=$3, Laser${idx}_Travel=0))
}

CloneLaser() {
	CoordSys_Clone $1 $2
	((${1}_Travel=${2}_Travel))
}

RemoveLaser() {
	let LaserCount--
	CloneLaser Laser$1 Laser$LaserCount
}

eval "UpdateLasers() {
	local progress=\$Timing_dT*$LASER_SPEED
	for ((i=LaserCount-1; i>=0; i--)); do
		((Laser\${i}_Pos_x+= Laser\${i}_KV_x*progress/$FixedPt))
		((Laser\${i}_Pos_y+= Laser\${i}_KV_y*progress/$FixedPt))
		((Laser\${i}_Pos_z+= Laser\${i}_KV_z*progress/$FixedPt))
		((Laser\${i}_Travel+=progress))
		if ((Laser\${i}_Travel>$MAX_LASER_TRAVEL)); then
			RemoveLaser \$i
		fi
	done
}"

DrawLasers() {
	glDisable GL_LIGHTING
	for ((i=0; i<LaserCount; i++)); do
		glPushMatrix
		Laser${i}_EnterCS
		LaserBeam
		glPopMatrix
	done
	glEnable GL_LIGHTING
}

Shoot() {
	local x y z xOfs yOfs zOfs
	((xOfs=Ship_GunXOffset[NextGun], yOfs=Ship_GunYOffset, zOfs=Ship_GunZOffset,
	 x=Ship_Pos_x+(Ship_IV_x*xOfs+Ship_JV_x*yOfs+Ship_KV_x*zOfs)/Geom_FixedPt,
	 y=Ship_Pos_y+(Ship_IV_y*xOfs+Ship_JV_y*yOfs+Ship_KV_y*zOfs)/Geom_FixedPt,
	 z=Ship_Pos_z+(Ship_IV_z*xOfs+Ship_JV_z*yOfs+Ship_KV_z*zOfs)/Geom_FixedPt))
	AddLaser $x $y $z Ship
#	cat ${SOUNDDIR:-.}/laser.dsp >/dev/dsp &
	((NextGun++, NextGun>3?NextGun=0:0))
}

# Pretty simple- update the ship's direction and speed, then move it along
# its forward vector.
#
# Create a new bullet if it's time and the spacebar is pressed.
#
UpdateShip() {
	local dT=Timing_dT Dist
	if ((InpAimLf)); then Ship_RelativeYaw $((-dT*5)); Ship_RelativeRoll $((-dT*3)); fi
	if ((InpAimRt)); then Ship_RelativeYaw $((dT*5)); Ship_RelativeRoll $((dT*3)); fi
	if ((InpAimUp)); then Ship_RelativePitch $((dT*8));  fi
	if ((InpAimDn)); then Ship_RelativePitch $((-dT*8)); fi
	Ship_Normalize

	if ((InpAccel && ShipSpeed<20)); then let ShipSpeed++; fi
	if ((InpDeaccel && ShipSpeed>0)); then let ShipSpeed--; fi
	Ship_Throttle=$ShipSpeed;
	((Dist=Timing_dT*ShipSpeed))
	((Ship_Pos_x+=Ship_KV_x*Dist/$FixedPt, Ship_Pos_y+=Ship_KV_y*Dist/$FixedPt, Ship_Pos_z+=Ship_KV_z*Dist/$FixedPt))

	if ((InpShoot||ShootCount)); then
		((Timing_T-LastShoot>SHOOT_PERIOD*2? LastShoot=Timing_T-SHOOT_PERIOD*2:0))
		for ((; LastShoot+SHOOT_PERIOD<Timing_T; LastShoot+=SHOOT_PERIOD)); do
			Shoot
		done
		ShootCount=0
	fi
}

# The general idea behind this one is to find the vector between the camera
# and the ship, and set it to exactly $CamDist, moving the camera along
# the vector as necessary.  The usual effect is for the camera to
# "fall in line" behind the ship.
#
# The camera's upward vector is always the same as the ship, and the camera
# looks toward 19 units ahead of the ship (so the player can see where they're
# going).
#
UpdateCam() {
	local fx fy fz # The "follow" point, toward which the camera travels
	((fx=Ship_Pos_x+Ship_JV_x*CamFollowHeight, fy=Ship_Pos_y+Ship_JV_y*CamFollowHeight, fz=Ship_Pos_z+Ship_JV_y*CamFollowHeight))
	((CamTrail_x=Cam_Pos_x-fx, CamTrail_y=Cam_Pos_y-fy, CamTrail_z=Cam_Pos_z-fz))
	# avoid div-by-0
	if ((CamTrail_x*CamTrail_x+CamTrail_y*CamTrail_y+CamTrail_z*CamTrail_z>$FixedPt)); then
		CamTrail_SetMagnitude $CamDist
		((Cam_Pos_x=fx+CamTrail_x, Cam_Pos_y=fy+CamTrail_y, Cam_Pos_z=fz+CamTrail_z))
	else
		((Cam_Pos_x=fx-Ship_KV_x*CamDist, Cam_Pos_y=fy-Ship_KV_y*CamDist, Cam_Pos_z=fz-Ship_KV_z*CamDist))
	fi
	((Cam_KV_x=CamTrail_x-Ship_KV_x*19, Cam_KV_y=CamTrail_y-Ship_KV_y*19, Cam_KV_z=CamTrail_z-Ship_KV_z*19))
	Cam_KV_Normalize
	((Cam_JV_x=Ship_JV_x, Cam_JV_y=Ship_JV_y, Cam_JV_z=Ship_JV_z))
	Cam_RegenIV
	Cam_IV_Normalize
	Cam_RegenJV # the camera's "up" is not necessarily the same as the ship's "up"
}

# Put the camera directly on top of its follow point, which causes the Update to reset its location
ResetCam() {
	((Cam_Pos_x=Ship_Pos_x+Ship_JV_x*CamFollowHeight, Cam_Pos_y=Ship_Pos_y+Ship_JV_y*CamFollowHeight, Cam_Pos_z=Ship_Pos_z+Ship_JV_y*CamFollowHeight))
	UpdateCam
}

RenderLoop_DispatchEvent() {
	case "$1" in
	K)
		if [[ $2 = "+" ]]; then Press=1; else Press=0; fi
		case "$3" in
		right) InpAimRt=$Press;;
		left)  InpAimLf=$Press;;
		up)    InpAimUp=$Press;;
		down)  InpAimDn=$Press;;
		=)     InpAccel=$Press;;
		-)     InpDeaccel=$Press;;
		space) ((InpShoot=Press, Press?ShootCount++:0));;
		q)     RenderLoop_Done=1;;
		esac
		;;
	esac
}

RenderLoop_Render() {
	UpdateLasers
	UpdateShip
	UpdateCam
	
	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT
	glLoadIdentity
	Cam_ExitCS
	glPushMatrix
		glLight GL_LIGHT0 GL_POSITION 0 0 1 0
		glScale 5
		glColor '#FFFFFF'
		glCallList CubeField
	glPopMatrix
	glPushMatrix
		Ship_EnterCS
		glTranslate 0 -1 -5
		Ship
	glPopMatrix
	DrawLasers
	glFlush
	cglSwapBuffers
}

main() {
	Init
	RenderLoop_Run;
	cglQuit
}

# TODO: Write better options processing
if (( $# > 1 )) && [[ "$1" == "--geometry" ]]; then
	CmdlineGL_Options=("${CmdlineGL_Options[@]}" "$1" "$2");
	shift;
	shift;
fi
if (( $# < 1 )); then
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	main
elif [[ "$1" == "--record" ]]; then
	CmdlineGL() { tee replay | command CmdlineGL; }
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	main
elif [[ "$1" == "--dump" ]]; then
	CmdlineGL_Start stdout || die "Can't init CmdlineGL state"
	main
else
	echo 'Usage: FlightSim.sh [ --record | --dump ]'
	echo
	echo '   --dump    Dump all output to stdout at a virtual 40fps'
	echo '   --record  Run CmdlineGL, but duplicate all output to "./replay"'
	echo
	echo '   Recordings can be played by piping them into CmdlineGL.'
	echo '   For instance:'
	echo '         $ CmdlineGL <replay >/dev/null'
	echo
	echo 'Controls:'
	echo '    Up   / Down   Pitch of ship'
	echo '    Left / Right  Roll+Yaw of ship'
	echo '    +    / -      Speed of ship'
	echo '    Space         Fire lasers'
	echo '    q             Quit'
	echo
fi


================================================
FILE: share/examples/ImgCube.sh
================================================
#! /bin/bash
[ -n "$BASH_VERSION" ] || exec bash $0
set -u
# Define our handy die function
die() { echo "$@" >&2; exit 2; }

source "${BASH_SOURCE%/*}/../CmdlineGL.lib" || die "Can't find CmdlineGL.lib (${BASH_SOURCE%/*}/../CmdlineGL.lib)";
CmdlineGL_LoadLib RenderLoop ModelViewer Cube || die "Can't load required libraries"

Init() {
	glEnable GL_NORMALIZE GL_DEPTH_TEST
	glEnable GL_LIGHTING
	glEnable GL_TEXTURE_2D
	glShadeModel GL_SMOOTH
	glClearColor '#000033'
	glBlendFunc GL_SRC_ALPHA GL_ONE
	#glEnable GL_COLOR_MATERIAL

	CmdlineGL_LoadTex checker

	# Lighting
	glLoadIdentity
	glEnable GL_LIGHT0
	glLight GL_LIGHT0 GL_AMBIENT 0.8 0.8 0.8 0
 	glLight GL_LIGHT0 GL_DIFFUSE 1 0.8 0.8 0
	glLight GL_LIGHT0 GL_SPECULAR 0.8 0.8 0.8 0
	glLight GL_LIGHT0 GL_POSITION 10 10 10 1
	
	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT
}

RenderLoop_Render() {
	ModelViewer_Update
	
	glLoadIdentity
	ModelViewer_ApplyMatrix
	glColor 0.5 0.5 0.5 1
	glBindTexture GL_TEXTURE_2D checker
	Cube
	glFlush
	cglSwapBuffers
	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT
}

blend=0
RenderLoop_DispatchEvent() {
	if ! ModelViewer_DispatchEvent "$@"; then
		if [[ "$1" == K && "$2" == + ]]; then
			case "$3" in
			q) RenderLoop_Done=1;;
			b) ((blend=!blend)) \
			       && { glEnable GL_BLEND; glDisable GL_DEPTH_TEST; } \
			       || { glDisable GL_BLEND; glEnable GL_DEPTH_TEST; }
			   ;;
			esac
		fi
	fi
}

main () {
	Init
	RenderLoop_Run
	cglQuit
}

if (( $# < 1 )); then
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	main
elif [[ "$1" == "--record" ]]; then
	CmdlineGL() { tee replay | command CmdlineGL; }
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	main
elif [[ "$1" == "--dump" ]]; then
	CmdlineGL_Start stdout || die "Can't init CmdlineGL state"
	main
else
	echo 'Usage: ImgCube.sh [ --record | --dump ]'
	echo
	echo '   --dump    Dump all output to stdout at a virtual 40fps'
	echo '   --record  Run CmdlineGL, but duplicate all output to "./replay"'
	echo
	echo '   Recordings can be played by piping them into CmdlineGL.'
	echo '   For instance:'
	echo '         $ CmdlineGL <replay >/dev/null'
fi


================================================
FILE: share/examples/ModelViewer.sh
================================================
#! /bin/bash
[ -n "$BASH_VERSION" ] || exec bash $0 "$@"
set -u
# Define our handy die function
die() { echo "$@" >&2; exit 2; }

source "${BASH_SOURCE%/*}/../CmdlineGL.lib" || die "Can't find CmdlineGL.lib (${BASH_SOURCE%/*}/../CmdlineGL.lib)";
CmdlineGL_LoadLib RenderLoop ModelViewer

Init() {
	glEnable GL_NORMALIZE GL_DEPTH_TEST GL_CULL_FACE
	glEnable GL_LIGHTING
	#glEnable GL_TEXTURE_2D
	glShadeModel GL_SMOOTH
	glClearColor '#000033'

	# Lighting
	glLoadIdentity
	glEnable GL_LIGHT0
	glLight GL_LIGHT0 GL_AMBIENT 0.8 0.8 0.8 0
 	glLight GL_LIGHT0 GL_DIFFUSE 1 0.8 0.8 0
	glLight GL_LIGHT0 GL_SPECULAR 0.8 0.8 0.8 0
	glLight GL_LIGHT0 GL_POSITION 10 10 10 1

	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT
}

RenderLoop_Render() {
	ModelViewer_Update
	
	glLoadIdentity
	ModelViewer_ApplyMatrix
	glColor 0.5 0.5 0.5 1
	$Model
	glFlush
	cglSwapBuffers
	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT
}

RenderLoop_DispatchEvent() {
	if ! ModelViewer_DispatchEvent "$@"; then
		if [[ "$1" == "K" && "$2" == "+" && "$3" == "q" ]]; then
			RenderLoop_Done=1;
		fi
	fi
}

if (( $# != 1 )); then
	echo 'Usage: ModelViewer.sh LIBNAME'
	echo
	echo ' where LIBNAME is the base-name of a library that defines a renderable model,'
	echo ' such as "Cube" (bash-lib/Cube.lib)'
	echo
elif ! CmdlineGL_LoadLib "$1"; then
	echo 'Failed to load model "$1"'
fi
Model=$1;
Model=${Model##*/}
Model=${Model%.*}

if ! [[ "$( type -t "$Model" )" == function ]]; then
	echo "Library \"$1\" does not define a function named \"$Model\""
	echo 'This is required'
else
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	Init
	RenderLoop_Run
	cglQuit
fi


================================================
FILE: share/examples/Pyramids.sh
================================================
#! /bin/bash
[ -n "$BASH_VERSION" ] || exec bash $0

# Define our handy die function
die() { echo "$@" >&2; exit 2; }

source "${BASH_SOURCE%/*}/../CmdlineGL.lib" || die "Can't find CmdlineGL.lib (${BASH_SOURCE%/*}/../CmdlineGL.lib)";
CmdlineGL_LoadLib Timing RenderLoop

let x=0;

ToInt() {
	Result=${1/./}
}
ToFloat() {
	let dec_pos=${#1}-$2;
	Result=${1:0:dec_pos}.${1:dec_pos};
}

Triangle() {
	glBegin GL_TRIANGLES
	glVertex 3 -3 0
	glVertex -3 -3 0
	glVertex 0 3 0
	glEnd
}

Pyramid() {
	glBegin GL_TRIANGLES
	glColor 0.5 0.5 0.2
	glNormal 0 2 4.4
	glVertex 1 -1 1
	glVertex -1 -1 1
	glVertex 0 1.2 0

	glColor 0.5 0.2 0.5
	glNormal -4.4 2 0
	glVertex -1 -1 1
	glVertex -1 -1 -1
	glVertex 0 1.2 0

	glColor 0.2 0.5 0.5
	glNormal 0 2 -4.4
	glVertex -1 -1 -1
	glVertex 1 -1 -1
	glVertex 0 1.2 0

	glColor 0.2 0.7 0.5
	glNormal 4.4 2 0
	glVertex 1 -1 -1
	glVertex 1 -1 1
	glVertex 0 1.2 0
	glEnd

	glColor 0.2 0.2 0.5
	glBegin GL_QUADS
	glNormal 0 -1 0
	glVertex 1 -1 -1
	glVertex -1 -1 -1
	glVertex -1 -1 1
	glVertex 1 -1 1
	glEnd
}

BuildList() {
	glLoadIdentity
	glNewList tri GL_COMPILE
	glColor 0.5 0.5 0.5
	Pyramid
	glEndList
}

SetLights() {
	glLoadIdentity
	glEnable GL_LIGHTING
	glEnable GL_COLOR_MATERIAL
	glEnable GL_LIGHT0
	glLight GL_LIGHT0 GL_AMBIENT 0.8 0.8 0.8 0
 	glLight GL_LIGHT0 GL_DIFFUSE 1 0.8 0.8 0
	glLight GL_LIGHT0 GL_SPECULAR 0.8 0.8 0.8 0
	glLight GL_LIGHT0 GL_POSITION 10 10 10 1
}

Init() {
	BuildList
	SetLights

	glEnable GL_NORMALIZE
	glEnable GL_DEPTH_TEST
	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT

	frametime=0;
	direction=0;
	distance=10;
	pitch=0;
}

Swap() {
	glFlush
	cglSwapBuffers
	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT
}

RenderLoop_Render() {
	Update; # apply current user input to program state
	
	ToFloat 000$((Timing_T*8%36000)) 2
	x=$Result
	glLoadIdentity
	glTranslate 0 0 -$distance
	glRotate $pitch 1 0 0
	glRotate $direction 0 1 0
	
	glTranslate 3 0 0
	glCallList tri
	glTranslate -3 0 3
	glCallList tri
	glTranslate -3 0 -3
	glCallList tri
	glTranslate 3 0 -3
	glCallList tri
	glTranslate 0 0 3
	
	glRotate $x 0 0 1
	glRotate $x 0 1 1
	glCallList tri
	Swap;
}

MouseClick() {
	local Press btn=$2
	if [[ "$1" = "+" ]]; then Press=1; else Press=0; fi
	if ((btn==1)); then
		((Dragging=Press))
	fi
}

# Handle mouse drag actions.
# If the mouse has moved since last time change the pitch or direction
# by the vertical or horizontal distance the mouse has moved.
# Also repaint the robot and record the "last position" of the mouse.
#
MouseMotion() {
	local dx=$3 dy=$4;
	if ((Dragging)); then
		if ((dy)); then
			((pitch+= dy))
		fi
		if ((dx)); then
			((direction+= dx));
		fi
	fi
}

RenderLoop_DispatchEvent() {
	local Press
	case "$1" in
	K)
		if [[ "$2" = "+" ]]; then Press=1; else Press=0; fi
		case "$3" in
		right)  PanRight=$Press;;
		left)   PanLeft=$Press;;
		up)     PanUp=$Press;;
		down)   PanDn=$Press;;
		=)      ZoomIn=$Press;;
		-)      ZoomOut=$Press;;
		q)      RenderLoop_Done=1;;
		esac
		;;
	M)
		if [[ "$2" = "@" ]]; then
			MouseMotion $3 $4 $5 $6
		else
			MouseClick $2 $3
		fi
		;;
	esac
}

Update() {
	if ((PanLeft)); then let direction+=2; fi
	if ((PanRight));then let direction-=2; fi
	if ((PanUp));   then let pitch-=2; fi
	if ((PanDn));   then let pitch+=2; fi
	if ((ZoomIn));  then let distance-=1; fi
	if ((ZoomOut)); then let distance+=1; fi
}

main() {
	Init
	Swap
	RenderLoop_Run;
	cglQuit
}

if [[ "$1" == "--record" ]]; then
	CmdlineGL() { tee replay | command CmdlineGL; }
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	main
elif [[ "$1" == "--dump" ]]; then
	CmdlineGL_Start stdout || die "Can't init CmdlineGL state"
	main
elif [[ -z "$1" ]]; then
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	main
else
	echo 'Usage: Pyramids.sh [ --record | --dump ]'
	echo
	echo '   --dump    Dump all output to stdout at a virtual 40fps'
	echo '   --record  Run CmdlineGL, but duplicate all output to "./replay"'
	echo
	echo '   Recordings can be played by piping them into CmdlineGL.'
	echo '   For instance:'
	echo '         $ CmdlineGL <replay >/dev/null'
fi


================================================
FILE: share/examples/Robot.sh
================================================
#! /bin/bash
[ -n "$BASH_VERSION" ] || exec bash $0
set -u
# Define our handy die function
die() { echo "$@" >&2; exit 2; }

source "${BASH_SOURCE%/*}/../CmdlineGL.lib" || die "Can't find CmdlineGL.lib (${BASH_SOURCE%/*}/../CmdlineGL.lib)";
CmdlineGL_LoadLib RenderLoop ModelViewer

#------------------------------------------------------------------------------
# This is an almost line-by-line conversion from C to bash
# The conversion process was rather fun in a strange morbid way...
#
# Actually, the program is somewhat improved, since it uses display lists now
#
#------------------------------------------------------------------------------
# Constants for the dimensions of the robot's body
#
#                   ___
#                  /   \
#   Y              \   /
#   |             -_____-
#   |        -----|     |-----
#   |        |    |     |    |
#   |______X |----|     |----|
#  / 0       | |  |     |  | |
# /           \\  |     |   \\
# Z            \\ -__-__-    \\
#               \\_ | | __    \\
#               |  LJ LJ  |
#               |  r|_r|  |
#               |  |   |  |
#               |  |   |  |
#               |--|   |--|
#               |  |   |  |
#               |  |   |  |
#               |__|   |__|
#              |    | |    |
#              ------ ------
#

HEAD_RADIUS=100;
TORSO_LENGTH=480;
TORSO_RADIUS=90;
HIP_WIDTH=220;
HIP_SPACING=20;
HIP_RADIUS=35;
SHOULDER_WIDTH=500;
SHOULDER_RADIUS=70;
UPPER_ARM_LENGTH=300;
UPPER_ARM_RADIUS=40;
LOWER_ARM_LENGTH=280;
LOWER_ARM_RADIUS=30;
UPPER_LEG_LENGTH=340;
UPPER_LEG_RADIUS=55;
LOWER_LEG_LENGTH=300;
LOWER_LEG_RADIUS=45;
FOOT_LENGTH=160;
FOOT_HEIGHT=30;
HEAD_HEIGHT=$((TORSO_LENGTH+$HEAD_RADIUS));
PELVIC_HEIGHT=$((-HIP_RADIUS*18/10));
SHOULDER_HEIGHT=$((TORSO_LENGTH*84/100));
SHOULDER_OFFSET=$((SHOULDER_WIDTH/2 - $UPPER_ARM_RADIUS));
ELBOW_RADIUS=$((LOWER_ARM_RADIUS*12/10));
ELBOW_WIDTH=$((LOWER_ARM_RADIUS*24/10));
LEG_OFFSET=$((HIP_WIDTH/2 ));
UPPER_LEG_PIPE_LENGTH=$((UPPER_LEG_LENGTH+UPPER_LEG_RADIUS));
HIP_SUPPORT_WIDTH=$((HIP_WIDTH - HIP_SPACING*2 - UPPER_LEG_RADIUS*2));
HIP_SUPPORT_HALF_WIDTH=$((HIP_SUPPORT_WIDTH/2));
KNEE_RADIUS=$((LOWER_LEG_RADIUS*12/10));
KNEE_WIDTH=$((LOWER_LEG_RADIUS*24/10));

# Indicies of important angles
#
NeckX=0; NeckY=1;
LShoulder=2; RShoulder=3; LElbow=4; RElbow=5;
Torso=6;
LHip=7; RHip=8; LKnee=9; RKnee=10; LFoot=11; RFoot=12;

# Joint records for the motionless robot and the four stage animation
#
Standing=(    0  0  1000  1000 -2000 -2000      0       0     0     0     0  1000  1000 );
WalkScript0=( 0  0  2000  2000 -5500 -5500      0   -4000  1000  7000  1000 -2000  -500 );
WalkScript1=( 0  0  4000     0 -4000 -7000   -700   -3000  2500  2000  3000  2000  3000 );
WalkScript2=( 0  0  2000  2000 -5500 -5500      0    1000 -4000  1000  7000  -500 -2000 );
WalkScript3=( 0  0     0  4000 -7000 -4000    700    2500 -3000  3000  2000  3000  2000 );


# Variables that describe the current robot
#
Robot_Joints=0;
Robot_MoveProgress=0;
Robot_Animate=true;

# Variables related to the camera
#
View_Direction=0;
View_Pitch=0;
View_Distance=1000;
View_Mode=0;

Dragging=0;

# Draw the head of the robot.
# Draws a sphere with radius "HEAD_RADIUS" at the current origin.
#
build_head() {
	glNewList head GL_COMPILE
	glPushMatrix
	glScale $HEAD_RADIUS $HEAD_RADIUS $HEAD_RADIUS
	gluSphere quadric 100 10 10
	glPopMatrix
	glEndList
}
head() {
	glCallList head
}

# Draw a closed cylinder.
# This is equivalent to a
#
closedCylinder() { # GLUquadricObj *qobj GLdouble baseRadius GLdouble topRadius GLdouble height GLint slices GLint stacks
	baseRadius=$2; topRadius=$3; height=$4; slices=$5; stacks=$6;
	gluCylinder $1 0 $baseRadius 0 $slices 1
	gluCylinder $1 $baseRadius $topRadius $height $slices $stacks
	glTranslate 0 0 $height
	gluCylinder $1 $topRadius 0 0 $slices 1
	glTranslate 0 0 -$height
}

# Draw the torso of the robot.
# This consists of a large vertical cylinder above the origin a tapered
# cylinder on the bottom of it and horizontal shoulder cylinder.
# The origin ends up located at the very bottom of the torso.
#
build_torso() {
	glNewList torso GL_COMPILE
	glPushMatrix
	glTranslate 0 $TORSO_LENGTH 0
	glRotate 9000 100 0 0
	local TorsoZ=$((TORSO_LENGTH*8/10));
	closedCylinder quadric $TORSO_RADIUS $TORSO_RADIUS $TorsoZ 20 1
	glTranslate 0 0 $TorsoZ
	closedCylinder quadric $TORSO_RADIUS $((HIP_SUPPORT_WIDTH/2)) $((TORSO_LENGTH*2/10)) 20 1
	glPopMatrix

	glPushMatrix
	glTranslate $((-SHOULDER_WIDTH/2)) $SHOULDER_HEIGHT 0.0
	glRotate 9000 0 100 0
	closedCylinder quadric $SHOULDER_RADIUS $SHOULDER_RADIUS $SHOULDER_WIDTH 20 1
	glPopMatrix
	glEndList
}
torso() {
	glCallList torso
}

# Draw the lower torso of the robot.
# This piece is composed of a vertical disc (short horizontal cylinder) and a
# long horizontal cylinder that runs through it.
#
build_lower_torso() {
	glNewList lower_torso GL_COMPILE
	glPushMatrix
	glTranslate 0 $PELVIC_HEIGHT 0
	glRotate 9000 0 100 0
	glTranslate 0 0 -$HIP_SUPPORT_HALF_WIDTH
	closedCylinder quadric $TORSO_RADIUS $TORSO_RADIUS $HIP_SUPPORT_WIDTH 20 1

	glTranslate 0 0 -$(($HIP_SPACING + $UPPER_LEG_RADIUS))
	gluCylinder quadric $HIP_RADIUS $HIP_RADIUS $HIP_WIDTH 10 1
	glPopMatrix
	glEndList
}
lower_torso() {
	glCallList lower_torso
}

# Draw the upper arm of the robot.
# This is a simple cylinder that runs vertically from its origin downward
# toward the elbow.
#
build_upper_arm() {
	glNewList upper_arm GL_COMPILE
	glPushMatrix
	glRotate 9000 100 0 0
	gluCylinder quadric $UPPER_ARM_RADIUS $LOWER_ARM_RADIUS $UPPER_ARM_LENGTH 20 1
	glPopMatrix
	glEndList
}
upper_arm() {
	glCallList upper_arm
}

# Draw the elbow and lower arm.
# This function draws a short horizontal cylinder at the origin then draws
# lower arm extending downward from there.
#
build_lower_arm() {
	glNewList lower_arm GL_COMPILE
	glPushMatrix
	glRotate 9000 0 100 0
	glTranslate 0 0 $((-ELBOW_WIDTH/2))
	closedCylinder quadric $ELBOW_RADIUS $ELBOW_RADIUS $ELBOW_WIDTH 20 1
	glPopMatrix

	glPushMatrix
	glRotate 9000 100 0 0
	closedCylinder quadric $LOWER_ARM_RADIUS $LOWER_ARM_RADIUS $LOWER_ARM_LENGTH 20 1
	glPopMatrix
	glEndList
}
lower_arm() {
	glCallList lower_arm
}

# Draw the upper leg.
# This function draws a simple cylinder that starts just above the origin and
# extends downward.
#
build_upper_leg() {
	glNewList upper_leg GL_COMPILE
	glPushMatrix
	glTranslate 0 $UPPER_LEG_RADIUS 0
	glRotate 9000 100 0 0
	closedCylinder quadric $UPPER_LEG_RADIUS $LOWER_LEG_RADIUS $UPPER_LEG_PIPE_LENGTH 20 1
	glPopMatrix
	glEndList
}
upper_leg() {
	glCallList upper_leg
}

# Draw the knee and lower leg.
# This draws a short horizontal cylinder for the knee then draws a vertical
# cylinder for the lower leg.
#
build_lower_leg() {
	glNewList lower_leg GL_COMPILE
	glPushMatrix
	glRotate 9000 0 100 0
	glTranslate 0 0 $((-KNEE_WIDTH/2))
	closedCylinder quadric $KNEE_RADIUS $KNEE_RADIUS $KNEE_WIDTH 20 1
	glPopMatrix

	glPushMatrix
	glRotate 9000 1 0 0
	gluCylinder quadric $LOWER_LEG_RADIUS $LOWER_LEG_RADIUS $LOWER_LEG_LENGTH 20 1
	glPopMatrix
	glEndList
}
lower_leg() {
	glCallList lower_leg
}

# Draw the ankle and foot.
# This draws a short horizontal cylinder for the ankle/heel and then draws
# a flattened tapered cylinder along the Z axis for the foot.
#
build_foot() {
	glNewList foot GL_COMPILE
	glPushMatrix
	glRotate 9000 0 100 0
	glTranslate 0 0 $((-LOWER_LEG_RADIUS))
	closedCylinder quadric $LOWER_LEG_RADIUS $LOWER_LEG_RADIUS $((LOWER_LEG_RADIUS*2)) 20 1
	glPopMatrix
	glPushMatrix
	glTranslate 0 $((-LOWER_LEG_RADIUS*3/10)) 0
	glScale 100 30 100
	closedCylinder quadric $((LOWER_LEG_RADIUS*8/10)) $((LOWER_LEG_RADIUS*12/10)) $FOOT_LENGTH 20 1
	glPopMatrix
	glEndList
}
foot() {
	glCallList foot
}

# Display the robot using the joint angles in the Robot structure.
#
# This function starts from the identity matrix and first modifies the matrix
# to put the camera in the desired position.
#
# It then traverses the structure of the robot's body calling each of
# the drawing functions in thew process.
#
# Last it swaps the drawing buffers to make the new image visible.
#
Repaint() {
	# Clear the drawing buffer
	glClear GL_COLOR_BUFFER_BIT
	glClear GL_DEPTH_BUFFER_BIT
	glLoadIdentity

	# Alter the model matrix to give the effect of having a movable camera.
	ModelViewer_ApplyMatrix

	glPushMatrix
	# Move to the center of the head rotate by the neck angles and draw.
	glTranslate 0 $HEAD_HEIGHT 0
	glRotate ${Robot_Joints[$NeckX]} 100 0 0
	glRotate ${Robot_Joints[$NeckY]} 0 100 0
		head
	glPopMatrix

	# Draw the torso.  It never needs rotated.
	torso

	glPushMatrix
	# Draw the left arm.  Start at the left shoulder draw the uper arm
	#  then translate down to the elbow rotate and draw the lower arm.
	glTranslate $SHOULDER_OFFSET $SHOULDER_HEIGHT 0
	glRotate ${Robot_Joints[$LShoulder]} 100 0 0
		upper_arm
		glTranslate 0 -$UPPER_ARM_LENGTH 0
		glRotate ${Robot_Joints[$LElbow]} 100 0 0
			lower_arm
	glPopMatrix

	glPushMatrix
	# Same for the right arm.
	glTranslate -$SHOULDER_OFFSET $SHOULDER_HEIGHT 0
	glRotate ${Robot_Joints[$RShoulder]} 100 0 0
		upper_arm
		glTranslate 0 -$UPPER_ARM_LENGTH 0
		glRotate ${Robot_Joints[$RElbow]} 100 0 0
			lower_arm
	glPopMatrix

	glPushMatrix
	# Rotate for the hips draw them then draw the left and right leg.
	glRotate ${Robot_Joints[$Torso]} 0 100 0
		lower_torso

		glPushMatrix
		# First move to the left draw the upper leg and then translate down
		#  to the knee draw the lower leg then translate down to the foot
		#  then rotate and draw it.
		glTranslate $LEG_OFFSET $PELVIC_HEIGHT 0
		glRotate ${Robot_Joints[$LHip]} 100 0 0
			upper_leg
			glTranslate 0 -$UPPER_LEG_LENGTH 0
			glRotate ${Robot_Joints[$LKnee]} 100 0 0
				lower_leg
				glTranslate 0 -$LOWER_LEG_LENGTH 0
				glRotate ${Robot_Joints[$LFoot]} 100 0 0
				foot
		glPopMatrix

		glPushMatrix
		# Same for the right leg.
		glTranslate -$LEG_OFFSET $PELVIC_HEIGHT 0
		glRotate ${Robot_Joints[$RHip]} 100 0 0
			upper_leg
			glTranslate 0 -$UPPER_LEG_LENGTH 0
			glRotate ${Robot_Joints[$RKnee]} 100 0 0
				lower_leg
				glTranslate 0 -$LOWER_LEG_LENGTH 0
				glRotate ${Robot_Joints[$RFoot]} 100 0 0
				foot
		glPopMatrix
	glPopMatrix

	# Flush any remaining drawing commands and flip the buffers.
	glFlush
	cglSwapBuffers
}


# Set the angles of the current robot's joints by averaging angles from a
# "before" and "after" position.  "Progress" indicates how far the joint
# has come from the "before" position toward the "after" one.
# This function stores the results directly into the Robot global variable.
#
SetJoints() {
	local FromAng=$1 ToAng=$2 Progress=$3;
	for ((i=0; i<13; i++)); do
		(( Robot_Joints[i]=$FromAng[i]+($ToAng[i]-$FromAng[i])*Progress/100 ));
	done
}


# Animate the robot by progressing it through the four walking states.
# This function renews the GLut timer so that it will be called again
#  periodically.
# The parameter is ignored.
#
Animate() {
	if [[ -n "$Robot_Animate" ]]; then
		let Robot_MoveProgress+=5;
		if (( Robot_MoveProgress >= 400 )); then
 			let Robot_MoveProgress-=400;
		fi
		(( idx1=Robot_MoveProgress/100 ))
		(( idx2=idx1+1 ))
		if (( idx2 > 3 )); then idx2=0; fi
		SetJoints "WalkScript$idx1" "WalkScript$idx2" $((Robot_MoveProgress - (idx1 * 100) ))
	else
		SetJoints "Standing" "Standing" 0
	fi
}

# Initialize the globals and set up OpenGL.
#
Init() {
	# Use fixed point numbers for all floating-point GL parameters
	cglPushDivisor 100

	# Turn on normalization of surface vectors and enable Z-buffering.
	glEnable GL_NORMALIZE
	glEnable GL_DEPTH_TEST
	# Setup lighting
	# Turn on lights and iterate through the global array of light records.
	# For each light in the array enable that number light in OpenGl and
	#  set the colors for it.
	glEnable GL_LIGHTING
	glLight GL_LIGHT0 GL_AMBIENT 80 80 80 100
	glLight GL_LIGHT0 GL_DIFFUSE 80 80 80 100
	glLight GL_LIGHT0 GL_SPECULAR 80 80 80 100
	glLight GL_LIGHT0 GL_POSITION 1000 1000 1000 100
	glEnable GL_LIGHT0

	# Setup material properties.
	# Allocate quadrics with filled drawing style
	gluNewQuadric quadric
	gluQuadricDrawStyle quadric GLU_FILL

	build_head;
	build_torso;
	build_lower_torso;
	build_upper_arm;
	build_lower_arm;
	build_upper_leg;
	build_lower_leg;
	build_foot;

	# Initialize runtime variables.
	Robot_MoveProgress=0;
	SetJoints "WalkScript0" "WalkScript0" 0
	Robot_Animate=true;
}

RenderLoop_DispatchEvent() {
	if ! ModelViewer_DispatchEvent "$@"; then
		if [[ "$1" == "K" && "$2" == "+" && "$3" == "q" ]]; then
			RenderLoop_Done=1;
		fi
	fi
}

RenderLoop_Render() {
	ModelViewer_Update
	Animate
	Repaint
}

main() {
	Init
	RenderLoop_Run;
	cglQuit
}

# TODO: Write better options processing
if (( $# > 1 )) && [[ "$1" == "--geometry" ]]; then
        CmdlineGL_Options=("${CmdlineGL_Options[@]}" "$1" "$2");
        shift;
        shift;
fi
if (( ! $# )); then
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	main
elif [[ "$1" == "--record" ]]; then
	CmdlineGL() { tee replay | command CmdlineGL; }
	CmdlineGL_Start rw || die "Can't init CmdlineGL"
	main
elif [[ "$1" == "--dump" ]]; then
	CmdlineGL_Start stdout || die "Can't init CmdlineGL state"
	main
else
	echo 'Usage: Robot.sh [ --record | --dump ]'
	echo
	echo '   --dump    Dump all output to stdout at a virtual 40fps'
	echo '   --record  Run CmdlineGL, but duplicate all output to "./replay"'
	echo
	echo '   Recordings can be played by piping them into CmdlineGL.'
	echo '   For instance:'
	echo '         $ CmdlineGL <replay >/dev/null'
fi



================================================
FILE: share/examples/SpinText.sh
================================================
#! /bin/bash
#
#  This is an extremely minimal example which rotates a string of text
#  rendered in 3D.  The only complicated part is finding a valid font...
#
text="$1";
font="$2";
set -eu

source "${BASH_SOURCE%/*}/../CmdlineGL.lib" || die "Can't find CmdlineGL.lib (${BASH_SOURCE%/*}/../CmdlineGL.lib)";

if [[ -z "$text" ]]; then
	echo "Usage: SpinText.sh STRING_OF_TEXT [FONT_FILE]";
	exit 1;
fi
if [[ -z "$font" ]]; then
	# See if we can find a sensible default
	font=`find /usr/share -name '*.ttf' | grep -i mono | head -n 1`;
	if [[ ! -f "$font" ]]; then
		echo "Can't find any default font; specify font filename as second argument.";
		exit 2;
	fi
else
	if [[ ! -f "$font" ]]; then
		echo "No such font \"$font\"";
		exit 2;
	fi
fi

R=0
T=0
spin_rate=12 # degrees per second

# Initialize CmdlineGL for rendering only (no input or feedback)
CmdlineGL_Start ro
glEnable GL_NORMALIZE GL_DEPTH_TEST GL_CULL_FACE
glShadeModel GL_SMOOTH

# Load font file and configure font rendering parameters
ftglCreateExtrudeFont font1 "$font"
ftglSetFontFaceSize   font1 72 72
ftglSetFontDepth      font1 20

# Prepare the graphics in a display list.  Need to call once first since ftgl
# creates its own display lists, then again to capture those in the second
# display list.
ftglRenderFont font1 "$text" FTGL_ALIGN_CENTER FTGL_RENDER_ALL
glNewList mytext GL_COMPILE
glTranslate -$(( ${#text}/2 * 40 )) -36 10   # flaky guess at midpoint of string
ftglRenderFont font1 "$text" FTGL_RENDER_ALL
glEndList

# set up lighting (otherwise no change as it rotates)
glEnable GL_LIGHTING GL_LIGHT0
glLight GL_LIGHT0 GL_AMBIENT .8 .8 .8 0
glLight GL_LIGHT0 GL_DIFFUSE 1 .8 .8 0
glLight GL_LIGHT0 GL_SPECULAR .8 .8 .8 0
glLight GL_LIGHT0 GL_POSITION 10 10 10 1

while true; do
	glClear GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT
	glLoadIdentity
	glRotate $((R+=spin_rate))/60 0 1 0  # assuming 60fps
	glScale 10/$((40 * ${#text} / 2))    # flaky guess at scaling to window width
	glCallList mytext
	glFlush
	cglSwapBuffers
	cglSync $((T+=16)) # blindly assume we can maintain 60fps
done


================================================
FILE: share/lib-bash/CmdlineGL.lib
================================================
# CmdlineGL Base API
#--------------------------------------
# Functions:
#   CmdlineGL_LoadLib LIBRARY [LIBRARY...]
#   CmdlineGL_LoadTex TEXTURE [TEXTURE...]
#   CmdlineGL_LoadFont FONT [FONT...]
#   CmdlineGL_Start [MODE]
#   CmdlineGL_Send COMMAND [ARGS...]
#   CmdlineGL_Recv
#   gl*
#   glu*
#   cgl*
#
# Variables:
#   CmdlineGL_Options - array variable of options to pass to CmdlineGL
#   CmdlineGL_LibPath - ':' separated list of directories where *.lib can be found
#   CmdlineGL_TexPath - same for *.png
#   CmdlineGL_FontPath - same for *.ttf
#   CmdlineGL_Mode: str - either 'stdout', 'w' or 'rw', or empty if CmdlineGL is not started yet
#   CmdlineGL_In: int - File handle of user input returned from CmdlineGL
#   CmdlineGL_Out: int - File handle where commands are written
#   CmdlineGL_InputLine: str - Most recent input event
#

declare -a CmdlineGL_Options
CmdlineGL_Mode=
CmdlineGL_In=
CmdlineGL_Out=
CmdlineGL_InputLine=
CmdlineGL_LibLoaded=":"
CmdlineGL_TexLoaded=":"
CmdlineGL_FontLoaded=":"

CmdlineGL_LoadLib() {
	# Unpack path into an array, so that we can expand the path into 'find' cleanly
	IFS=':' read -ra CmdlineGL_LibPathAry <<< "$CmdlineGL_LibPath";
	for lib in "$@"; do
		if [[ "$CmdlineGL_LibLoaded" != *:"$lib":* ]]; then
			local fname;
			# If full path name is given, load it directly
			if [[ "$lib" = */* ]]; then
				fname="$lib"
			else
				fname=`find "${CmdlineGL_LibPathAry[@]}" -name "$lib".lib | head -n 1` || { echo "Can't find lib '$lib' in path '$CmdlineGL_LibPath'"; return 2; }
			fi
			source "$fname" || { echo "Failed to load '$lib'"; return 2; }
			CmdlineGL_LibLoaded="$CmdlineGL_LibLoaded$lib:"
		fi
	done
}

CmdlineGL_LoadTex() {
	# Unpack path into an array, so that we can expand the path into 'find' cleanly
	IFS=':' read -ra CmdlineGL_TexPathAry <<< "$CmdlineGL_TexPath";
	for tex in "$@"; do
		if [[ "$CmdlineGL_TexLoaded" != *:"$tex":* ]]; then
			local fname=`find "${CmdlineGL_TexPathAry[@]}" -name "$tex".png | head -n 1` || { echo "Can't find Tex '$tex' in path '$CmdlineGL_TexPath'"; return 2; }
			glBindTexture GL_TEXTURE_2D "$tex"
			cglLoadImage2D "$fname"
			glTexParameter GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_LINEAR
			glTexParameter GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER GL_LINEAR
		fi
	done
}

CmdlineGL_LoadFont() {
	# Unpack path into an array, so that we can expand the path into 'find' cleanly
	IFS=':' read -ra CmdlineGL_FontPathAry <<< "$CmdlineGL_FontPath";
	for font in "$@"; do
		if [[ "$CmdlineGL_FontLoaded" != *:"$font":* ]]; then
			local fname=`find "${CmdlineGL_FontPathAry[@]}" -name "$font".png | head -n 1` || { echo "Can't find Font '$font' in path '$CmdlineGL_FontPath'"; return 2; }
			echo "TODO" >&2; return 1;
		fi
	done
}

# Build aliases for each available command in the API.
# This is the first time we check for an executable CmdlineGL, but can't check its
# exit status within the substitution...
CmdlineGL_Commands=( $( CmdlineGL --showcmds ) ) || { echo "Can't run CmdlineGL"; return 2; }
for cmd in ${CmdlineGL_Commands[@]}; do eval "$cmd(){ CmdlineGL_Send $cmd \"\$@\"; }"; done

# This default gets overwritten by CmdlineGL_Start  (thunk/trampoline style)
CmdlineGL_Send() {
	CmdlineGL_Start
	CmdlineGL_Send "$@"
}
CmdlineGL_Recv() {
	CmdlineGL_Start
	CmdlineGL_Recv "$@"
}
# If we are running under bash, we can create a coprocess
# and skip all the fifo nonsense.
CmdlineGL_Start() {
	if [[ $# > 0 && "$1" == stdout ]]; then
		CmdlineGL_Mode=stdout
		CmdlineGL_Out=1
		CmdlineGL_Recv(){ return 1; }
	elif [[ $# > 0 && "$1" == rw ]]; then
		if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
			coproc CmdlineGL "${CmdlineGL_Options[@]}" -t || return 1
			CmdlineGL_In="${COPROC[0]}"
			CmdlineGL_Out="${COPROC[1]}"
		else
			# In dark ages of bash, need to find free FDs and connect them to fifos
			CmdlineGL_CreateFifo || return 1
			CmdlineGL_NextFreeFD CmdlineGL_Out || return 1
			eval "exec $CmdlineGL_Out<>\"\$CMDLINEGL_FIFO_DIR/out\""
			# Start CmdlineGL before we open the input fifo, else it would block us
			CmdlineGL "${CmdlineGL_Options[@]}" -t <&$CmdlineGL_Out >"$CMDLINEGL_FIFO_DIR/in" &
			CmdlineGL_NextFreeFD CmdlineGL_In || return 1
			eval "exec $CmdlineGL_In<\"\$CMDLINEGL_FIFO_DIR/in\""
		fi
		CmdlineGL_Mode=rw
		CmdlineGL_Recv(){ read -r -u $CmdlineGL_In CmdlineGL_InputLine; };
	else
		if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
			coproc CmdlineGL "${CmdlineGL_Options[@]}" -t --noevents || return 1
			CmdlineGL_Out="${COPROC[1]}"
		else
			# In dark ages of bash, need to find free FDs and connect them to fifos
			CmdlineGL_CreateFifo || return 1
			CmdlineGL_NextFreeFD CmdlineGL_Out || return 1
			eval "exec $CmdlineGL_Out<>\"\$CMDLINEGL_FIFO_DIR/out\""
			CmdlineGL "${CmdlineGL_Options[@]}" -t <&$CmdlineGL_Out &
		fi
		CmdlineGL_Mode=w
		CmdlineGL_Recv(){ return 1; }
	fi
	CmdlineGL_Send(){
		# Quote each argument, replacing occurrences of \ with \\, newline with \n and " with \"
		{ echo -n "$1"; shift; for x; do x="${x//\\/\\\\}"; x="${x//$'\n'/\\n}"; echo -n ' "'"${x//\"/\\\"}"'"'; done; echo; } >&$CmdlineGL_Out;
	};
	# Performance optimization! whee!  But not for the functions that need quoted arguments...
	for cmd in "${CmdlineGL_Commands[@]}"; do
		if [[ "$cmd" != ftgl* && "$cmd" != cglEcho && "$cmd" != cglLoadImage2D  ]]; then
			eval "$cmd(){ echo $cmd \"\$@\" >&$CmdlineGL_Out; }";
		fi
	done
}

CmdlineGL_CreateFifo() {
	# Create a FIFO unless one is already active
	if [[ "z$CMDLINEGL_FIFO_DIR" = "z" ]]; then
		CMDLINEGL_FIFO_DIR=$(mktemp -d -t CmdlineGL.XXXXXX);
	elif [[ ! -d "$CMDLINEGL_FIFO_DIR" ]]; then
		echo "CMDLINEGL_FIFO_DIR '$CMDLINEGL_FIFO_DIR' does not exist"
		return 1
	fi
	[[ -e "$CMDLINEGL_FIFO_DIR/in" ]] || mkfifo "$CMDLINEGL_FIFO_DIR/in" || {
		echo "Failed to create input fifo";
		return 1;
	}
	[[ -e "$CMDLINEGL_FIFO_DIR/out" ]] || mkfifo "$CMDLINEGL_FIFO_DIR/out" || {
		echo "Failed to create output fifo";
		return 1;
	}
	return 0;
}

CmdlineGL_NextFreeFD() {
	local fd=2 fd_max=$(ulimit -n) dest_var=$1;
	while ((++fd < fd_max)); do ! true <&$fd && break; done 2>&- \
		&& (( $dest_var=fd )) \
		|| { echo "No free file descriptor"; return 1; }
}


================================================
FILE: share/lib-bash/Cube.lib
================================================
# Simple 2x2x2 cube model, with colored faces, which auto-compiles to a display list

Cube_InitGfx() {
	glNewList Cube GL_COMPILE
	glBegin GL_QUADS

	# Top
	glNormal 0 1 0
	glTexCoord 1 1; glVertex 1 1 1
	glTexCoord 1 0; glVertex 1 1 -1
	glTexCoord 0 0; glVertex -1 1 -1
	glTexCoord 0 1; glVertex -1 1 1

	# Right
	glNormal 1 0 0
	glTexCoord 1 0; glVertex 1 1 -1
	glTexCoord 1 1; glVertex 1 1 1
	glTexCoord 0 1; glVertex 1 -1 1
	glTexCoord 0 0; glVertex 1 -1 -1

	# Front
	glNormal 0 0 1
	glTexCoord 1 1; glVertex 1 1 1
	glTexCoord 0 1; glVertex -1 1 1
	glTexCoord 0 0; glVertex -1 -1 1
	glTexCoord 1 0; glVertex 1 -1 1

	# Bottom
	glNormal 0 -1 0
	glTexCoord 1 1; glVertex 1 -1 1
	glTexCoord 0 1; glVertex -1 -1 1
	glTexCoord 0 0; glVertex -1 -1 -1
	glTexCoord 1 0; glVertex 1 -1 -1

	# Left
	glNormal -1 0 0
	glTexCoord 1 1; glVertex -1 1 1
	glTexCoord 1 0; glVertex -1 1 -1
	glTexCoord 0 0; glVertex -1 -1 -1
	glTexCoord 0 1; glVertex -1 -1 1

	# Back
	glNormal 0 0 -1
	glTexCoord 0 1; glVertex 1 1 -1
	glTexCoord 0 0; glVertex 1 -1 -1
	glTexCoord 1 0; glVertex -1 -1 -1
	glTexCoord 1 1; glVertex -1 1 -1

	glEnd
	glEndList

	Cube() { glCallList Cube; }
}

Cube() {
	Cube_InitGfx
	glCallList Cube
}


================================================
FILE: share/lib-bash/Geom.lib
================================================
# Library of some stuff mostly to do with cartesian points, vectors
# and coordinate-systems.
#
CmdlineGL_LoadLib Trig

: ${Geom_FixedPt:=1000}
(( Geom_FixedPt= Geom_FixedPt )) # ensure integer, so it's safe to eval
declare -r Geom_FixedPt=$Geom_FixedPt

## Calculate the magnitude of a vector
# For numbers which are close to forming a unit vector, the initial guess of
# Mag^2/FixedPt is very close, and tends to converge in just 2-4 iterations.
# Note that this only approximates sqrt, since I don't bother to round properly
#
# @param $1: x coordinate
# @param $2: y coordinate
# @param $3: z coordinate
# @return $Result: the approximate magnitude of the vector
#
eval "Magnitude3D() {
	local magsq=\$1*\$1+\$2*\$2+\$3*\$3 mag=magsq/$Geom_FixedPt prevmag=0
	while((prevmag-mag>1||mag-prevmag>1));do((prevmag=mag,mag=(mag+magsq/mag)/2));done
	let Result=mag
}"

InitPoint() {
	let ${1}_x=${2:-0}
	let ${1}_y=${3:-0}
	let ${1}_z=${4:-0}
	eval "${1}_Add() { (( ${1}_x+=\$1, ${1}_y+=\$2, ${1}_z+=\$3 )); }"
	eval "${1}_Scale() { (( ${1}_x=${1}_x*\$1/$Geom_FixedPt, ${1}_y=${1}_y*\$1/$Geom_FixedPt, ${1}_z=${1}_z*\$1/$Geom_FixedPt )); }"
}
# Copy the value of Point $2 into Point $1
ClonePoint() {
	((${1}_x=${2}_x, ${1}_y=${2}_y, ${1}_z=${2}_z))
}
FreePoint() {
	unset ${1}_x ${1}_y ${1}_z ${1}_Add
}
InitVec() {
	InitPoint $1 $2 $3 $4
	eval "${1}_Magnitude() { Magnitude3D \$${1}_x \$${1}_y \$${1}_z; }"
	eval "${1}_SetMagnitude() {
		local NewMag=\$1
		${1}_Magnitude
		((${1}_x=${1}_x*NewMag/Result, ${1}_y=${1}_y*NewMag/Result, ${1}_z=${1}_z*NewMag/Result))
	}"
	eval "${1}_Normalize() {
		${1}_Magnitude
		((${1}_x=${1}_x*$Geom_FixedPt/Result, ${1}_y=${1}_y*$Geom_FixedPt/Result, ${1}_z=${1}_z*$Geom_FixedPt/Result))
	}"
}
FreeVec() {
	unset ${1}_Magnitude ${1}_MakeUnit
	FreePoint $1
}

InitCoordSys() {
	InitVec ${1}_IV $Geom_FixedPt 0 0
	InitVec ${1}_JV 0 $Geom_FixedPt 0
	InitVec ${1}_KV 0 0 $Geom_FixedPt
	InitVec ${1}_Pos 0 0 0

	# Push an OpenGL Matrix that changes the modelview coordinate system to that
 	# of this object.
	#
	eval "${1}_EnterCS() {
		glMultMatrix \$${1}_IV_x/$Geom_FixedPt \$${1}_IV_y/$Geom_FixedPt \$${1}_IV_z/$Geom_FixedPt 0 \\
		             \$${1}_JV_x/$Geom_FixedPt \$${1}_JV_y/$Geom_FixedPt \$${1}_JV_z/$Geom_FixedPt 0 \\
		             \$${1}_KV_x/$Geom_FixedPt \$${1}_KV_y/$Geom_FixedPt \$${1}_KV_z/$Geom_FixedPt 0 \\
		             \$${1}_Pos_x/$Geom_FixedPt \$${1}_Pos_y/$Geom_FixedPt \$${1}_Pos_z/$Geom_FixedPt 1
	}"

	# Push an OpenGL matrix that enters this object's parent coordinate system.
	# This goes on the assumption that this object is the current coordinate system,
	# such as would happen if this CS were a mobile camera.
	# For situations where the modelview is in this coord. sys. due to a _EnterCS(),
 	# just use glPopMatrix
	# Note: this could be done in a single glMultMatrix, but then bash would be
	# doing the math instead of OpenGL.  I'm betting its best to generate this extra
	# line of output, though I didn't benchmark it.
	#
	eval "${1}_ExitCS() {
		glMultMatrix \$${1}_IV_x/$Geom_FixedPt \$${1}_JV_x/$Geom_FixedPt \$${1}_KV_x/$Geom_FixedPt 0 \\
		             \$${1}_IV_y/$Geom_FixedPt \$${1}_JV_y/$Geom_FixedPt \$${1}_KV_y/$Geom_FixedPt 0 \\
		             \$${1}_IV_z/$Geom_FixedPt \$${1}_JV_z/$Geom_FixedPt \$${1}_KV_z/$Geom_FixedPt 0 \\
		             0 0 0 1
		glTranslate -\$${1}_Pos_x/$Geom_FixedPt -\$${1}_Pos_y/$Geom_FixedPt -\$${1}_Pos_z/$Geom_FixedPt
	}"

	# Makes the I/J/K vectors into something close to unit vectors
	#
	eval "${1}_Normalize() {
		${1}_IV_Normalize
		${1}_JV_Normalize
		${1}_KV_Normalize
	}"

	eval "${1}_RelativeYaw() {
		local angle=\$1 x2 y2 z2 cy sy
		# K2= K * cos(y) - I * sin(y)
		sincos \$angle
		(( sy=Result, cy=Result2,
		   ${1}_KV_x= (${1}_KV_x*cy - ${1}_IV_x*sy)/$Trig_SinScale,
		   ${1}_KV_y= (${1}_KV_y*cy - ${1}_IV_y*sy)/$Trig_SinScale,
		   ${1}_KV_z= (${1}_KV_z*cy - ${1}_IV_z*sy)/$Trig_SinScale ))
		${1}_RegenIV
	}"
	eval "${1}_RelativePitch() {
		local angle=\$1 x2 y2 z2 cy sy
		# K2= K * cos(y) - J * sin(y)
		sincos \$angle
		(( sy=Result, cy=Result2,
		   ${1}_KV_x= (${1}_KV_x*cy - ${1}_JV_x*sy)/$Trig_SinScale,
		   ${1}_KV_y= (${1}_KV_y*cy - ${1}_JV_y*sy)/$Trig_SinScale,
		   ${1}_KV_z= (${1}_KV_z*cy - ${1}_JV_z*sy)/$Trig_SinScale ))
		${1}_RegenJV
	}"
	eval "${1}_RelativeRoll() {
		local angle=\$1 x2 y2 z2 cy sy
		# J2= J * cos(y) - I * sin(y)
		sincos \$angle
		(( sy=Result, cy=Result2,
		   ${1}_JV_x= (${1}_JV_x*cy - ${1}_IV_x*sy)/$Trig_SinScale,
		   ${1}_JV_y= (${1}_JV_y*cy - ${1}_IV_y*sy)/$Trig_SinScale,
		   ${1}_JV_z= (${1}_JV_z*cy - ${1}_IV_z*sy)/$Trig_SinScale ))
		${1}_RegenIV
	}"
	# I= J cross K
	eval "${1}_RegenIV() {
		(( x2=(${1}_JV_y*${1}_KV_z - ${1}_JV_z*${1}_KV_y)/$Geom_FixedPt, y2=(${1}_JV_z*${1}_KV_x - ${1}_JV_x*${1}_KV_z)/$Geom_FixedPt, z2=(${1}_JV_x*${1}_KV_y - ${1}_JV_y*${1}_KV_x)/$Geom_FixedPt ))
		(( ${1}_IV_x=x2, ${1}_IV_y=y2, ${1}_IV_z=z2 ))
	}"
	# J= K cross I
	eval "${1}_RegenJV() {
		(( x2=(${1}_KV_y*${1}_IV_z - ${1}_KV_z*${1}_IV_y)/$Geom_FixedPt, y2=(${1}_KV_z*${1}_IV_x - ${1}_KV_x*${1}_IV_z)/$Geom_FixedPt, z2=(${1}_KV_x*${1}_IV_y - ${1}_KV_y*${1}_IV_x)/$Geom_FixedPt ))
		(( ${1}_JV_x=x2, ${1}_JV_y=y2, ${1}_JV_z=z2 ))
	}"
	# K= I cross J
	eval "${1}_RegenKV() {
		(( x2=(${1}_IV_y*${1}_JV_z - ${1}_IV_z*${1}_JV_y)/$Geom_FixedPt, y2=(${1}_IV_z*${1}_JV_x - ${1}_IV_x*${1}_JV_z)/$Geom_FixedPt, z2=(${1}_IV_x*${1}_JV_y - ${1}_IV_y*${1}_JV_x)/$Geom_FixedPt ))
		(( ${1}_KV_x=x2, ${1}_KV_y=y2, ${1}_KV_z=z2 ))
	}"
}

# Copy the value of CoordSys $2 into CoordSys $1
CoordSys_Clone() {
	ClonePoint ${1}_IV ${2}_IV
	ClonePoint ${1}_JV ${2}_JV
	ClonePoint ${1}_KV ${2}_KV
	ClonePoint ${1}_Pos ${2}_Pos
}

# Print the coordinate system
CoordSys_Print() {
	local x, y, z
	((x=${1}_IV_x y=${1}_IV_y z=${1}_IV_z))
	echo "I: ($x,	$y,	$z) Magnitude=$((x*x+y*y+z*z))"
	((x=${1}_JV_x y=${1}_JV_y z=${1}_JV_z))
	echo "J: ($x,	$y,	$z) Magnitude=$((x*x+y*y+z*z))"
	((x=${1}_KV_x y=${1}_KV_y z=${1}_KV_z))
	echo "K: ($x,	$y,	$z) Magnitude=$((x*x+y*y+z*z))"
	echo "Origin: (${1}_Pos_x,	${1}_Pos_y,	${1}_Pos_z)"
}

FreeCoordSys() {
	FreeVec ${1}_IV
	FreeVec ${1}_JV
	FreeVec ${1}_KV
	unset ${1}_EnterCS ${1}_ExitCS ${1}_Normalize
	unset ${1}_RelativeYaw ${1}_RelativePitch ${1}_RelativeRoll
	unset ${1}_RegenIV ${1}_RegenJV ${1}_RegenKV
}


================================================
FILE: share/lib-bash/LaserBeam.lib
================================================
LaserBeam_InitGfx() {
	glNewList LaserBeam GL_COMPILE

	# Front
	glBegin GL_TRIANGLE_FAN
	glColor 1 0.7 0.7
	glVertex 0 0 10
	glColor 1 0.2 0.2
	glVertex 0 0.2 9
	glVertex -0.2 0 9
	glVertex 0 -0.2 9
	glVertex 0.2 0 9
	glVertex 0 0.2 9
	glEnd

	# Tail
	glBegin GL_TRIANGLE_FAN
	glColor 0.6 0 0
	glVertex 0 0 0
	glColor 1 0.2 0.2
	glVertex 0 0.2 9
	glVertex 0.2 0 9
	glVertex 0 -0.2 9
	glVertex -0.2 0 9
	glVertex 0 0.2 9
	glEnd

	glEndList

	LaserBeam() { glCallList LaserBeam; }
}

LaserBeam() {
	LaserBeam_InitGfx
	glCallList LaserBeam
}


================================================
FILE: share/lib-bash/LinInterpolate.lib
================================================
# Title: LinInterpolate.sh
# Author: Michael Conrad
# Date:	2005-05-29
#

## Perform a linear interpolation on a table of values, to simulate a function.
# This routine uses linear interpolation between the nearest two
# elements of an array containing points along the function.
#
# $1: Table: a name of an array
# $2: Pos: a position of the value to retrieve, in the range 0..Max
# $3: Max: the maximum value of Pos
interpolate() {
	# FromIdx: the index of the array to interpolate from
	# ToIdx: the index of the array to interpolate to
	# Pct: the percentage of the distance from From to To which the result should be
	local Table=$1 Pos=$2 Max=$3 TableLen From Pct Idx FromVal ToVal
	eval "TableLen=\${#$Table[@]}"
	(( From= Pos * (TableLen-1), Idx= From/Max, Pct= From - Idx*Max ))
	if ((Idx==$TableMax)); then
		(( Result=Table[Idx] ));
	else
		(( FromVal=$Table[Idx], ToVal=$Table[Idx+1], Result= FromVal + (ToVal - FromVal)*Pct/Max ));
	fi
}

## Create an interpolation function with the array and bounds "compiled" into it.
# This function creates a new function which performs the same operation as
# "interpolate", except that it "compiles" the name, maximum table element,
# and maximum parameter value into the function as constants, for better
# performance.
#
# $1: FunctionName: the name to define for the function
# $2: Table: the name of the table to interpolate from
# $3: InputMax: the maximum input value; this defines the domain of the function
make_interpolate_func() {
	local fname=$1 Table=$2 TableMax TableMax inpMax=$3
	eval "(( TableMax=\${#$Table[@]} - 1 ))"
	eval "$fname() {
		local From Pct Idx FromVal ToVal
		(( From= \$1*$TableMax, Idx=From/$inpMax, Pct=From-Idx*$inpMax ))
		if ((Idx==$TableMax)); then
			(( Result=$Table[Idx] ))
		else
			(( FromVal=$Table[Idx], ToVal=$Table[Idx+1], Result=FromVal+(ToVal-FromVal)*Pct/$inpMax ));
		fi
	}"
}


================================================
FILE: share/lib-bash/ModelViewer.lib
================================================
# ModelView API
#--------------------------------------
#
# This module sets up the translations and handles user input to give the
# effect of a camera looking at a model, which you then render at the origin.
#
# Functions
#   ModelViewer_DispatchEvent EVENT [PARAM...]
#   ModelViewer_Update
#   ModelViewer_ApplyMatrix
# Variables
#   ModelViewer_PanDegPerSec - Number of degrees to rotate per second of continual up/down/left/right keypress
#   ModelViewer_ZoomPctPerSec - Percentage of distance to model which we will travel in one second
#   ModelViewer_Distance   - Initial camera distance from the model
#   ModelViewer_Pitch      - Initial camera vertical angle above the model
#   ModelViewer_Direction  - Initial camera sideways angle around the model
#

CmdlineGL_LoadLib Timing

ModelViewer_Zoom=1000
ModelViewer_Pitch=0
ModelViewer_Direction=0

ModelViewer_MDrag=0
ModelViewer_Left=0
ModelViewer_Right=0
ModelViewer_Up=0
ModelViewer_Down=0
ModelViewer_Grow=0
ModelViewer_Shrink=0
ModelViewer_PanDegPerSec=90
ModelViewer_ZoomPctPerSec=40

# DispatchEvent returns true if it consumed the event, false otherwise.
# Positonal arguments are as received from CmdlineGL
ModelViewer_DispatchEvent() {
	(( $# > 0 )) || return 1
	local Press
	case "$1" in
	K)
		if [[ "$2" == + ]]; then Press=1; else Press=0; fi
		case "$3" in
		right) ((ModelViewer_Right= Press));;
		left)  ((ModelViewer_Left=  Press));;
		up)    ((ModelViewer_Up=    Press));;
		down)  ((ModelViewer_Down=  Press));;
		=)     ((ModelViewer_Grow=  Press));;
		-)     ((ModelViewer_Shrink=Press));;
		*)     return 1;; # not consumed
		esac
		return 0 # consumed
		;;
	M)
		case "$2" in
		@)
			local dx="$5" dy="$6";
			# Handle mouse drag actions.
			# If the mouse has moved since last time change the pitch or direction
			# by the vertical or horizontal distance the mouse has moved.
			if ((ModelViewer_MDrag)); then
				((ModelViewer_Pitch+= dy*1000))
				((ModelViewer_Direction+= dx*1000));
				return 0 # consumed
			fi
			;;
		+)
			if [[ "$3" == 1 ]]; then
				ModelViewer_MDrag=1
				return 0 # consumed
			fi;;
		-)
			if [[ "$3" == 1 ]]; then
				ModelViewer_MDrag=0
				return 0 # consumed
			fi;;
		esac
		return 1 # not consumed
		;;
	esac
}

# The user input events toggle variables, but don't modify the viewer state, yet.
# Call Update once per frame to apply those inputs to the viewer state.
ModelViewer_Update() {
	# All numbers are scaled by 1000 to match time reported in milliseconds
	((ModelViewer_Left))  && ((ModelViewer_Direction+=Timing_dT*ModelViewer_PanDegPerSec))
	((ModelViewer_Right)) && ((ModelViewer_Direction-=Timing_dT*ModelViewer_PanDegPerSec))
	((ModelViewer_Up))    && ((ModelViewer_Pitch-=Timing_dT*ModelViewer_PanDegPerSec))
	((ModelViewer_Down))  && ((ModelViewer_Pitch+=Timing_dT*ModelViewer_PanDegPerSec))
	local adjust;
	((adjust=ModelViewer_Grow? ( ModelViewer_Shrink? 0 : -1 ) : ModelViewer_Shrink? 1 : 0))
	((adjust= adjust + adjust * ModelViewer_ZoomPctPerSec*ModelViewer_Zoom*Timing_dT/100000))
	((ModelViewer_Zoom += adjust))
	((ModelViewer_Zoom>=10)) || ((ModelViewer_Zoom=10))
}

# Call this right after LoadIdentity to move from camera space to model space
ModelViewer_ApplyMatrix() {
	glScale 1000/$ModelViewer_Zoom
	glRotate $ModelViewer_Pitch/1000 1/1 0 0
	glRotate $ModelViewer_Direction/1000 0 1/1 0
}


================================================
FILE: share/lib-bash/RenderLoop.lib
================================================
CmdlineGL_LoadLib Timing

# RenderLoop API
#--------------------------------------
# Functions:
#   RenderLoop_Run
# Callbacks (to be implemented by user)
#   RenderLoop_Render
#   RenderLoop_DispatchEvent EVENT_TYPE [EVENT_ARGS...]
#
# Variables:
#   RenderLoop_Done: bool - Set to true in order to end main loop
#
RenderLoop_Done=0

# Generic do-nothing place holder to be replaced by caller
RenderLoop_Render() {
	glClear GL_COLOR_BUFFER_BIT
	cglSwapBuffers
}

# Generic do-nothing place holder which receives each input event
RenderLoop_DispatchEvent() {
	false
}

# Using Timing lib, repeatedly run RenderLoop_Render and RenderLoop_DispatchEvent
# as needed and terminate when RenderLoop_Done is set.
RenderLoop_Run() {
	RenderLoop_Done="";
	if [[ -z "$CmdlineGL_In" ]]; then
		# For write-only mode, assume a constant max frame rate.
		# If we get too far ahead, the pipe will fill and pause us.
		while (( !RenderLoop_Done )); do
			RenderLoop_Render || true
			Timing_SyncNextFrame
			Timing_AssumeNextFrame
		done
	else
		# For read-write mode, ask for the time before starting the frame,
		# then when we collect the input we know we're done when we see
		# the timestamp come back, and don't need to do awkward non-blocking
		# reads.
		cglGetTime
		while (( !RenderLoop_Done )); do
			RenderLoop_Render || true
			Timing_SyncNextFrame
			RenderLoop_ProcessInput
			cglGetTime
		done
	fi
}

# Split out for readability
RenderLoop_ProcessInput() {
	local ReadMore=1 ReadTimeout=0
	while ((ReadMore)); do
		if CmdlineGL_Recv; then
			if [[ "$CmdlineGL_InputLine" == t=* ]]; then
				# The time is the last thing we read for this frame's input
				Timing_Update ${CmdlineGL_InputLine:2}
				ReadMore=0
			else
				RenderLoop_DispatchEvent $CmdlineGL_InputLine
			fi
		else
			let ReadTimeout++
			if ((ReadTimeout>3)); then
				RenderLoop_Done=1
				ReadMore=0
				echo "Reading from pipe timed out...  shutting down." >&2
			fi
		fi
	done
}


================================================
FILE: share/lib-bash/Ship.lib
================================================
# This is the model for the player's ship.
# It was originally designed facing the -Z axis, but then I decided to make
# all my models face down +z, and rather than mangling all the coordinates,
# I just rotate 180 before drawing it.
#

CmdlineGL_LoadLib Trig Geom

Ship_Throttle=1
Ship_Initialized=0
Ship_GunXOffset=( -$((12*Geom_FixedPt/10)) $((12*Geom_FixedPt/10)) -$((16*Geom_FixedPt/10)) $((16*Geom_FixedPt/10)) )
Ship_GunYOffset=$((3*Geom_FixedPt/10))
Ship_GunZOffset=$((9*Geom_FixedPt/10))

Ship_WingShield() {
	#inside
	glBegin GL_TRIANGLE_FAN;
	glNormal 1 0 0; 	glVertex 0 0 0
	glNormal 1 0 0.2;	glVertex 0.2 0 -3
	glNormal 1 -0.7 0;	glVertex 0.2 1 0.5
	glNormal 1 0 -0.2;	glVertex 0.2 0 2
	glNormal 1 0.7 0;	glVertex 0.2 -1 0.5
	glNormal 1 0 0.2;	glVertex 0.2 0 -3
	glEnd
	#outside
	glBegin GL_TRIANGLE_FAN;
	glNormal -1 0 0;	glVertex -0.5 0 0
	glNormal -1 1 0;	glVertex 0.2 1 0.5
	glNormal -1 0 -0.3;	glVertex 0.2 0 -3
	glNormal -1 -1 0;	glVertex 0.2 -1 0.5
	glNormal -1 0 0.3;	glVertex 0.2 0 2
	glNormal -1 1 0;	glVertex 0.2 1 0.5
	glEnd
}

Ship_Wing() {
	glBegin GL_QUAD_STRIP
	glNormal 0 1 0.1;   glVertex -2 0 0.5;     glVertex 0 0 1.5
	glNormal 0 1 -0.1;  glVertex -2 0.3 -0.3;  glVertex 0 0.3 -0.5
	glNormal 0 0 -1;    glVertex -2 0 -0.4;    glVertex 0 0 -0.6
	glNormal 0 -2 -0.1; glVertex -2 -0.3 -0.3; glVertex 0 -0.3 -0.5
	glNormal 0 -1 0.1;  glVertex -2 0 0.5;     glVertex 0 0 1.5
	glEnd
}

Ship_HalfBody() {
	glBegin GL_TRIANGLE_STRIP
	glNormal 0.1 1 -0.1
	glVertex 0 0 -6
	glVertex 0 0.2 -4
	glVertex 0.8 0 -4.5
	glNormal 0 1 0
	glVertex 0 0.3 -3
	glVertex 1 0 -3.3
	glNormal 0 1 -0.3
	glVertex 0 0.2 0
	glVertex 0.8 0 0
	glEnd
	glBegin GL_TRIANGLE_STRIP
	glNormal 0.1 -1 -0.1
	glVertex 0 0 -6
	glVertex 0.8 0 -4.5
	glVertex 0 -0.2 -4
	glNormal 0 -1 0
	glVertex 1 0 -3.3
	glVertex 0 -0.3 -3
	glNormal 0 -1 -0.3
	glVertex 0.8 0 0
	glVertex 0 -0.2 0
	glEnd
}

Ship_Thruster() {
	# Back half of thruster
	gluCylinder thruster 0.4 0.2 1.7 7 1
	glTranslate 0 0 1.7
	gluCylinder thruster 0.2 0.17 0 7 1
	glTranslate 0 0 -1.7
	glRotate 180 0 1 0
	# Front half of thruster
	gluCylinder thruster 0.4 0.2 1.1 7 1
	glTranslate 0 0 1.1
	gluCylinder thruster 0.18 0.2 0 7 1
	gluCylinder innerthruster 0.18 0.0 -0.5 7 1
}
Ship_BuildThrustLists() {
	glNewList Thrust0 GL_COMPILE
	glDisable GL_LIGHTING
	glBegin GL_TRIANGLE_FAN
	glColor '#770000'
	glVertex 0 0 1.5
	glColor '#330000'
	for ((i=0; i<8; i++)); do
		sincos $((-36000*i/7))
		glVertex $((Result*18))/1000000 $((Result2*18))/1000000 1.7
	done
	glEnd
	glEnable GL_LIGHTING
	glEndList

	glNewList Thrust1 GL_COMPILE
	glDisable GL_LIGHTING
	glDisable GL_CULL_FACE
	glBegin GL_TRIANGLE_FAN
	glColor '#CC3300'
	glVertex 0 0 1.5
	glColor '#551100'
	for ((i=0; i<8; i++)); do
		sincos $((-36000*i/7))
		glVertex $((Result*18))/1000000 $((Result2*18))/1000000 1.7
	done
	glEnd
	glBlendFunc GL_SRC_ALPHA GL_ONE
	glEnable GL_BLEND
	glBegin GL_TRIANGLE_STRIP
	for ((i=0; i<8; i++)); do
		sincos $((36000*i/7))
		glColor '#AA0000DD'
		glVertex $((Result*18))/1000000 $((Result2*18))/1000000 1.7
		glColor '#33000000'
		glVertex $((Result*8))/1000000 $((Result2*8))/1000000 2.3
	done
	glEnd
	glEnable GL_CULL_FACE
	glDisable GL_BLEND
	glEnable GL_LIGHTING
	glEndList
}
Ship_Thrust() {
	glRotate $RANDOM 0 0 1
	if ((Ship_Throttle)); then glCallList Thrust1; else glCallList Thrust0; fi
}
Ship_Gun() {
	glPushMatrix
	glBegin GL_TRIANGLE_FAN
	glNormal 0 1 0; 	glVertex 0 0.1 0
	glNormal 0 0.5 1;	glVertex 0 -0.15 0.3
	glNormal 1 0.5 -1;	glVertex 0.1 0 -0.1
	glNormal -1 0.5 -1;	glVertex -0.1 0 -0.1
	glNormal 0 0.5 1;	glVertex 0 -0.15 0.3
	glEnd
	glTranslate 0 0 -1
	gluCylinder gun 0.025 0.05 1 5 1
	glPopMatrix
}

Ship_InitGfx() {
	gluNewQuadric thruster
	gluNewQuadric innerthruster
	gluNewQuadric gun
	gluQuadricOrientation innerthruster GLU_INSIDE

	glNewList Ship GL_COMPILE
	glDisable GL_TEXTURE_2D
	glColor 0.4 0.4 0.4
	glPushMatrix
		Ship_HalfBody
		Ship_Wing
		glTranslate -2 0 0
		Ship_WingShield
		glTranslate 4 0 0
		glRotate 180 0 0 1
		Ship_WingShield
		glTranslate 2 0 0
		Ship_Wing
		Ship_HalfBody
	glPopMatrix

	glColor 0.2 0.2 0.2
	glPushMatrix
		glTranslate -1.9 -0.4 -0.2
		glRotate 10 0 1 0
		glRotate 5 1 0 0
		Ship_Thruster
	glPopMatrix
	glPushMatrix
		glTranslate 1.9 -0.4 -0.2
		glRotate -10 0 1 0
		glRotate 5 1 0 0
		Ship_Thruster
	glPopMatrix

	glPushMatrix
		glTranslate -1.2 0.3 -0.1
		Ship_Gun
		glTranslate -0.4 0 0
		Ship_Gun
		glTranslate 2.8 0 0
		Ship_Gun
		glTranslate 0.4 0 0
		Ship_Gun
	glPopMatrix
	glEndList

	Ship_BuildThrustLists

	Ship_Initialized=1;
}

Ship() {
	((Ship_Initialized))||Ship_InitGfx
	cglPushDivisor 1
	glPushMatrix
	glRotate 180 0 1 0 # reverse model on Z axis
	glCallList Ship
	glPushMatrix
	glTranslate -1.9 -0.4 -0.2
	glRotate 10 0 1 0
	glRotate 5 1 0 0
	Ship_Thrust
	glPopMatrix
	glTranslate 1.9 -0.4 -0.2
	glRotate -10 0 1 0
	glRotate 5 1 0 0
	Ship_Thrust
	glPopMatrix
	cglPopDivisor
}


================================================
FILE: share/lib-bash/Timing.lib
================================================
# Prevent multiple inclusion
[[ -z ${TIMING_LIB+x} ]] || return 0
TIMING_LIB=1;

# Timing API
#--------------------------------------
#
# This library uses a concept of "Virtual Frames per Second"; any time the
# game loop isn't able to maintain the minimum frame rate it will slow down
# the game time to match the speed of the computer.  In other words, Timing_T
# may count slower than real time in order to maintain the minimum FPS needed
# by game logic.
#
# Functions:
#   Timing_SetMinMaxFPS MIN MAX
#   Timing_Update TIME
#   Timing_SyncNextFrame
#   Timing_AssumeNextFrame
#   Timing_PrintFPS
#
# Variables:
#
#   Timing_T: milliseconds - virtual time
#   Timing_RT: milliseconds - real time
#   Timing_Frame: current frame number
#   Timing_dT: milliseconds - virtual time elapsed since last frame
#   Timing_FPS: int - frames per real second
#   Timing_FPVS: int - frames per virtual second
#   Timing_AvgCount: int - number of samples to average
#   Timing_MinFPS: int - lowest allowed FPVS
#   Timing_MaxFPS: int - highest allowed FPS
#
Timing_T=0;
Timing_RT=0;
Timing_Slip=0;
Timing_dT=0;
Timing_dTAvg=100;
Timing_dRTAvg=100;
Timing_AvgCount=10;
Timing_FPS=0;
Timing_FPVS=0;
Timing_FPSPrint_dT=0;
Timing_FPSLastPrint=0;
Timing_Frame=0;

# Choose the minimum frame rate (below which the "game time" slows down)
# and the max frame rate (above which we sleep)
Timing_SetMinMaxFPS() {
	local NewMin=$1
	local NewMax=$2
	(( Timing_MinFPS=NewMin ))
	(( Timing_MaxFPS=NewMax ))
	(( Timing_MaxFPS < Timing_MinFPS )) && (( Timing_MaxFPS=Timing_MinFPS ))
	(( Timing_MaxFPS < 1 )) && Timing_MaxFPS=1
	(( Timing_Min_dT=1000/Timing_MaxFPS ))
	if (( Timing_MinFPS > 0 )); then
		(( Timing_Max_dT=(1000+Timing_MinFPS/2)/Timing_MinFPS )) # round up
	else
		Timing_Max_dT=1000000;
	fi
}

# Update the game time with a new "Real" timestamp, and assume a frame is completed.
# This will adjust the game time according to Min FPS, and recalculate the current FPS
Timing_Update() {
	local NewTime=$1
	((
		Timing_dT=NewTime-Timing_RT,
		Timing_Frame++,
		Timing_dT<1? Timing_dT=1 : 1,
		Timing_dRTAvg= (Timing_dRTAvg * Timing_AvgCount + Timing_dT*100) / (Timing_AvgCount+1),
		Timing_dT>Timing_Max_dT? Timing_dT=Timing_Max_dT : 1,
		Timing_RT=NewTime, Timing_T+=Timing_dT,
		Timing_dTAvg= (Timing_dTAvg * Timing_AvgCount + Timing_dT*100) / (Timing_AvgCount+1),

		Timing_FPVS=100000/Timing_dTAvg, Timing_FPS=100000/Timing_dRTAvg,
		1
	))
	if (( (Timing_FPSPrint_dT? (Timing_T - Timing_FPSLastPrint) : -1) > Timing_FPSPrint_dT )); then
		Timing_PrintFPS
		((Timing_FPSLastPrint+=Timing_FPSPrintFreq, Frame=0))
	fi
	return 0
}

# Send the command to CmdlineGL asking it to pause until the given timestamp.
Timing_SyncNextFrame() {
	cglSync $(( Timing_RT+Timing_Min_dT ))
}

# Simulate a maximum frame rate.  Used when we don't get feedback from CmdlineGL
# about the current time, like when generating a recording.
Timing_AssumeNextFrame() {
	Timing_Update $(( Timing_RT+Timing_Min_dT ))
}

# Called automatically by Update if Timing_FPSPrint_dT > 0
Timing_PrintFPS() {
	echo "FPS: $Timing_FPS  $Timing_Frame" >&2
}

Timing_SetMinMaxFPS 10 80


================================================
FILE: share/lib-bash/Trig.lib
================================================
# Title: Trig.sh
# Author: Michael Conrad
# Date: 2005-05-29
#
# Some useful trig functions.
# All are approximated using linear interpolation over a table of values.
#
CmdlineGL_LoadLib LinInterpolate
Trig_SinScale=10000

SIN_TABLE=( 0 0523 1045 1564 2079 2588 3090 3584 4067 4540 5000 5446 5878 6293 6691 7071 7431 7771 8090 8387 8660 8910 9135 9336 9511 9659 9781 9877 9945 9986 10000 )
SIN_TABLE_LEN=${#SIN_TABLE[@]}
let SIN_TABLE_MAX=SIN_TABLE_LEN-1

ASIN_TABLE=( 0 115 229 344 459 574 689 805 921 1037 1154 1271 1389 1507 1626 1746 1866 1988 2110 2233 2358 2483 2610 2739 2869 3000 3133 3268 3406 3545 3687 3832 3979 4130 4284 4443 4605 4773 4946 5126 5313 5508 5714 5932 6164 6416 6693 7005 7374 7852 9000 )
ASIN_TABLE_LEN=${#ASIN_TABLE[@]}
ASIN_TAIL_TABLE=( 6416 6469 6523 6578 6635 6693 6752 6813 6875 6939 7005 7073 7144 7218 7294 7374 7456 7547 7641 7742 7852 7974 8111 8275 8487 9000 )
ASIN_TAIL_TABLE_LEN=${#ASIN_TAIL_TABLE[@]}
let ASIN_TABLE_MAX=ASIN_TABLE_LEN-1
let ASIN_TAIL_TABLE_MAX=ASIN_TAIL_TABLE_LEN-1

## Autogeneration of "trig_interpolate"
# This generates an interpolation function that uses SIN_TABLE and
# expects params in the range 0-9000
#
make_interpolate_func trig_interpolate SIN_TABLE 9000

## Autogeneration of "trig_inv_interpolate"
# This generates an interpolation function for the inverse trig functions
# that uses ASIN_TABLE and expects params in the range 0-10000
#
# trig_inv_tail_interpolate handles arcsin inputs of 9000-10000
# It provides much better tracking of the arcsin function over this range
#
make_interpolate_func trig_inv_interpolate ASIN_TABLE 10000
make_interpolate_func trig_inv_tail_interpolate ASIN_TAIL_TABLE 1000

## Get the sine of an angle
# @Param $1: the angle, as an integer from 0 to 36000 (or any modular equivalent)
# @return $Result: the sin of the angle, from -10000 to 10000
#
sin() {
	local Angle=$1 Flip=1
	(( Angle=(Angle%36000+36000)%36000 ));
	if (( Angle >= 18000 )); then (( Angle-=18000, Flip=-1 )); fi
	if (( Angle >= 9000 )); then (( Angle=18000-Angle )); fi
	trig_interpolate $Angle
	(( Result*=Flip ));
}

## Get the cosine of an angle
# @Param $1: the angle, as an integer from 0 to 36000 (or any modular equivalent)
# @return $Result: the cos of the angle, from -10000 to 10000
#
cos() {
	local Angle=$1 Flip=1
	(( Angle=(Angle%36000+36000)%36000 ));
	if (( Angle >= 18000 )); then (( Angle=36000-Angle )); fi
	if (( Angle >= 9000 )); then (( Angle-=9000, Flip=-1 )); else (( Angle=9000-Angle )); fi
	trig_interpolate $Angle
	(( Result*=Flip ));
}

## Perform both Sine and Cosine
# This function performs both the sin and cosine on an angle, and runs significantly
# faster than calling them separately.
#
# @Param $1: the angle, as an integer from 0 to 36000 (or any modular equivalent)
# @return $Result: the sin of the angle, from -10000 to 10000
# @return $Result2: the cos of the angle, from -10000 to 10000
#
# It was a sad day indeed when I realized that runtimes went down when I eliminated whitespace,
# and that doing the substitutions during the declaration of the function instead of at runtime
# made a significant difference...
#
eval "sincos() {
local Angle sFlip=1 cFlip=1 From Pct Idx FromVal ToVal
((Angle=(\$1%36000+36000)%36000));
if((Angle>=9000&&Angle<27000)); then cFlip=-1;fi
if((Angle>=18000));then((Angle-=18000,sFlip=-1));fi
if((Angle>=9000));then((Angle=18000-Angle));fi
((From=Angle*$SIN_TABLE_MAX,Idx=From/9000,Pct=From-Idx*9000))
if((Idx==$SIN_TABLE_MAX));then
((Result=SIN_TABLE[$SIN_TABLE_MAX]*sFlip,Result2=SIN_TABLE[0]*cFlip))
else
((FromVal=SIN_TABLE[Idx],ToVal=SIN_TABLE[Idx+1],Result=(FromVal+(ToVal-FromVal)*Pct/9000)*sFlip));
((FromVal=SIN_TABLE[$SIN_TABLE_MAX-Idx],ToVal=SIN_TABLE[$((SIN_TABLE_MAX-1))-Idx],Result2=(FromVal+(ToVal-FromVal)*Pct/9000)*cFlip));
fi
}"

tan() {
	sincos $1
	(( Result=Result*10000/Result2 ))
}

## Get the angle of a Sine
# @Param $1: the sine of the angle, as an integer from -10000..10000
# @return Result: the angle of the sine, as an integer from -9000..9000
#
asin() {
	local Sin=$1 Sign=1
	if (( Sin < 0 )); then (( Sin=-Sin, Sign=-1 )); fi
	if (( Sin <= 9000 )); then trig_inv_interpolate $Sin
	else (( Sin=Sin-9000 )); trig_inv_tail_interpolate $Sin; fi
	(( Result*=Sign ));
}

## Get the angle of a Cosine
# @Param $1: the cosine of the angle, as an integer from -10000 to 10000
# @return Result: the angle of the cosine, as an integer from 0 to 18000
#
acos() {
	local Cos=$1 Sign=1
	if (( Cos < 0 )); then (( Cos=-Cos, Sign=-1 )); fi
	if (( Cos <= 9000 )); then trig_inv_interpolate $Cos
	else (( Cos=Cos-9000 )); trig_inv_tail_interpolate $Cos; fi
	(( Result=9000-(Result*Sign) ));
}


================================================
FILE: share/lib-sh/CmdlineGL.lib
================================================
CmdlineGL_setup_fifo() {
	# Create a FIFO unless one is already active
	if [ "z$CMDLINEGL_FIFO_DIR" = "z" ]; then
		CMDLINEGL_FIFO_DIR=$(mktemp -d -t CmdlineGL.XXXXXX);
	elif [ ! -d "$CMDLINEGL_FIFO_DIR" ]; then
		echo "CMDLINE_FIFO_DIR '$CMDLINEGL_FIFO_DIR' does not exist"
		return 1
	fi
	[ -e "$CMDLINEGL_FIFO_DIR/out" ] || mkfifo "$CMDLINEGL_FIFO_DIR/out" || {
		echo "Failed to create output fifo";
		return 1;
	}
	[ -e "$CMDLINEGL_FIFO_DIR/in" ] || mkfifo "$CMDLINEGL_FIFO_DIR/in" || {
		echo "Failed to create input fifo";
		return 1;
	}
	return 0;
}

CmdlineGL_Start() {
	CmdlineGL_setup_fifo
	if [ "x$1" = "rw" ]; then
		CmdlineGL -f $CmdlineGL_Out > $CmdlineGL_In &
		CmdlineGL_Mode=rw
		CmdlineGL_Out=$CMDLINEGL_FIFO_DIR/out;
		CmdlineGL_In=$CMDLINEGL_FIFO_DIR/in;
	else
		CmdlineGL -f $CmdlineGL_Out --nouimsg &
		CmdlineGL_Mode=w
		CmdlineGL_Out=$CMDLINEGL_FIFO_DIR/out;
	fi
}
# build functions for each available command in the API
for cmd in `CmdlineGL --showcmds`; do
	eval "$cmd() { test \"z\$CmdlineGL_Mode\" != \"z\" || CmdlineGL_start; echo \"$cmd \$@\" >>\$CmdlineGL_Out; }"
done
CmdlineGL_ReadInput() {
	CmdlineGL_InputLine=$(read -r <$CmdlineGL_In);
}


================================================
FILE: src/ConstList.Win32.txt
================================================
GL_VERSION_1_1
GL_ACCUM
GL_LOAD
GL_RETURN
GL_MULT
GL_ADD
GL_NEVER
GL_LESS
GL_EQUAL
GL_LEQUAL
GL_GREATER
GL_NOTEQUAL
GL_GEQUAL
GL_ALWAYS
GL_CURRENT_BIT
GL_POINT_BIT
GL_LINE_BIT
GL_POLYGON_BIT
GL_POLYGON_STIPPLE_BIT
GL_PIXEL_MODE_BIT
GL_LIGHTING_BIT
GL_FOG_BIT
GL_DEPTH_BUFFER_BIT
GL_ACCUM_BUFFER_BIT
GL_STENCIL_BUFFER_BIT
GL_VIEWPORT_BIT
GL_TRANSFORM_BIT
GL_ENABLE_BIT
GL_COLOR_BUFFER_BIT
GL_HINT_BIT
GL_EVAL_BIT
GL_LIST_BIT
GL_TEXTURE_BIT
GL_SCISSOR_BIT
GL_ALL_ATTRIB_BITS
GL_POINTS
GL_LINES
GL_LINE_LOOP
GL_LINE_STRIP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
GL_QUADS
GL_QUAD_STRIP
GL_POLYGON
GL_ZERO
GL_ONE
GL_SRC_COLOR
GL_ONE_MINUS_SRC_COLOR
GL_SRC_ALPHA
GL_ONE_MINUS_SRC_ALPHA
GL_DST_ALPHA
GL_ONE_MINUS_DST_ALPHA
GL_DST_COLOR
GL_ONE_MINUS_DST_COLOR
GL_SRC_ALPHA_SATURATE
GL_TRUE
GL_FALSE
GL_CLIP_PLANE0
GL_CLIP_PLANE1
GL_CLIP_PLANE2
GL_CLIP_PLANE3
GL_CLIP_PLANE4
GL_CLIP_PLANE5
GL_BYTE
GL_UNSIGNED_BYTE
GL_SHORT
GL_UNSIGNED_SHORT
GL_INT
GL_UNSIGNED_INT
GL_FLOAT
GL_2_BYTES
GL_3_BYTES
GL_4_BYTES
GL_DOUBLE
GL_NONE
GL_FRONT_LEFT
GL_FRONT_RIGHT
GL_BACK_LEFT
GL_BACK_RIGHT
GL_FRONT
GL_BACK
GL_LEFT
GL_RIGHT
GL_FRONT_AND_BACK
GL_AUX0
GL_AUX1
GL_AUX2
GL_AUX3
GL_NO_ERROR
GL_INVALID_ENUM
GL_INVALID_VALUE
GL_INVALID_OPERATION
GL_STACK_OVERFLOW
GL_STACK_UNDERFLOW
GL_OUT_OF_MEMORY
GL_2D
GL_3D
GL_3D_COLOR
GL_3D_COLOR_TEXTURE
GL_4D_COLOR_TEXTURE
GL_PASS_THROUGH_TOKEN
GL_POINT_TOKEN
GL_LINE_TOKEN
GL_POLYGON_TOKEN
GL_BITMAP_TOKEN
GL_DRAW_PIXEL_TOKEN
GL_COPY_PIXEL_TOKEN
GL_LINE_RESET_TOKEN
GL_EXP
GL_EXP2
GL_CW
GL_CCW
GL_COEFF
GL_ORDER
GL_DOMAIN
GL_CURRENT_COLOR
GL_CURRENT_INDEX
GL_CURRENT_NORMAL
GL_CURRENT_TEXTURE_COORDS
GL_CURRENT_RASTER_COLOR
GL_CURRENT_RASTER_INDEX
GL_CURRENT_RASTER_TEXTURE_COORDS
GL_CURRENT_RASTER_POSITION
GL_CURRENT_RASTER_POSITION_VALID
GL_CURRENT_RASTER_DISTANCE
GL_POINT_SMOOTH
GL_POINT_SIZE
GL_POINT_SIZE_RANGE
GL_POINT_SIZE_GRANULARITY
GL_LINE_SMOOTH
GL_LINE_WIDTH
GL_LINE_WIDTH_RANGE
GL_LINE_WIDTH_GRANULARITY
GL_LINE_STIPPLE
GL_LINE_STIPPLE_PATTERN
GL_LINE_STIPPLE_REPEAT
GL_LIST_MODE
GL_MAX_LIST_NESTING
GL_LIST_BASE
GL_LIST_INDEX
GL_POLYGON_MODE
GL_POLYGON_SMOOTH
GL_POLYGON_STIPPLE
GL_EDGE_FLAG
GL_CULL_FACE
GL_CULL_FACE_MODE
GL_FRONT_FACE
GL_LIGHTING
GL_LIGHT_MODEL_LOCAL_VIEWER
GL_LIGHT_MODEL_TWO_SIDE
GL_LIGHT_MODEL_AMBIENT
GL_SHADE_MODEL
GL_COLOR_MATERIAL_FACE
GL_COLOR_MATERIAL_PARAMETER
GL_COLOR_MATERIAL
GL_FOG
GL_FOG_INDEX
GL_FOG_DENSITY
GL_FOG_START
GL_FOG_END
GL_FOG_MODE
GL_FOG_COLOR
GL_DEPTH_RANGE
GL_DEPTH_TEST
GL_DEPTH_WRITEMASK
GL_DEPTH_CLEAR_VALUE
GL_DEPTH_FUNC
GL_ACCUM_CLEAR_VALUE
GL_STENCIL_TEST
GL_STENCIL_CLEAR_VALUE
GL_STENCIL_FUNC
GL_STENCIL_VALUE_MASK
GL_STENCIL_FAIL
GL_STENCIL_PASS_DEPTH_FAIL
GL_STENCIL_PASS_DEPTH_PASS
GL_STENCIL_REF
GL_STENCIL_WRITEMASK
GL_MATRIX_MODE
GL_NORMALIZE
GL_VIEWPORT
GL_MODELVIEW_STACK_DEPTH
GL_PROJECTION_STACK_DEPTH
GL_TEXTURE_STACK_DEPTH
GL_MODELVIEW_MATRIX
GL_PROJECTION_MATRIX
GL_TEXTURE_MATRIX
GL_ATTRIB_STACK_DEPTH
GL_CLIENT_ATTRIB_STACK_DEPTH
GL_ALPHA_TEST
GL_ALPHA_TEST_FUNC
GL_ALPHA_TEST_REF
GL_DITHER
GL_BLEND_DST
GL_BLEND_SRC
GL_BLEND
GL_LOGIC_OP_MODE
GL_INDEX_LOGIC_OP
GL_COLOR_LOGIC_OP
GL_AUX_BUFFERS
GL_DRAW_BUFFER
GL_READ_BUFFER
GL_SCISSOR_BOX
GL_SCISSOR_TEST
GL_INDEX_CLEAR_VALUE
GL_INDEX_WRITEMASK
GL_COLOR_CLEAR_VALUE
GL_COLOR_WRITEMASK
GL_INDEX_MODE
GL_RGBA_MODE
GL_DOUBLEBUFFER
GL_STEREO
GL_RENDER_MODE
GL_PERSPECTIVE_CORRECTION_HINT
GL_POINT_SMOOTH_HINT
GL_LINE_SMOOTH_HINT
GL_POLYGON_SMOOTH_HINT
GL_FOG_HINT
GL_TEXTURE_GEN_S
GL_TEXTURE_GEN_T
GL_TEXTURE_GEN_R
GL_TEXTURE_GEN_Q
GL_PIXEL_MAP_I_TO_I
GL_PIXEL_MAP_S_TO_S
GL_PIXEL_MAP_I_TO_R
GL_PIXEL_MAP_I_TO_G
GL_PIXEL_MAP_I_TO_B
GL_PIXEL_MAP_I_TO_A
GL_PIXEL_MAP_R_TO_R
GL_PIXEL_MAP_G_TO_G
GL_PIXEL_MAP_B_TO_B
GL_PIXEL_MAP_A_TO_A
GL_PIXEL_MAP_I_TO_I_SIZE
GL_PIXEL_MAP_S_TO_S_SIZE
GL_PIXEL_MAP_I_TO_R_SIZE
GL_PIXEL_MAP_I_TO_G_SIZE
GL_PIXEL_MAP_I_TO_B_SIZE
GL_PIXEL_MAP_I_TO_A_SIZE
GL_PIXEL_MAP_R_TO_R_SIZE
GL_PIXEL_MAP_G_TO_G_SIZE
GL_PIXEL_MAP_B_TO_B_SIZE
GL_PIXEL_MAP_A_TO_A_SIZE
GL_UNPACK_SWAP_BYTES
GL_UNPACK_LSB_FIRST
GL_UNPACK_ROW_LENGTH
GL_UNPACK_SKIP_ROWS
GL_UNPACK_SKIP_PIXELS
GL_UNPACK_ALIGNMENT
GL_PACK_SWAP_BYTES
GL_PACK_LSB_FIRST
GL_PACK_ROW_LENGTH
GL_PACK_SKIP_ROWS
GL_PACK_SKIP_PIXELS
GL_PACK_ALIGNMENT
GL_MAP_COLOR
GL_MAP_STENCIL
GL_INDEX_SHIFT
GL_INDEX_OFFSET
GL_RED_SCALE
GL_RED_BIAS
GL_ZOOM_X
GL_ZOOM_Y
GL_GREEN_SCALE
GL_GREEN_BIAS
GL_BLUE_SCALE
GL_BLUE_BIAS
GL_ALPHA_SCALE
GL_ALPHA_BIAS
GL_DEPTH_SCALE
GL_DEPTH_BIAS
GL_MAX_EVAL_ORDER
GL_MAX_LIGHTS
GL_MAX_CLIP_PLANES
GL_MAX_TEXTURE_SIZE
GL_MAX_PIXEL_MAP_TABLE
GL_MAX_ATTRIB_STACK_DEPTH
GL_MAX_MODELVIEW_STACK_DEPTH
GL_MAX_NAME_STACK_DEPTH
GL_MAX_PROJECTION_STACK_DEPTH
GL_MAX_TEXTURE_STACK_DEPTH
GL_MAX_VIEWPORT_DIMS
GL_MAX_CLIENT_ATTRIB_STACK_DEPTH
GL_SUBPIXEL_BITS
GL_INDEX_BITS
GL_RED_BITS
GL_GREEN_BITS
GL_BLUE_BITS
GL_ALPHA_BITS
GL_DEPTH_BITS
GL_STENCIL_BITS
GL_ACCUM_RED_BITS
GL_ACCUM_GREEN_BITS
GL_ACCUM_BLUE_BITS
GL_ACCUM_ALPHA_BITS
GL_NAME_STACK_DEPTH
GL_AUTO_NORMAL
GL_MAP1_COLOR_4
GL_MAP1_INDEX
GL_MAP1_NORMAL
GL_MAP1_TEXTURE_COORD_1
GL_MAP1_TEXTURE_COORD_2
GL_MAP1_TEXTURE_COORD_3
GL_MAP1_TEXTURE_COORD_4
GL_MAP1_VERTEX_3
GL_MAP1_VERTEX_4
GL_MAP2_COLOR_4
GL_MAP2_INDEX
GL_MAP2_NORMAL
GL_MAP2_TEXTURE_COORD_1
GL_MAP2_TEXTURE_COORD_2
GL_MAP2_TEXTURE_COORD_3
GL_MAP2_TEXTURE_COORD_4
GL_MAP2_VERTEX_3
GL_MAP2_VERTEX_4
GL_MAP1_GRID_DOMAIN
GL_MAP1_GRID_SEGMENTS
GL_MAP2_GRID_DOMAIN
GL_MAP2_GRID_SEGMENTS
GL_TEXTURE_1D
GL_TEXTURE_2D
GL_FEEDBACK_BUFFER_POINTER
GL_FEEDBACK_BUFFER_SIZE
GL_FEEDBACK_BUFFER_TYPE
GL_SELECTION_BUFFER_POINTER
GL_SELECTION_BUFFER_SIZE
GL_TEXTURE_WIDTH
GL_TEXTURE_HEIGHT
GL_TEXTURE_INTERNAL_FORMAT
GL_TEXTURE_BORDER_COLOR
GL_TEXTURE_BORDER
GL_DONT_CARE
GL_FASTEST
GL_NICEST
GL_LIGHT0
GL_LIGHT1
GL_LIGHT2
GL_LIGHT3
GL_LIGHT4
GL_LIGHT5
GL_LIGHT6
GL_LIGHT7
GL_AMBIENT
GL_DIFFUSE
GL_SPECULAR
GL_POSITION
GL_SPOT_DIRECTION
GL_SPOT_EXPONENT
GL_SPOT_CUTOFF
GL_CONSTANT_ATTENUATION
GL_LINEAR_ATTENUATION
GL_QUADRATIC_ATTENUATION
GL_COMPILE
GL_COMPILE_AND_EXECUTE
GL_CLEAR
GL_AND
GL_AND_REVERSE
GL_COPY
GL_AND_INVERTED
GL_NOOP
GL_XOR
GL_OR
GL_NOR
GL_EQUIV
GL_INVERT
GL_OR_REVERSE
GL_COPY_INVERTED
GL_OR_INVERTED
GL_NAND
GL_SET
GL_EMISSION
GL_SHININESS
GL_AMBIENT_AND_DIFFUSE
GL_COLOR_INDEXES
GL_MODELVIEW
GL_PROJECTION
GL_TEXTURE
GL_COLOR
GL_DEPTH
GL_STENCIL
GL_COLOR_INDEX
GL_STENCIL_INDEX
GL_DEPTH_COMPONENT
GL_RED
GL_GREEN
GL_BLUE
GL_ALPHA
GL_RGB
GL_RGBA
GL_LUMINANCE
GL_LUMINANCE_ALPHA
GL_BITMAP
GL_POINT
GL_LINE
GL_FILL
GL_RENDER
GL_FEEDBACK
GL_SELECT
GL_FLAT
GL_SMOOTH
GL_KEEP
GL_REPLACE
GL_INCR
GL_DECR
GL_VENDOR
GL_RENDERER
GL_VERSION
GL_EXTENSIONS
GL_S
GL_T
GL_R
GL_Q
GL_MODULATE
GL_DECAL
GL_TEXTURE_ENV_MODE
GL_TEXTURE_ENV_COLOR
GL_TEXTURE_ENV
GL_EYE_LINEAR
GL_OBJECT_LINEAR
GL_SPHERE_MAP
GL_TEXTURE_GEN_MODE
GL_OBJECT_PLANE
GL_EYE_PLANE
GL_NEAREST
GL_LINEAR
GL_NEAREST_MIPMAP_NEAREST
GL_LINEAR_MIPMAP_NEAREST
GL_NEAREST_MIPMAP_LINEAR
GL_LINEAR_MIPMAP_LINEAR
GL_TEXTURE_MAG_FILTER
GL_TEXTURE_MIN_FILTER
GL_TEXTURE_WRAP_S
GL_TEXTURE_WRAP_T
GL_CLAMP
GL_REPEAT
GL_CLIENT_PIXEL_STORE_BIT
GL_CLIENT_VERTEX_ARRAY_BIT
GL_CLIENT_ALL_ATTRIB_BITS
GL_POLYGON_OFFSET_FACTOR
GL_POLYGON_OFFSET_UNITS
GL_POLYGON_OFFSET_POINT
GL_POLYGON_OFFSET_LINE
GL_POLYGON_OFFSET_FILL
GL_ALPHA4
GL_ALPHA8
GL_ALPHA12
GL_ALPHA16
GL_LUMINANCE4
GL_LUMINANCE8
GL_LUMINANCE12
GL_LUMINANCE16
GL_LUMINANCE4_ALPHA4
GL_LUMINANCE6_ALPHA2
GL_LUMINANCE8_ALPHA8
GL_LUMINANCE12_ALPHA4
GL_LUMINANCE12_ALPHA12
GL_LUMINANCE16_ALPHA16
GL_INTENSITY
GL_INTENSITY4
GL_INTENSITY8
GL_INTENSITY12
GL_INTENSITY16
GL_R3_G3_B2
GL_RGB4
GL_RGB5
GL_RGB8
GL_RGB10
GL_RGB12
GL_RGB16
GL_RGBA2
GL_RGBA4
GL_RGB5_A1
GL_RGBA8
GL_RGB10_A2
GL_RGBA12
GL_RGBA16
GL_TEXTURE_RED_SIZE
GL_TEXTURE_GREEN_SIZE
GL_TEXTURE_BLUE_SIZE
GL_TEXTURE_ALPHA_SIZE
GL_TEXTURE_LUMINANCE_SIZE
GL_TEXTURE_INTENSITY_SIZE
GL_PROXY_TEXTURE_1D
GL_PROXY_TEXTURE_2D
GL_TEXTURE_PRIORITY
GL_TEXTURE_RESIDENT
GL_TEXTURE_BINDING_1D
GL_TEXTURE_BINDING_2D
GL_VERTEX_ARRAY
GL_NORMAL_ARRAY
GL_COLOR_ARRAY
GL_INDEX_ARRAY
GL_TEXTURE_COORD_ARRAY
GL_EDGE_FLAG_ARRAY
GL_VERTEX_ARRAY_SIZE
GL_VERTEX_ARRAY_TYPE
GL_VERTEX_ARRAY_STRIDE
GL_NORMAL_ARRAY_TYPE
GL_NORMAL_ARRAY_STRIDE
GL_COLOR_ARRAY_SIZE
GL_COLOR_ARRAY_TYPE
GL_COLOR_ARRAY_STRIDE
GL_INDEX_ARRAY_TYPE
GL_INDEX_ARRAY_STRIDE
GL_TEXTURE_COORD_ARRAY_SIZE
GL_TEXTURE_COORD_ARRAY_TYPE
GL_TEXTURE_COORD_ARRAY_STRIDE
GL_EDGE_FLAG_ARRAY_STRIDE
GL_VERTEX_ARRAY_POINTER
GL_NORMAL_ARRAY_POINTER
GL_COLOR_ARRAY_POINTER
GL_INDEX_ARRAY_POINTER
GL_TEXTURE_COORD_ARRAY_POINTER
GL_EDGE_FLAG_ARRAY_POINTER
GL_V2F
GL_V3F
GL_C4UB_V2F
GL_C4UB_V3F
GL_C3F_V3F
GL_N3F_V3F
GL_C4F_N3F_V3F
GL_T2F_V3F
GL_T4F_V4F
GL_T2F_C4UB_V3F
GL_T2F_C3F_V3F
GL_T2F_N3F_V3F
GL_T2F_C4F_N3F_V3F
GL_T4F_C4F_N3F_V4F
GL_EXT_vertex_array
GL_EXT_bgra
GL_EXT_paletted_texture
GL_WIN_swap_hint
GL_WIN_draw_range_elements
GL_VERTEX_ARRAY_EXT
GL_NORMAL_ARRAY_EXT
GL_COLOR_ARRAY_EXT
GL_INDEX_ARRAY_EXT
GL_TEXTURE_COORD_ARRAY_EXT
GL_EDGE_FLAG_ARRAY_EXT
GL_VERTEX_ARRAY_SIZE_EXT
GL_VERTEX_ARRAY_TYPE_EXT
GL_VERTEX_ARRAY_STRIDE_EXT
GL_VERTEX_ARRAY_COUNT_EXT
GL_NORMAL_ARRAY_TYPE_EXT
GL_NORMAL_ARRAY_STRIDE_EXT
GL_NORMAL_ARRAY_COUNT_EXT
GL_COLOR_ARRAY_SIZE_EXT
GL_COLOR_ARRAY_TYPE_EXT
GL_COLOR_ARRAY_STRIDE_EXT
GL_COLOR_ARRAY_COUNT_EXT
GL_INDEX_ARRAY_TYPE_EXT
GL_INDEX_ARRAY_STRIDE_EXT
GL_INDEX_ARRAY_COUNT_EXT
GL_TEXTURE_COORD_ARRAY_SIZE_EXT
GL_TEXTURE_COORD_ARRAY_TYPE_EXT
GL_TEXTURE_COORD_ARRAY_STRIDE_EXT
GL_TEXTURE_COORD_ARRAY_COUNT_EXT
GL_EDGE_FLAG_ARRAY_STRIDE_EXT
GL_EDGE_FLAG_ARRAY_COUNT_EXT
GL_VERTEX_ARRAY_POINTER_EXT
GL_NORMAL_ARRAY_POINTER_EXT
GL_COLOR_ARRAY_POINTER_EXT
GL_INDEX_ARRAY_POINTER_EXT
GL_TEXTURE_COORD_ARRAY_POINTER_EXT
GL_EDGE_FLAG_ARRAY_POINTER_EXT
GL_DOUBLE_EXT
GL_BGR_EXT
GL_BGRA_EXT
GL_COLOR_TABLE_FORMAT_EXT
GL_COLOR_TABLE_WIDTH_EXT
GL_COLOR_TABLE_RED_SIZE_EXT
GL_COLOR_TABLE_GREEN_SIZE_EXT
GL_COLOR_TABLE_BLUE_SIZE_EXT
GL_COLOR_TABLE_ALPHA_SIZE_EXT
GL_COLOR_TABLE_LUMINANCE_SIZE_EXT
GL_COLOR_TABLE_INTENSITY_SIZE_EXT
GL_COLOR_INDEX1_EXT
GL_COLOR_INDEX2_EXT
GL_COLOR_INDEX4_EXT
GL_COLOR_INDEX8_EXT
GL_COLOR_INDEX12_EXT
GL_COLOR_INDEX16_EXT
GL_MAX_ELEMENTS_VERTICES_WIN
GL_MAX_ELEMENTS_INDICES_WIN
GL_PHONG_WIN
GL_PHONG_HINT_WIN
GL_FOG_SPECULAR_TEXTURE_WIN
GL_LOGIC_OP
GL_TEXTURE_COMPONENTS
GLU_VERSION_1_1
GLU_VERSION_1_2
GLU_INVALID_ENUM
GLU_INVALID_VALUE
GLU_OUT_OF_MEMORY
GLU_INCOMPATIBLE_GL_VERSION
GLU_VERSION
GLU_EXTENSIONS
GLU_TRUE
GLU_FALSE
GLU_SMOOTH
GLU_FLAT
GLU_NONE
GLU_POINT
GLU_LINE
GLU_FILL
GLU_SILHOUETTE
GLU_OUTSIDE
GLU_INSIDE
GLU_TESS_MAX_COORD
GLU_TESS_WINDING_RULE
GLU_TESS_BOUNDARY_ONLY
GLU_TESS_TOLERANCE
GLU_TESS_WINDING_ODD
GLU_TESS_WINDING_NONZERO
GLU_TESS_WINDING_POSITIVE
GLU_TESS_WINDING_NEGATIVE
GLU_TESS_WINDING_ABS_GEQ_TWO
GLU_TESS_BEGIN
GLU_TESS_VERTEX
GLU_TESS_END
GLU_TESS_ERROR
GLU_TESS_EDGE_FLAG
GLU_TESS_COMBINE
GLU_TESS_BEGIN_DATA
GLU_TESS_VERTEX_DATA
GLU_TESS_END_DATA
GLU_TESS_ERROR_DATA
GLU_TESS_EDGE_FLAG_DATA
GLU_TESS_COMBINE_DATA
GLU_TESS_ERROR1
GLU_TESS_ERROR2
GLU_TESS_ERROR3
GLU_TESS_ERROR4
GLU_TESS_ERROR5
GLU_TESS_ERROR6
GLU_TESS_ERROR7
GLU_TESS_ERROR8
GLU_TESS_MISSING_BEGIN_POLYGON
GLU_TESS_MISSING_BEGIN_CONTOUR
GLU_TESS_MISSING_END_POLYGON
GLU_TESS_MISSING_END_CONTOUR
GLU_TESS_COORD_TOO_LARGE
GLU_TESS_NEED_COMBINE_CALLBACK
GLU_AUTO_LOAD_MATRIX
GLU_CULLING
GLU_SAMPLING_TOLERANCE
GLU_DISPLAY_MODE
GLU_PARAMETRIC_TOLERANCE
GLU_SAMPLING_METHOD
GLU_U_STEP
GLU_V_STEP
GLU_PATH_LENGTH
GLU_PARAMETRIC_ERROR
GLU_DOMAIN_DISTANCE
GLU_MAP1_TRIM_2
GLU_MAP1_TRIM_3
GLU_OUTLINE_POLYGON
GLU_OUTLINE_PATCH
GLU_NURBS_ERROR1
GLU_NURBS_ERROR2
GLU_NURBS_ERROR3
GLU_NURBS_ERROR4
GLU_NURBS_ERROR5
GLU_NURBS_ERROR6
GLU_NURBS_ERROR7
GLU_NURBS_ERROR8
GLU_NURBS_ERROR9
GLU_NURBS_ERROR10
GLU_NURBS_ERROR11
GLU_NURBS_ERROR12
GLU_NURBS_ERROR13
GLU_NURBS_ERROR14
GLU_NURBS_ERROR15
GLU_NURBS_ERROR16
GLU_NURBS_ERROR17
GLU_NURBS_ERROR18
GLU_NURBS_ERROR19
GLU_NURBS_ERROR20
GLU_NURBS_ERROR21
GLU_NURBS_ERROR22
GLU_NURBS_ERROR23
GLU_NURBS_ERROR24
GLU_NURBS_ERROR25
GLU_NURBS_ERROR26
GLU_NURBS_ERROR27
GLU_NURBS_ERROR28
GLU_NURBS_ERROR29
GLU_NURBS_ERROR30
GLU_NURBS_ERROR31
GLU_NURBS_ERROR32
GLU_NURBS_ERROR33
GLU_NURBS_ERROR34
GLU_NURBS_ERROR35
GLU_NURBS_ERROR36
GLU_NURBS_ERROR37
GLU_CW
GLU_CCW
GLU_INTERIOR
GLU_EXTERIOR
GLU_UNKNOWN
GLU_BEGIN
GLU_VERTEX
GLU_END
GLU_ERROR
GLU_EDGE_FLAG


================================================
FILE: src/ConstList.works_for_me
================================================
GL_VERSION_1_1
GL_VERSION_1_2
GL_VERSION_1_3
GL_ARB_imaging
GL_FALSE
GL_TRUE
GL_2_BYTES
GL_3_BYTES
GL_4_BYTES
GL_POINTS
GL_LINES
GL_LINE_LOOP
GL_LINE_STRIP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
GL_QUADS
GL_QUAD_STRIP
GL_POLYGON
GL_VERTEX_ARRAY
GL_NORMAL_ARRAY
GL_COLOR_ARRAY
GL_INDEX_ARRAY
GL_TEXTURE_COORD_ARRAY
GL_EDGE_FLAG_ARRAY
GL_VERTEX_ARRAY_SIZE
GL_VERTEX_ARRAY_TYPE
GL_VERTEX_ARRAY_STRIDE
GL_NORMAL_ARRAY_TYPE
GL_NORMAL_ARRAY_STRIDE
GL_COLOR_ARRAY_SIZE
GL_COLOR_ARRAY_TYPE
GL_COLOR_ARRAY_STRIDE
GL_INDEX_ARRAY_TYPE
GL_INDEX_ARRAY_STRIDE
GL_TEXTURE_COORD_ARRAY_SIZE
GL_TEXTURE_COORD_ARRAY_TYPE
GL_TEXTURE_COORD_ARRAY_STRIDE
GL_EDGE_FLAG_ARRAY_STRIDE
GL_VERTEX_ARRAY_POINTER
GL_NORMAL_ARRAY_POINTER
GL_COLOR_ARRAY_POINTER
GL_INDEX_ARRAY_POINTER
GL_TEXTURE_COORD_ARRAY_POINTER
GL_EDGE_FLAG_ARRAY_POINTER
GL_V2F
GL_V3F
GL_C4UB_V2F
GL_C4UB_V3F
GL_C3F_V3F
GL_N3F_V3F
GL_C4F_N3F_V3F
GL_T2F_V3F
GL_T4F_V4F
GL_T2F_C4UB_V3F
GL_T2F_C3F_V3F
GL_T2F_N3F_V3F
GL_T2F_C4F_N3F_V3F
GL_T4F_C4F_N3F_V4F
GL_MATRIX_MODE
GL_MODELVIEW
GL_PROJECTION
GL_TEXTURE
GL_POINT_SMOOTH
GL_POINT_SIZE
GL_POINT_SIZE_GRANULARITY
GL_POINT_SIZE_RANGE
GL_LINE_SMOOTH
GL_LINE_STIPPLE
GL_LINE_STIPPLE_PATTERN
GL_LINE_STIPPLE_REPEAT
GL_LINE_WIDTH
GL_LINE_WIDTH_GRANULARITY
GL_LINE_WIDTH_RANGE
GL_POINT
GL_LINE
GL_FILL
GL_CW
GL_CCW
GL_FRONT
GL_BACK
GL_POLYGON_MODE
GL_POLYGON_SMOOTH
GL_POLYGON_STIPPLE
GL_EDGE_FLAG
GL_CULL_FACE
GL_CULL_FACE_MODE
GL_FRONT_FACE
GL_POLYGON_OFFSET_FACTOR
GL_POLYGON_OFFSET_UNITS
GL_POLYGON_OFFSET_POINT
GL_POLYGON_OFFSET_LINE
GL_POLYGON_OFFSET_FILL
GL_COMPILE
GL_COMPILE_AND_EXECUTE
GL_LIST_BASE
GL_LIST_INDEX
GL_LIST_MODE
GL_NEVER
GL_LESS
GL_EQUAL
GL_LEQUAL
GL_GREATER
GL_NOTEQUAL
GL_GEQUAL
GL_ALWAYS
GL_DEPTH_TEST
GL_DEPTH_BITS
GL_DEPTH_CLEAR_VALUE
GL_DEPTH_FUNC
GL_DEPTH_RANGE
GL_DEPTH_WRITEMASK
GL_DEPTH_COMPONENT
GL_LIGHTING
GL_LIGHT0
GL_LIGHT1
GL_LIGHT2
GL_LIGHT3
GL_LIGHT4
GL_LIGHT5
GL_LIGHT6
GL_LIGHT7
GL_SPOT_EXPONENT
GL_SPOT_CUTOFF
GL_CONSTANT_ATTENUATION
GL_LINEAR_ATTENUATION
GL_QUADRATIC_ATTENUATION
GL_AMBIENT
GL_DIFFUSE
GL_SPECULAR
GL_SHININESS
GL_EMISSION
GL_POSITION
GL_SPOT_DIRECTION
GL_AMBIENT_AND_DIFFUSE
GL_COLOR_INDEXES
GL_LIGHT_MODEL_TWO_SIDE
GL_LIGHT_MODEL_LOCAL_VIEWER
GL_LIGHT_MODEL_AMBIENT
GL_FRONT_AND_BACK
GL_SHADE_MODEL
GL_FLAT
GL_SMOOTH
GL_COLOR_MATERIAL
GL_COLOR_MATERIAL_FACE
GL_COLOR_MATERIAL_PARAMETER
GL_NORMALIZE
GL_CLIP_PLANE0
GL_CLIP_PLANE1
GL_CLIP_PLANE2
GL_CLIP_PLANE3
GL_CLIP_PLANE4
GL_CLIP_PLANE5
GL_ACCUM_RED_BITS
GL_ACCUM_GREEN_BITS
GL_ACCUM_BLUE_BITS
GL_ACCUM_ALPHA_BITS
GL_ACCUM_CLEAR_VALUE
GL_ACCUM
GL_ADD
GL_LOAD
GL_MULT
GL_RETURN
GL_ALPHA_TEST
GL_ALPHA_TEST_REF
GL_ALPHA_TEST_FUNC
GL_BLEND
GL_BLEND_SRC
GL_BLEND_DST
GL_ZERO
GL_ONE
GL_SRC_COLOR
GL_ONE_MINUS_SRC_COLOR
GL_SRC_ALPHA
GL_ONE_MINUS_SRC_ALPHA
GL_DST_ALPHA
GL_ONE_MINUS_DST_ALPHA
GL_DST_COLOR
GL_ONE_MINUS_DST_COLOR
GL_SRC_ALPHA_SATURATE
GL_FEEDBACK
GL_RENDER
GL_SELECT
GL_2D
GL_3D
GL_3D_COLOR
GL_3D_COLOR_TEXTURE
GL_4D_COLOR_TEXTURE
GL_POINT_TOKEN
GL_LINE_TOKEN
GL_LINE_RESET_TOKEN
GL_POLYGON_TOKEN
GL_BITMAP_TOKEN
GL_DRAW_PIXEL_TOKEN
GL_COPY_PIXEL_TOKEN
GL_PASS_THROUGH_TOKEN
GL_FEEDBACK_BUFFER_POINTER
GL_FEEDBACK_BUFFER_SIZE
GL_FEEDBACK_BUFFER_TYPE
GL_SELECTION_BUFFER_POINTER
GL_SELECTION_BUFFER_SIZE
GL_FOG
GL_FOG_MODE
GL_FOG_DENSITY
GL_FOG_COLOR
GL_FOG_INDEX
GL_FOG_START
GL_FOG_END
GL_LINEAR
GL_EXP
GL_EXP2
GL_LOGIC_OP
GL_INDEX_LOGIC_OP
GL_COLOR_LOGIC_OP
GL_LOGIC_OP_MODE
GL_CLEAR
GL_SET
GL_COPY
GL_COPY_INVERTED
GL_NOOP
GL_INVERT
GL_AND
GL_NAND
GL_OR
GL_NOR
GL_XOR
GL_EQUIV
GL_AND_REVERSE
GL_AND_INVERTED
GL_OR_REVERSE
GL_OR_INVERTED
GL_STENCIL_TEST
GL_STENCIL_WRITEMASK
GL_STENCIL_BITS
GL_STENCIL_FUNC
GL_STENCIL_VALUE_MASK
GL_STENCIL_REF
GL_STENCIL_FAIL
GL_STENCIL_PASS_DEPTH_PASS
GL_STENCIL_PASS_DEPTH_FAIL
GL_STENCIL_CLEAR_VALUE
GL_STENCIL_INDEX
GL_KEEP
GL_REPLACE
GL_INCR
GL_DECR
GL_NONE
GL_LEFT
GL_RIGHT
GL_FRONT_LEFT
GL_FRONT_RIGHT
GL_BACK_LEFT
GL_BACK_RIGHT
GL_AUX0
GL_AUX1
GL_AUX2
GL_AUX3
GL_COLOR_INDEX
GL_RED
GL_GREEN
GL_BLUE
GL_ALPHA
GL_LUMINANCE
GL_LUMINANCE_ALPHA
GL_ALPHA_BITS
GL_RED_BITS
GL_GREEN_BITS
GL_BLUE_BITS
GL_INDEX_BITS
GL_SUBPIXEL_BITS
GL_AUX_BUFFERS
GL_READ_BUFFER
GL_DRAW_BUFFER
GL_DOUBLEBUFFER
GL_STEREO
GL_BITMAP
GL_COLOR
GL_DEPTH
GL_STENCIL
GL_DITHER
GL_RGB
GL_RGBA
GL_MAX_LIST_NESTING
GL_MAX_ATTRIB_STACK_DEPTH
GL_MAX_MODELVIEW_STACK_DEPTH
GL_MAX_NAME_STACK_DEPTH
GL_MAX_PROJECTION_STACK_DEPTH
GL_MAX_TEXTURE_STACK_DEPTH
GL_MAX_EVAL_ORDER
GL_MAX_LIGHTS
GL_MAX_CLIP_PLANES
GL_MAX_TEXTURE_SIZE
GL_MAX_PIXEL_MAP_TABLE
GL_MAX_VIEWPORT_DIMS
GL_MAX_CLIENT_ATTRIB_STACK_DEPTH
GL_ATTRIB_STACK_DEPTH
GL_CLIENT_ATTRIB_STACK_DEPTH
GL_COLOR_CLEAR_VALUE
GL_COLOR_WRITEMASK
GL_CURRENT_INDEX
GL_CURRENT_COLOR
GL_CURRENT_NORMAL
GL_CURRENT_RASTER_COLOR
GL_CURRENT_RASTER_DISTANCE
GL_CURRENT_RASTER_INDEX
GL_CURRENT_RASTER_POSITION
GL_CURRENT_RASTER_TEXTURE_COORDS
GL_CURRENT_RASTER_POSITION_VALID
GL_CURRENT_TEXTURE_COORDS
GL_INDEX_CLEAR_VALUE
GL_INDEX_MODE
GL_INDEX_WRITEMASK
GL_MODELVIEW_MATRIX
GL_MODELVIEW_STACK_DEPTH
GL_NAME_STACK_DEPTH
GL_PROJECTION_MATRIX
GL_PROJECTION_STACK_DEPTH
GL_RENDER_MODE
GL_RGBA_MODE
GL_TEXTURE_MATRIX
GL_TEXTURE_STACK_DEPTH
GL_VIEWPORT
GL_AUTO_NORMAL
GL_MAP1_COLOR_4
GL_MAP1_INDEX
GL_MAP1_NORMAL
GL_MAP1_TEXTURE_COORD_1
GL_MAP1_TEXTURE_COORD_2
GL_MAP1_TEXTURE_COORD_3
GL_MAP1_TEXTURE_COORD_4
GL_MAP1_VERTEX_3
GL_MAP1_VERTEX_4
GL_MAP2_COLOR_4
GL_MAP2_INDEX
GL_MAP2_NORMAL
GL_MAP2_TEXTURE_COORD_1
GL_MAP2_TEXTURE_COORD_2
GL_MAP2_TEXTURE_COORD_3
GL_MAP2_TEXTURE_COORD_4
GL_MAP2_VERTEX_3
GL_MAP2_VERTEX_4
GL_MAP1_GRID_DOMAIN
GL_MAP1_GRID_SEGMENTS
GL_MAP2_GRID_DOMAIN
GL_MAP2_GRID_SEGMENTS
GL_COEFF
GL_DOMAIN
GL_ORDER
GL_FOG_HINT
GL_LINE_SMOOTH_HINT
GL_PERSPECTIVE_CORRECTION_HINT
GL_POINT_SMOOTH_HINT
GL_POLYGON_SMOOTH_HINT
GL_DONT_CARE
GL_FASTEST
GL_NICEST
GL_SCISSOR_TEST
GL_SCISSOR_BOX
GL_MAP_COLOR
GL_MAP_STENCIL
GL_INDEX_SHIFT
GL_INDEX_OFFSET
GL_RED_SCALE
GL_RED_BIAS
GL_GREEN_SCALE
GL_GREEN_BIAS
GL_BLUE_SCALE
GL_BLUE_BIAS
GL_ALPHA_SCALE
GL_ALPHA_BIAS
GL_DEPTH_SCALE
GL_DEPTH_BIAS
GL_PIXEL_MAP_S_TO_S_SIZE
GL_PIXEL_MAP_I_TO_I_SIZE
GL_PIXEL_MAP_I_TO_R_SIZE
GL_PIXEL_MAP_I_TO_G_SIZE
GL_PIXEL_MAP_I_TO_B_SIZE
GL_PIXEL_MAP_I_TO_A_SIZE
GL_PIXEL_MAP_R_TO_R_SIZE
GL_PIXEL_MAP_G_TO_G_SIZE
GL_PIXEL_MAP_B_TO_B_SIZE
GL_PIXEL_MAP_A_TO_A_SIZE
GL_PIXEL_MAP_S_TO_S
GL_PIXEL_MAP_I_TO_I
GL_PIXEL_MAP_I_TO_R
GL_PIXEL_MAP_I_TO_G
GL_PIXEL_MAP_I_TO_B
GL_PIXEL_MAP_I_TO_A
GL_PIXEL_MAP_R_TO_R
GL_PIXEL_MAP_G_TO_G
GL_PIXEL_MAP_B_TO_B
GL_PIXEL_MAP_A_TO_A
GL_PACK_ALIGNMENT
GL_PACK_LSB_FIRST
GL_PACK_ROW_LENGTH
GL_PACK_SKIP_PIXELS
GL_PACK_SKIP_ROWS
GL_PACK_SWAP_BYTES
GL_UNPACK_ALIGNMENT
GL_UNPACK_LSB_FIRST
GL_UNPACK_ROW_LENGTH
GL_UNPACK_SKIP_PIXELS
GL_UNPACK_SKIP_ROWS
GL_UNPACK_SWAP_BYTES
GL_ZOOM_X
GL_ZOOM_Y
GL_TEXTURE_ENV
GL_TEXTURE_ENV_MODE
GL_TEXTURE_1D
GL_TEXTURE_2D
GL_TEXTURE_WRAP_S
GL_TEXTURE_WRAP_T
GL_TEXTURE_MAG_FILTER
GL_TEXTURE_MIN_FILTER
GL_TEXTURE_ENV_COLOR
GL_TEXTURE_GEN_S
GL_TEXTURE_GEN_T
GL_TEXTURE_GEN_MODE
GL_TEXTURE_BORDER_COLOR
GL_TEXTURE_WIDTH
GL_TEXTURE_HEIGHT
GL_TEXTURE_BORDER
GL_TEXTURE_COMPONENTS
GL_TEXTURE_RED_SIZE
GL_TEXTURE_GREEN_SIZE
GL_TEXTURE_BLUE_SIZE
GL_TEXTURE_ALPHA_SIZE
GL_TEXTURE_LUMINANCE_SIZE
GL_TEXTURE_INTENSITY_SIZE
GL_NEAREST_MIPMAP_NEAREST
GL_NEAREST_MIPMAP_LINEAR
GL_LINEAR_MIPMAP_NEAREST
GL_LINEAR_MIPMAP_LINEAR
GL_OBJECT_LINEAR
GL_OBJECT_PLANE
GL_EYE_LINEAR
GL_EYE_PLANE
GL_SPHERE_MAP
GL_DECAL
GL_MODULATE
GL_NEAREST
GL_REPEAT
GL_CLAMP
GL_S
GL_T
GL_R
GL_Q
GL_TEXTURE_GEN_R
GL_TEXTURE_GEN_Q
GL_VENDOR
GL_RENDERER
GL_VERSION
GL_EXTENSIONS
GL_NO_ERROR
GL_INVALID_VALUE
GL_INVALID_ENUM
GL_INVALID_OPERATION
GL_STACK_OVERFLOW
GL_STACK_UNDERFLOW
GL_OUT_OF_MEMORY
GL_CURRENT_BIT
GL_POINT_BIT
GL_LINE_BIT
GL_POLYGON_BIT
GL_POLYGON_STIPPLE_BIT
GL_PIXEL_MODE_BIT
GL_LIGHTING_BIT
GL_FOG_BIT
GL_DEPTH_BUFFER_BIT
GL_ACCUM_BUFFER_BIT
GL_STENCIL_BUFFER_BIT
GL_VIEWPORT_BIT
GL_TRANSFORM_BIT
GL_ENABLE_BIT
GL_COLOR_BUFFER_BIT
GL_HINT_BIT
GL_EVAL_BIT
GL_LIST_BIT
GL_TEXTURE_BIT
GL_SCISSOR_BIT
GL_ALL_ATTRIB_BITS
GL_PROXY_TEXTURE_1D
GL_PROXY_TEXTURE_2D
GL_TEXTURE_PRIORITY
GL_TEXTURE_RESIDENT
GL_TEXTURE_BINDING_1D
GL_TEXTURE_BINDING_2D
GL_TEXTURE_INTERNAL_FORMAT
GL_ALPHA4
GL_ALPHA8
GL_ALPHA12
GL_ALPHA16
GL_LUMINANCE4
GL_LUMINANCE8
GL_LUMINANCE12
GL_LUMINANCE16
GL_LUMINANCE4_ALPHA4
GL_LUMINANCE6_ALPHA2
GL_LUMINANCE8_ALPHA8
GL_LUMINANCE12_ALPHA4
GL_LUMINANCE12_ALPHA12
GL_LUMINANCE16_ALPHA16
GL_INTENSITY
GL_INTENSITY4
GL_INTENSITY8
GL_INTENSITY12
GL_INTENSITY16
GL_R3_G3_B2
GL_RGB4
GL_RGB5
GL_RGB8
GL_RGB10
GL_RGB12
GL_RGB16
GL_RGBA2
GL_RGBA4
GL_RGB5_A1
GL_RGBA8
GL_RGB10_A2
GL_RGBA12
GL_RGBA16
GL_CLIENT_PIXEL_STORE_BIT
GL_CLIENT_VERTEX_ARRAY_BIT
GL_ALL_CLIENT_ATTRIB_BITS
GL_CLIENT_ALL_ATTRIB_BITS
GL_RESCALE_NORMAL
GL_CLAMP_TO_EDGE
GL_MAX_ELEMENTS_VERTICES
GL_MAX_ELEMENTS_INDICES
GL_BGR
GL_BGRA
GL_UNSIGNED_BYTE_3_3_2
GL_UNSIGNED_BYTE_2_3_3_REV
GL_UNSIGNED_SHORT_5_6_5
GL_UNSIGNED_SHORT_5_6_5_REV
GL_UNSIGNED_SHORT_4_4_4_4
GL_UNSIGNED_SHORT_4_4_4_4_REV
GL_UNSIGNED_SHORT_5_5_5_1
GL_UNSIGNED_SHORT_1_5_5_5_REV
GL_UNSIGNED_INT_8_8_8_8
GL_UNSIGNED_INT_8_8_8_8_REV
GL_UNSIGNED_INT_10_10_10_2
GL_UNSIGNED_INT_2_10_10_10_REV
GL_LIGHT_MODEL_COLOR_CONTROL
GL_SINGLE_COLOR
GL_SEPARATE_SPECULAR_COLOR
GL_TEXTURE_MIN_LOD
GL_TEXTURE_MAX_LOD
GL_TEXTURE_BASE_LEVEL
GL_TEXTURE_MAX_LEVEL
GL_SMOOTH_POINT_SIZE_RANGE
GL_SMOOTH_POINT_SIZE_GRANULARITY
GL_SMOOTH_LINE_WIDTH_RANGE
GL_SMOOTH_LINE_WIDTH_GRANULARITY
GL_ALIASED_POINT_SIZE_RANGE
GL_ALIASED_LINE_WIDTH_RANGE
GL_PACK_SKIP_IMAGES
GL_PACK_IMAGE_HEIGHT
GL_UNPACK_SKIP_IMAGES
GL_UNPACK_IMAGE_HEIGHT
GL_TEXTURE_3D
GL_PROXY_TEXTURE_3D
GL_TEXTURE_DEPTH
GL_TEXTURE_WRAP_R
GL_MAX_3D_TEXTURE_SIZE
GL_TEXTURE_BINDING_3D
GL_CONSTANT_COLOR
GL_ONE_MINUS_CONSTANT_COLOR
GL_CONSTANT_ALPHA
GL_ONE_MINUS_CONSTANT_ALPHA
GL_COLOR_TABLE
GL_POST_CONVOLUTION_COLOR_TABLE
GL_POST_COLOR_MATRIX_COLOR_TABLE
GL_PROXY_COLOR_TABLE
GL_PROXY_POST_CONVOLUTION_COLOR_TABLE
GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE
GL_COLOR_TABLE_SCALE
GL_COLOR_TABLE_BIAS
GL_COLOR_TABLE_FORMAT
GL_COLOR_TABLE_WIDTH
GL_COLOR_TABLE_RED_SIZE
GL_COLOR_TABLE_GREEN_SIZE
GL_COLOR_TABLE_BLUE_SIZE
GL_COLOR_TABLE_ALPHA_SIZE
GL_COLOR_TABLE_LUMINANCE_SIZE
GL_COLOR_TABLE_INTENSITY_SIZE
GL_CONVOLUTION_1D
GL_CONVOLUTION_2D
GL_SEPARABLE_2D
GL_CONVOLUTION_BORDER_MODE
GL_CONVOLUTION_FILTER_SCALE
GL_CONVOLUTION_FILTER_BIAS
GL_REDUCE
GL_CONVOLUTION_FORMAT
GL_CONVOLUTION_WIDTH
GL_CONVOLUTION_HEIGHT
GL_MAX_CONVOLUTION_WIDTH
GL_MAX_CONVOLUTION_HEIGHT
GL_POST_CONVOLUTION_RED_SCALE
GL_POST_CONVOLUTION_GREEN_SCALE
GL_POST_CONVOLUTION_BLUE_SCALE
GL_POST_CONVOLUTION_ALPHA_SCALE
GL_POST_CONVOLUTION_RED_BIAS
GL_POST_CONVOLUTION_GREEN_BIAS
GL_POST_CONVOLUTION_BLUE_BIAS
GL_POST_CONVOLUTION_ALPHA_BIAS
GL_CONSTANT_BORDER
GL_REPLICATE_BORDER
GL_CONVOLUTION_BORDER_COLOR
GL_COLOR_MATRIX
GL_COLOR_MATRIX_STACK_DEPTH
GL_MAX_COLOR_MATRIX_STACK_DEPTH
GL_POST_COLOR_MATRIX_RED_SCALE
GL_POST_COLOR_MATRIX_GREEN_SCALE
GL_POST_COLOR_MATRIX_BLUE_SCALE
GL_POST_COLOR_MATRIX_ALPHA_SCALE
GL_POST_COLOR_MATRIX_RED_BIAS
GL_POST_COLOR_MATRIX_GREEN_BIAS
GL_POST_COLOR_MATRIX_BLUE_BIAS
GL_POST_COLOR_MATRIX_ALPHA_BIAS
GL_HISTOGRAM
GL_PROXY_HISTOGRAM
GL_HISTOGRAM_WIDTH
GL_HISTOGRAM_FORMAT
GL_HISTOGRAM_RED_SIZE
GL_HISTOGRAM_GREEN_SIZE
GL_HISTOGRAM_BLUE_SIZE
GL_HISTOGRAM_ALPHA_SIZE
GL_HISTOGRAM_LUMINANCE_SIZE
GL_HISTOGRAM_SINK
GL_MINMAX
GL_MINMAX_FORMAT
GL_MINMAX_SINK
GL_TABLE_TOO_LARGE
GL_BLEND_EQUATION
GL_MIN
GL_MAX
GL_FUNC_ADD
GL_FUNC_SUBTRACT
GL_FUNC_REVERSE_SUBTRACT
GL_BLEND_COLOR
GL_TEXTURE0
GL_TEXTURE1
GL_TEXTURE2
GL_TEXTURE3
GL_TEXTURE4
GL_TEXTURE5
GL_TEXTURE6
GL_TEXTURE7
GL_TEXTURE8
GL_TEXTURE9
GL_TEXTURE10
GL_TEXTURE11
GL_TEXTURE12
GL_TEXTURE13
GL_TEXTURE14
GL_TEXTURE15
GL_TEXTURE16
GL_TEXTURE17
GL_TEXTURE18
GL_TEXTURE19
GL_TEXTURE20
GL_TEXTURE21
GL_TEXTURE22
GL_TEXTURE23
GL_TEXTURE24
GL_TEXTURE25
GL_TEXTURE26
GL_TEXTURE27
GL_TEXTURE28
GL_TEXTURE29
GL_TEXTURE30
GL_TEXTURE31
GL_ACTIVE_TEXTURE
GL_CLIENT_ACTIVE_TEXTURE
GL_MAX_TEXTURE_UNITS
GL_NORMAL_MAP
GL_REFLECTION_MAP
GL_TEXTURE_CUBE_MAP
GL_TEXTURE_BINDING_CUBE_MAP
GL_TEXTURE_CUBE_MAP_POSITIVE_X
GL_TEXTURE_CUBE_MAP_NEGATIVE_X
GL_TEXTURE_CUBE_MAP_POSITIVE_Y
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
GL_TEXTURE_CUBE_MAP_POSITIVE_Z
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
GL_PROXY_TEXTURE_CUBE_MAP
GL_MAX_CUBE_MAP_TEXTURE_SIZE
GL_COMPRESSED_ALPHA
GL_COMPRESSED_LUMINANCE
GL_COMPRESSED_LUMINANCE_ALPHA
GL_COMPRESSED_INTENSITY
GL_COMPRESSED_RGB
GL_COMPRESSED_RGBA
GL_TEXTURE_COMPRESSION_HINT
GL_TEXTURE_COMPRESSED_IMAGE_SIZE
GL_TEXTURE_COMPRESSED
GL_NUM_COMPRESSED_TEXTURE_FORMATS
GL_COMPRESSED_TEXTURE_FORMATS
GL_MULTISAMPLE
GL_SAMPLE_ALPHA_TO_COVERAGE
GL_SAMPLE_ALPHA_TO_ONE
GL_SAMPLE_COVERAGE
GL_SAMPLE_BUFFERS
GL_SAMPLES
GL_SAMPLE_COVERAGE_VALUE
GL_SAMPLE_COVERAGE_INVERT
GL_MULTISAMPLE_BIT
GL_TRANSPOSE_MODELVIEW_MATRIX
GL_TRANSPOSE_PROJECTION_MATRIX
GL_TRANSPOSE_TEXTURE_MATRIX
GL_TRANSPOSE_COLOR_MATRIX
GL_COMBINE
GL_COMBINE_RGB
GL_COMBINE_ALPHA
GL_SOURCE0_RGB
GL_SOURCE1_RGB
GL_SOURCE2_RGB
GL_SOURCE0_ALPHA
GL_SOURCE1_ALPHA
GL_SOURCE2_ALPHA
GL_OPERAND0_RGB
GL_OPERAND1_RGB
GL_OPERAND2_RGB
GL_OPERAND0_ALPHA
GL_OPERAND1_ALPHA
GL_OPERAND2_ALPHA
GL_RGB_SCALE
GL_ADD_SIGNED
GL_INTERPOLATE
GL_SUBTRACT
GL_CONSTANT
GL_PRIMARY_COLOR
GL_PREVIOUS
GL_DOT3_RGB
GL_DOT3_RGBA
GL_CLAMP_TO_BORDER
GL_ARB_multitexture
GL_TEXTURE0_ARB
GL_TEXTURE1_ARB
GL_TEXTURE2_ARB
GL_TEXTURE3_ARB
GL_TEXTURE4_ARB
GL_TEXTURE5_ARB
GL_TEXTURE6_ARB
GL_TEXTURE7_ARB
GL_TEXTURE8_ARB
GL_TEXTURE9_ARB
GL_TEXTURE10_ARB
GL_TEXTURE11_ARB
GL_TEXTURE12_ARB
GL_TEXTURE13_ARB
GL_TEXTURE14_ARB
GL_TEXTURE15_ARB
GL_TEXTURE16_ARB
GL_TEXTURE17_ARB
GL_TEXTURE18_ARB
GL_TEXTURE19_ARB
GL_TEXTURE20_ARB
GL_TEXTURE21_ARB
GL_TEXTURE22_ARB
GL_TEXTURE23_ARB
GL_TEXTURE24_ARB
GL_TEXTURE25_ARB
GL_TEXTURE26_ARB
GL_TEXTURE27_ARB
GL_TEXTURE28_ARB
GL_TEXTURE29_ARB
GL_TEXTURE30_ARB
GL_TEXTURE31_ARB
GL_ACTIVE_TEXTURE_ARB
GL_CLIENT_ACTIVE_TEXTURE_ARB
GL_MAX_TEXTURE_UNITS_ARB
GL_EXT_abgr
GL_ABGR_EXT
GL_EXT_blend_color
GL_CONSTANT_COLOR_EXT
GL_ONE_MINUS_CONSTANT_COLOR_EXT
GL_CONSTANT_ALPHA_EXT
GL_ONE_MINUS_CONSTANT_ALPHA_EXT
GL_BLEND_COLOR_EXT
GL_EXT_polygon_offset
GL_POLYGON_OFFSET_EXT
GL_POLYGON_OFFSET_FACTOR_EXT
GL_POLYGON_OFFSET_BIAS_EXT
GL_EXT_texture3D
GL_PACK_SKIP_IMAGES_EXT
GL_PACK_IMAGE_HEIGHT_EXT
GL_UNPACK_SKIP_IMAGES_EXT
GL_UNPACK_IMAGE_HEIGHT_EXT
GL_TEXTURE_3D_EXT
GL_PROXY_TEXTURE_3D_EXT
GL_TEXTURE_DEPTH_EXT
GL_TEXTURE_WRAP_R_EXT
GL_MAX_3D_TEXTURE_SIZE_EXT
GL_TEXTURE_3D_BINDING_EXT
GL_EXT_texture_object
GL_TEXTURE_PRIORITY_EXT
GL_TEXTURE_RESIDENT_EXT
GL_TEXTURE_1D_BINDING_EXT
GL_TEXTURE_2D_BINDING_EXT
GL_EXT_rescale_normal
GL_RESCALE_NORMAL_EXT
GL_EXT_vertex_array
GL_VERTEX_ARRAY_EXT
GL_NORMAL_ARRAY_EXT
GL_COLOR_ARRAY_EXT
GL_INDEX_ARRAY_EXT
GL_TEXTURE_COORD_ARRAY_EXT
GL_EDGE_FLAG_ARRAY_EXT
GL_VERTEX_ARRAY_SIZE_EXT
GL_VERTEX_ARRAY_TYPE_EXT
GL_VERTEX_ARRAY_STRIDE_EXT
GL_VERTEX_ARRAY_COUNT_EXT
GL_NORMAL_ARRAY_TYPE_EXT
GL_NORMAL_ARRAY_STRIDE_EXT
GL_NORMAL_ARRAY_COUNT_EXT
GL_COLOR_ARRAY_SIZE_EXT
GL_COLOR_ARRAY_TYPE_EXT
GL_COLOR_ARRAY_STRIDE_EXT
GL_COLOR_ARRAY_COUNT_EXT
GL_INDEX_ARRAY_TYPE_EXT
GL_INDEX_ARRAY_STRIDE_EXT
GL_INDEX_ARRAY_COUNT_EXT
GL_TEXTURE_COORD_ARRAY_SIZE_EXT
GL_TEXTURE_COORD_ARRAY_TYPE_EXT
GL_TEXTURE_COORD_ARRAY_STRIDE_EXT
GL_TEXTURE_COORD_ARRAY_COUNT_EXT
GL_EDGE_FLAG_ARRAY_STRIDE_EXT
GL_EDGE_FLAG_ARRAY_COUNT_EXT
GL_VERTEX_ARRAY_POINTER_EXT
GL_NORMAL_ARRAY_POINTER_EXT
GL_COLOR_ARRAY_POINTER_EXT
GL_INDEX_ARRAY_POINTER_EXT
GL_TEXTURE_COORD_ARRAY_POINTER_EXT
GL_EDGE_FLAG_ARRAY_POINTER_EXT
GL_SGIS_texture_edge_clamp
GL_CLAMP_TO_EDGE_SGIS
GL_EXT_blend_minmax
GL_FUNC_ADD_EXT
GL_MIN_EXT
GL_MAX_EXT
GL_BLEND_EQUATION_EXT
GL_EXT_blend_subtract
GL_FUNC_SUBTRACT_EXT
GL_FUNC_REVERSE_SUBTRACT_EXT
GL_EXT_blend_logic_op
GL_EXT_point_parameters
GL_POINT_SIZE_MIN_EXT
GL_POINT_SIZE_MAX_EXT
GL_POINT_FADE_THRESHOLD_SIZE_EXT
GL_DISTANCE_ATTENUATION_EXT
GL_EXT_paletted_texture
GL_TABLE_TOO_LARGE_EXT
GL_TEXTURE_INDEX_SIZE_EXT
GL_COLOR_INDEX1_EXT
GL_COLOR_INDEX2_EXT
GL_COLOR_INDEX4_EXT
GL_COLOR_INDEX8_EXT
GL_COLOR_INDEX12_EXT
GL_COLOR_INDEX16_EXT
GL_EXT_clip_volume_hint
GL_CLIP_VOLUME_CLIPPING_HINT_EXT
GL_EXT_compiled_vertex_array
GL_ARRAY_ELEMENT_LOCK_FIRST_EXT
GL_ARRAY_ELEMENT_LOCK_COUNT_EXT
GL_HP_occlusion_test
GL_OCCLUSION_TEST_HP
GL_OCCLUSION_TEST_RESULT_HP
GL_EXT_shared_texture_palette
GL_SHARED_TEXTURE_PALETTE_EXT
GL_EXT_stencil_wrap
GL_INCR_WRAP_EXT
GL_DECR_WRAP_EXT
GL_NV_texgen_reflection
GL_NORMAL_MAP_NV
GL_REFLECTION_MAP_NV
GL_EXT_texture_env_add
GL_MESA_window_pos
GL_MESA_resize_buffers
GL_EXT_texture_env_dot3
GL_DOT3_RGB_EXT
GL_DOT3_RGBA_EXT
GL_MESA_trace
GL_TRACE_ALL_BITS_MESA
GL_TRACE_OPERATIONS_BIT_MESA
GL_TRACE_PRIMITIVES_BIT_MESA
GL_TRACE_ARRAYS_BIT_MESA
GL_TRACE_TEXTURES_BIT_MESA
GL_TRACE_PIXELS_BIT_MESA
GL_TRACE_ERRORS_BIT_MESA
GL_TRACE_MASK_MESA
GL_TRACE_NAME_MESA
GL_MESA_packed_depth_stencil
GL_DEPTH_STENCIL_MESA
GL_UNSIGNED_INT_24_8_MESA
GL_UNSIGNED_INT_8_24_REV_MESA
GL_UNSIGNED_SHORT_15_1_MESA
GL_UNSIGNED_SHORT_1_15_REV_MESA
GL_MESA_ycbcr_texture
GL_YCBCR_MESA
GL_UNSIGNED_SHORT_8_8_MESA
GL_UNSIGNED_SHORT_8_8_REV_MESA
GL_MESA_pack_invert
GL_PACK_INVERT_MESA
GL_APPLE_client_storage
GL_UNPACK_CLIENT_STORAGE_APPLE
GL_APPLE_ycbcr_422
GL_YCBCR_422_APPLE
GL_UNSIGNED_SHORT_8_8_APPLE
GL_UNSIGNED_SHORT_8_8_REV_APPLE
GL_ATI_texture_env_combine3
GL_MODULATE_ADD_ATI
GL_MODULATE_SIGNED_ADD_ATI
GL_MODULATE_SUBTRACT_ATI
GLU_EXT_object_space_tess
GLU_EXT_nurbs_tessellator
GLU_FALSE
GLU_TRUE
GLU_VERSION_1_1
GLU_VERSION_1_2
GLU_VERSION_1_3
GLU_VERSION
GLU_EXTENSIONS
GLU_INVALID_ENUM
GLU_INVALID_VALUE
GLU_OUT_OF_MEMORY
GLU_INVALID_OPERATION
GLU_OUTLINE_POLYGON
GLU_OUTLINE_PATCH
GLU_NURBS_ERROR
GLU_ERROR
GLU_NURBS_BEGIN
GLU_NURBS_BEGIN_EXT
GLU_NURBS_VERTEX
GLU_NURBS_VERTEX_EXT
GLU_NURBS_NORMAL
GLU_NURBS_NORMAL_EXT
GLU_NURBS_COLOR
GLU_NURBS_COLOR_EXT
GLU_NURBS_TEXTURE_COORD
GLU_NURBS_TEX_COORD_EXT
GLU_NURBS_END
GLU_NURBS_END_EXT
GLU_NURBS_BEGIN_DATA
GLU_NURBS_BEGIN_DATA_EXT
GLU_NURBS_VERTEX_DATA
GLU_NURBS_VERTEX_DATA_EXT
GLU_NURBS_NORMAL_DATA
GLU_NURBS_NORMAL_DATA_EXT
GLU_NURBS_COLOR_DATA
GLU_NURBS_COLOR_DATA_EXT
GLU_NURBS_TEXTURE_COORD_DATA
GLU_NURBS_TEX_COORD_DATA_EXT
GLU_NURBS_END_DATA
GLU_NURBS_END_DATA_EXT
GLU_NURBS_ERROR1
GLU_NURBS_ERROR2
GLU_NURBS_ERROR3
GLU_NURBS_ERROR4
GLU_NURBS_ERROR5
GLU_NURBS_ERROR6
GLU_NURBS_ERROR7
GLU_NURBS_ERROR8
GLU_NURBS_ERROR9
GLU_NURBS_ERROR10
GLU_NURBS_ERROR11
GLU_NURBS_ERROR12
GLU_NURBS_ERROR13
GLU_NURBS_ERROR14
GLU_NURBS_ERROR15
GLU_NURBS_ERROR16
GLU_NURBS_ERROR17
GLU_NURBS_ERROR18
GLU_NURBS_ERROR19
GLU_NURBS_ERROR20
GLU_NURBS_ERROR21
GLU_NURBS_ERROR22
GLU_NURBS_ERROR23
GLU_NURBS_ERROR24
GLU_NURBS_ERROR25
GLU_NURBS_ERROR26
GLU_NURBS_ERROR27
GLU_NURBS_ERROR28
GLU_NURBS_ERROR29
GLU_NURBS_ERROR30
GLU_NURBS_ERROR31
GLU_NURBS_ERROR32
GLU_NURBS_ERROR33
GLU_NURBS_ERROR34
GLU_NURBS_ERROR35
GLU_NURBS_ERROR36
GLU_NURBS_ERROR37
GLU_AUTO_LOAD_MATRIX
GLU_CULLING
GLU_SAMPLING_TOLERANCE
GLU_DISPLAY_MODE
GLU_PARAMETRIC_TOLERANCE
GLU_SAMPLING_METHOD
GLU_U_STEP
GLU_V_STEP
GLU_NURBS_MODE
GLU_NURBS_MODE_EXT
GLU_NURBS_TESSELLATOR
GLU_NURBS_TESSELLATOR_EXT
GLU_NURBS_RENDERER
GLU_NURBS_RENDERER_EXT
GLU_OBJECT_PARAMETRIC_ERROR
GLU_OBJECT_PARAMETRIC_ERROR_EXT
GLU_OBJECT_PATH_LENGTH
GLU_OBJECT_PATH_LENGTH_EXT
GLU_PATH_LENGTH
GLU_PARAMETRIC_ERROR
GLU_DOMAIN_DISTANCE
GLU_MAP1_TRIM_2
GLU_MAP1_TRIM_3
GLU_POINT
GLU_LINE
GLU_FILL
GLU_SILHOUETTE
GLU_SMOOTH
GLU_FLAT
GLU_NONE
GLU_OUTSIDE
GLU_INSIDE
GLU_TESS_BEGIN
GLU_BEGIN
GLU_TESS_VERTEX
GLU_VERTEX
GLU_TESS_END
GLU_END
GLU_TESS_ERROR
GLU_TESS_EDGE_FLAG
GLU_EDGE_FLAG
GLU_TESS_COMBINE
GLU_TESS_BEGIN_DATA
GLU_TESS_VERTEX_DATA
GLU_TESS_END_DATA
GLU_TESS_ERROR_DATA
GLU_TESS_EDGE_FLAG_DATA
GLU_TESS_COMBINE_DATA
GLU_CW
GLU_CCW
GLU_INTERIOR
GLU_EXTERIOR
GLU_UNKNOWN
GLU_TESS_WINDING_RULE
GLU_TESS_BOUNDARY_ONLY
GLU_TESS_TOLERANCE
GLU_TESS_ERROR1
GLU_TESS_ERROR2
GLU_TESS_ERROR3
GLU_TESS_ERROR4
GLU_TESS_ERROR5
GLU_TESS_ERROR6
GLU_TESS_ERROR7
GLU_TESS_ERROR8
GLU_TESS_MISSING_BEGIN_POLYGON
GLU_TESS_MISSING_BEGIN_CONTOUR
GLU_TESS_MISSING_END_POLYGON
GLU_TESS_MISSING_END_CONTOUR
GLU_TESS_COORD_TOO_LARGE
GLU_TESS_NEED_COMBINE_CALLBACK
GLU_TESS_WINDING_ODD
GLU_TESS_WINDING_NONZERO
GLU_TESS_WINDING_POSITIVE
GLU_TESS_WINDING_NEGATIVE
GLU_TESS_WINDING_ABS_GEQ_TWO
GLU_TESS_MAX_COORD
FTGL_RENDER_FRONT
FTGL_RENDER_BACK
FTGL_RENDER_SIDE
FTGL_RENDER_ALL
FTGL_ALIGN_LEFT
FTGL_ALIGN_CENTER
FTGL_ALIGN_RIGHT
FTGL_ALIGN_JUSTIFY


================================================
FILE: src/Contained_RBTree.c
================================================
/******************************************************************************\
*   Contained_RBTree.cpp            by: TheSilverDirk / Michael Conrad         *
*   Created: 03/12/2000             Last Modified: 08/29/2002                  *
*   See Contained_RBTree.h for details                                         *
\******************************************************************************/

/*
Credits:

  Intrest in red/black trees was inspired by Dr. John Franco, and his animated
	red/black tree java applet.
  http://www.ececs.uc.edu/~franco/C321/html/RedBlack/redblack.html

  The node insertion code was written in a joint effort with Anthony Deeter.

  The red/black deletion algorithm was derived from the deletion patterns in
   "Fundamentals of Sequential and Parallel Algorithms",
	by Dr. Kenneth A. Berman and Dr. Jerome L. Paul

  I also got the sentinel node idea from this book.

*/
#include <config.h>
#include "Contained_RBTree.h"

//namespace ContainedClass {

RBTreeNode Sentinel= { &Sentinel, &Sentinel, &Sentinel, RBTreeNode_Black, 0 };

bool RBTreeNode_IsSentinel( RBTreeNode *Node ) {
	return Node->Left == Node;
}

void RBTreeNode_Init( RBTreeNode* Node ) {
	Node->Color= RBTreeNode_Unassigned;
}

void RBTree_InitRootSentinel( RBTreeNode *RootSentinel ) {
	RootSentinel->Color= RBTreeNode_Black;
	RootSentinel->Left= &Sentinel;
	RootSentinel->Right= &Sentinel;
	RootSentinel->Parent= 0; // this uniquely marks this as the root sentinel
	RootSentinel->Object= 0;
}

void RBTree_Clear( RBTreeNode *RootSentinel ) {
	RBTreeNode *Temp= RBTree_GetLeftmost(RootSentinel->Left);
	while (Temp != &Sentinel) {
		Temp->Color= RBTreeNode_Unassigned;
		Temp= RBTree_GetNext(Temp);
	}
	RootSentinel->Left= &Sentinel;
}

bool RBTree_Add( RBTreeNode *RootSentinel, RBTreeNode* NewNode, RBTree_inorder_func* inorder ) {
	RBTreeNode* Current;
	if (NewNode->Color != RBTreeNode_Unassigned)
		return false;

	NewNode->Color= RBTreeNode_Red;
	NewNode->Left=  &Sentinel;
	NewNode->Right= &Sentinel;

	Current= RootSentinel->Left;
	if (Current == &Sentinel) {
		RootSentinel->Left= NewNode;
		NewNode->Parent= RootSentinel;
	}
	else {
		do {
			// if the new node comes before the current node, go left
			if (inorder( NewNode->Object, Current->Object )) {
				if (Current->Left == &Sentinel) {
					Current->Left= NewNode;
					NewNode->Parent= Current;
					break;
				}
				else Current= Current->Left;
			}
			// else go right
			else {
				if (Current->Right == &Sentinel) {
					Current->Right= NewNode;
					NewNode->Parent= Current;
					break;
				}
				else Current= Current->Right;
			}
		} while (1);
		RBTree_Balance( Current );
	}
	RootSentinel->Left->Color= RBTreeNode_Black;
	return true;
}

RBTreeNode* RBTree_Find( const RBTreeNode *RootSentinel, const void* SearchKey, RBTree_compare_func* compare) {
	RBTreeNode* Current= RootSentinel->Left;
	while (Current != &Sentinel) {
		int i= compare( SearchKey, Current->Object );
		if      (i<0) Current= Current->Left;
		else if (i>0) Current= Current->Right;
		else return Current;
	}
	return &Sentinel;
}

RBTreeNode* RBTree_GetLeftmost( RBTreeNode* Node ) {
	while (Node->Left != &Sentinel)
		Node= Node->Left;
	return Node;
}

RBTreeNode* RBTree_GetRightmost( RBTreeNode* Node ) {
	while (Node->Right != &Sentinel)
		Node= Node->Right;
	return Node;
}

RBTreeNode* RBTree_GetPrev( RBTreeNode* Node ) {
	// If we are not at a leaf, move to the right-most node
	//  in the tree to the left of this node.
	if (Node->Left != &Sentinel) {
		RBTreeNode* Current= Node->Left;
		while (Current->Right != &Sentinel)
			Current= Current->Right;
		return Current;
	}
	// Else walk up the tree until we see a parent node to the left
	else {
		RBTreeNode* Parent= Node->Parent;
		while (Parent->Left == Node) {
			Node= Parent;
			Parent= Parent->Parent;
			// Check for RootSentinel
			if (!Parent) return &Sentinel;
		}
		return Parent;
	}
}

RBTreeNode* RBTree_GetNext( RBTreeNode* Node ) {
	// If we are not at a leaf, move to the left-most node
	//  in the tree to the right of this node.
	if (Node->Right != &Sentinel) {
		RBTreeNode* Current= Node->Right;
		while (Current->Left != &Sentinel)
			Current= Current->Left;
		return Current;
	}
	// Else walk up the tree until we see a parent node to the right
	else {
		RBTreeNode* Parent= Node->Parent;
		while (Parent->Right == Node) {
			Node= Parent;
			Parent= Node->Parent;
		}
		// Check for the RootSentinel
		if (!Parent->Parent) return &Sentinel;

		return Parent;
	}
}

void RBTree_RightSide_RightRotate( RBTreeNode* Node ) {
	RBTreeNode* temp= Node->Parent; // temp is used for parent
	RBTreeNode* child= Node->Left;

	temp->Right= child;
	child->Parent= temp;

	temp= child->Right; // temp is now used for grandchild
	Node->Left= temp;
	/*if (temp != Sentinel)*/
	temp->Parent= Node;

	child->Right= Node;
	Node->Parent= child;
}

void RBTree_LeftSide_LeftRotate( RBTreeNode* Node ) {
	RBTreeNode* temp= Node->Parent; // temp is used for parent
	RBTreeNode* child= Node->Right;

	temp->Left= child;
	child->Parent= temp;

	temp= child->Left; // temp is now used for grandchild
	Node->Right= temp;
	/*if (temp != Sentinel)*/
	temp->Parent= Node;

	child->Left= Node;
	Node->Parent= child;
}

void RBTree_LeftSide_RightRotate( RBTreeNode* Node ) {
	RBTreeNode* temp= Node->Parent; // temp is used for parent
	RBTreeNode* child= Node->Left;

	temp->Left= child;
	child->Parent= temp;

	temp= child->Right; // temp is now used for grandchild
	Node->Left= temp;
	/*if (temp != Sentinel)*/
	temp->Parent= Node;

	child->Right= Node;
	Node->Parent= child;
}

void RBTree_RightSide_LeftRotate( RBTreeNode* Node ) {
	RBTreeNode* temp= Node->Parent; // temp is used for parent
	RBTreeNode* child= Node->Right;

	temp->Right= child;
	child->Parent= temp;

	temp= child->Left; // temp is now used for grandchild
	Node->Right= temp;
	/*if (temp != Sentinel)*/
	temp->Parent= Node;

	child->Left= Node;
	Node->Parent= child;
}

// current is the parent node of the node just added.  The child is red.
void RBTree_Balance( RBTreeNode* Current ) {
	// if Current is a black node, no rotations needed
	while (Current->Color != RBTreeNode_Black) {
//		if (!Current->Parent) break;  XXX may not need this
		// Current is red, the imbalanced child is red, and parent is black.

		RBTreeNode *Parent= Current->Parent;

		// if the Current is on the right of the parent, the parent is to the left
		if (Parent->Right == Current) {
			// if the sibling is also red, we can pull down the color black from the parent
			if (Parent->Left->Color == RBTreeNode_Red) {
				Parent->Left->Color= RBTreeNode_Black;
				Current->Color= RBTreeNode_Black;
				Parent->Color= RBTreeNode_Red;
				// jump twice up the tree. if Current reaches the HeadSentinel (black node), the loop will stop
				Current= Parent->Parent;
				continue;
			}
			// if the imbalance (red node) is on the left, and the parent is on the left,
			//  a "prep-slide" is needed. (see diagram)
			if (Current->Left->Color == RBTreeNode_Red)
				RBTree_RightSide_RightRotate( Current );

			// Now we can do our left rotation to balance the tree.
			if (Parent->Parent->Left == Parent)
				RBTree_LeftSide_LeftRotate( Parent );
			else
				RBTree_RightSide_LeftRotate( Parent );
			Parent->Color= RBTreeNode_Red;
			Parent->Parent->Color= RBTreeNode_Black;
			return;
		}
		// else the parent is to the right
		else {
			// if the sibling is also red, we can pull down the color black from the parent
			if (Parent->Right->Color == RBTreeNode_Red) {
				Parent->Right->Color= RBTreeNode_Black;
				Current->Color= RBTreeNode_Black;
				Parent->Color= RBTreeNode_Red;
				// jump twice up the tree. if Current reaches the HeadSentinel (black node), the loop will stop
				Current= Parent->Parent;
				continue;
			}
			// if the imbalance (red node) is on the right, and the parent is on the right,
			//  a "prep-slide" is needed. (see diagram)
			if (Current->Right->Color == RBTreeNode_Red)
				RBTree_LeftSide_LeftRotate( Current );

			// Now we can do our left rotation to balance the tree.
			if (Parent->Parent->Left == Parent)
				RBTree_LeftSide_RightRotate( Parent );
			else
				RBTree_RightSide_RightRotate( Parent );
			Parent->Color= RBTreeNode_Red;
			Parent->Parent->Color= RBTreeNode_Black;
			return;
		}
	}
	return;
}

bool RBTree_Prune( RBTreeNode* Current ) {
	RBTreeNode* Temp;
	if (Current->Color == RBTreeNode_Unassigned)
		return false;

	// If this is a leaf node (or almost a leaf) we can just prune it
	if (Current->Left == &Sentinel || Current->Right == &Sentinel)
		RBTree_PruneLeaf(Current);

	// Otherwise we need a successor.  We are guaranteed to have one because
	//  the current node has 2 children.
	else {
		RBTreeNode* Successor= RBTree_GetNext( Current );
		// Do we like this successor?  If not, get the other one.
		if (Successor->Color == RBTreeNode_Black && Successor->Left == &Sentinel && Successor->Right == &Sentinel)
			Successor= RBTree_GetPrev( Current );

		RBTree_PruneLeaf( Successor );

		// now exchange the successor for the current node
		Temp= Current->Right;
		Successor->Right= Temp;
		Temp->Parent= Successor;

		Temp= Current->Left;
		Successor->Left= Temp;
		Temp->Parent= Successor;

		Temp= Current->Parent;
		Successor->Parent= Temp;
		if (Temp->Left == Current) Temp->Left= Successor; else Temp->Right= Successor;
		Successor->Color= Current->Color;
	}
	Current->Color= RBTreeNode_Unassigned;
	return true;
}

// PruneLeaf performs pruning of nodes with at most one child node.
void RBTree_PruneLeaf( RBTreeNode* Node ) {
	RBTreeNode *Parent= Node->Parent, *Current, *Sibling;
	bool LeftSide= (Parent->Left == Node);

	// if the node is red and has at most one child, then it has no child.
	// Prune it.
	if (Node->Color == RBTreeNode_Red) {
		if (LeftSide) Parent->Left= &Sentinel;
		else Parent->Right= &Sentinel;
		return;
	}

	// Node is black here.  If it has a child, the child will be red.
	if (Node->Left != &Sentinel) {
		// swap with child
		Node->Left->Color= RBTreeNode_Black;
		Node->Left->Parent= Parent;
		if (LeftSide) Parent->Left= Node->Left;
		else Parent->Right= Node->Left;
		return;
	}
	if (Node->Right != &Sentinel) {
		// swap with child
		Node->Right->Color= RBTreeNode_Black;
		Node->Right->Parent= Parent;
		if (LeftSide) Parent->Left= Node->Right;
		else Parent->Right= Node->Right;
		return;
	}

/*	Now, we have determined that Node is a black leaf node with no children.
	The tree must have the same number of black nodes along any path from root
	to leaf.  We want to remove a black node, disrupting the number of black
	nodes along the path from the root to the current leaf.  To correct this,
	we must either shorten all other paths, or add a black node to the current
	path.  Then we can freely remove our black leaf.

	While we are pointing to it, we will go ahead and delete the leaf and
	replace it with the sentinel (which is also black, so it won't affect
	the algorithm).
*/
	if (LeftSide) Parent->Left= &Sentinel; else Parent->Right= &Sentinel;

	Sibling= (LeftSide)? Parent->Right : Parent->Left;
	Current= Node;

	// Loop until the current node is red, or until we get to the root node.
	// (The root node's parent is the RootSentinel, which will have a NULL parent.)
	while (Current->Color == RBTreeNode_Black && Parent->Parent != 0) {
		// If the sibling is red, we are unable to reduce the number of black
		//  nodes in the sibling tree, and we can't increase the number of black
		//  nodes in our tree..  Thus we must do a rotation from the sibling
		//  tree to our tree to give us some extra (red) nodes to play with.
		// This is Case 1 from the text
		if (Sibling->Color == RBTreeNode_Red) {
			Parent->Color= RBTreeNode_Red;
			Sibling->Color= RBTreeNode_Black;
			if (LeftSide) {
				if (Parent->Parent->Left == Parent)
					RBTree_LeftSide_LeftRotate( Parent );
				else
					RBTree_RightSide_LeftRotate( Parent );
				Sibling= Parent->Right;
			}
			else {
				if (Parent->Parent->Left == Parent)
					RBTree_LeftSide_RightRotate( Parent );
				else
					RBTree_RightSide_RightRotate( Parent );
				Sibling= Parent->Left;
			}
			continue;
		}
		// Sibling will be black here

		// If the sibling is black and both children are black, we have to
		//  reduce the black node count in the sibling's tree to match ours.
		// This is Case 2a from the text.
		if (Sibling->Right->Color == RBTreeNode_Black && Sibling->Left->Color == RBTreeNode_Black) {
			Sibling->Color= RBTreeNode_Red;
			// Now we move one level up the tree to continue fixing the
			// other branches.
			Current= Parent;
			Parent= Current->Parent;
			LeftSide= (Parent->Left == Current);
			Sibling= (LeftSide)? Parent->Right : Parent->Left;
			continue;
		}
		// Sibling will be black with 1 or 2 red children here

		// << Case 2b is handled by the while loop. >>

		// If one of the sibling's children are red, we again can't make the
		//  sibling red to balance the tree at the parent, so we have to do a
		//  rotation.  If the "near" nephew is red and the "far" nephew is
		//  black, we need to do a "prep-slide" (aka "double rotation")
		// After doing a rotation and rearranging a few colors, the effect is
		//  that we maintain the same number of black nodes per path on the far
		//  side of the parent, and we gain a black node on the current side,
		//  so we are done.
		// This is Case 4 from the text. (Case 3 is the double rotation)
		if (LeftSide) {
			if (Sibling->Right->Color == RBTreeNode_Black) { // Case 3 from the text
				RBTree_RightSide_RightRotate( Sibling );
				Sibling= Parent->Right;
			}
			// now Case 4 from the text
			Sibling->Right->Color= RBTreeNode_Black;
			Sibling->Color= Parent->Color;
			Parent->Color= RBTreeNode_Black;

			Current= Parent;
			Parent= Current->Parent;
			// I would have assigned to LeftSide here,
			//   but we are exiting the function, so why bother?
			// LeftSide= (Parent->Left == Current);
			if /*(LeftSide)*/(Parent->Left == Current)
				RBTree_LeftSide_LeftRotate( Current );
			else
				RBTree_RightSide_LeftRotate( Current );
			return;
		}
		else {
			if (Sibling->Left->Color == RBTreeNode_Black) { // Case 3 from the text
				RBTree_LeftSide_LeftRotate( Sibling );
				Sibling= Parent->Left;
			}
			// now Case 4 from the text
			Sibling->Left->Color= RBTreeNode_Black;
			Sibling->Color= Parent->Color;
			Parent->Color= RBTreeNode_Black;

			Current= Parent;
			Parent= Current->Parent;
			// I would have assigned to LeftSide here,
			//   but we are exiting the function, so why bother?
			// LeftSide= (Parent->Left == Current);
			if /*(LeftSide)*/(Parent->Left == Current)
				RBTree_LeftSide_RightRotate( Current );
			else
				RBTree_RightSide_RightRotate( Current );
			return;
		}
	}

	// Now, make the current node black (to fulfill Case 2b)
	// Case 4 will have exited directly out of the function.
	// If we stopped because we reached the top of the tree,
	//   the head is black anyway so don't worry about it.
	Current->Color= RBTreeNode_Black;
}

//}


================================================
FILE: src/Contained_RBTree.h
================================================
/******************************************************************************\
*   Contained_RBTree.h              by: TheSilverDirk / Michael Conrad
*   Created: 06/23/2000             Last Modified: 04/29/2005
*
*   2005-04-29: Hacked-up sufficiently to be compilable under C.
*
*   This is a red/black binary search tree implementation using the
*   "contained class" system.  The "inorder" and "compare" function pointers
*   allow you to do custom sorting.  These pointers MUST point to valid
*   functions before you use the tree.
\******************************************************************************/

#include "Global.h"

#ifndef CONTAINED_RBTREE_H
#define CONTAINED_RBTREE_H

//namespace ContainedClass {

/******************************************************************************\
*   Contained RBTree Data Structure
\******************************************************************************/

#define RBTreeNode_Black 0
#define RBTreeNode_Red 1
#define RBTreeNode_Unassigned 2
typedef struct RBTreeNode_t {
//	enum NodeColor { Black= 0, Red= 1, Unassigned= 2 };

	struct RBTreeNode_t* Left;
	struct RBTreeNode_t* Right;
	struct RBTreeNode_t* Parent;
	int         Color;
	void*       Object;
} RBTreeNode;

/******************************************************************************\
*   Base RBTree functions - all functions required to manipulate a R/B tree.
*
*   These functions are all ordinary functions in order to be compatible with
*     other languages, such as C
\******************************************************************************/
typedef bool RBTree_inorder_func( const void* Obj_A, const void* Obj_B );
typedef int  RBTree_compare_func( const void* SearchKey, const void* Object );

void RBTreeNode_Init( RBTreeNode* Node );
bool RBTreeNode_IsSentinel( RBTreeNode *Node );
void RBTree_InitRootSentinel( RBTreeNode *RootSentinel );
void RBTree_Clear( RBTreeNode *RootSentinel );
RBTreeNode* RBTree_GetPrev( RBTreeNode* Node );
RBTreeNode* RBTree_GetNext( RBTreeNode* Node );
RBTreeNode* RBTree_GetRightmost( RBTreeNode* Node );
RBTreeNode* RBTree_GetLeftmost( RBTreeNode* Node );
//inline const RBTreeNode* RBTree_GetLeftmost ( const RBTreeNode* Node ) { return RBTree_GetLeftmost (const_cast<RBTreeNode*>(Node)); }
//inline const RBTreeNode* RBTree_GetRightmost( const RBTreeNode* Node ) { return RBTree_GetRightmost(const_cast<RBTreeNode*>(Node)); }
//inline const RBTreeNode* RBTree_GetPrev     ( const RBTreeNode* Node ) { return RBTree_GetPrev     (const_cast<RBTreeNode*>(Node)); }
//inline const RBTreeNode* RBTree_GetNext     ( const RBTreeNode* Node ) { return RBTree_GetNext     (const_cast<RBTreeNode*>(Node)); }
void RBTree_LeftSide_LeftRotate( RBTreeNode* Node );
void RBTree_LeftSide_RightRotate( RBTreeNode* Node );
void RBTree_RightSide_RightRotate( RBTreeNode* Node );
void RBTree_RightSide_LeftRotate( RBTreeNode* Node );
bool RBTree_Add( RBTreeNode *RootSentinel, RBTreeNode* NewNode, RBTree_inorder_func* inorder );
RBTreeNode* RBTree_Find( const RBTreeNode *RootSentinel, const void* SearchKey, RBTree_compare_func* compare );
void RBTree_Balance( RBTreeNode* Node );
bool RBTree_Prune( RBTreeNode* Node );
void RBTree_PruneLeaf( RBTreeNode* Node );

extern RBTreeNode Sentinel;

/******************************************************************************\
*   Contained RBTree Class                                                     *
\******************************************************************************/
/*
class ECannotAddNode {};
class ECannotRemoveNode {};

class RBTree {
public:
	class Node: public RBTreeNode {
	public:
		Node()  { RBTreeNode_Init(this); }
		~Node() { if (RBTreeNode::Color != RBTreeNode::Unassigned) RBTree_Prune(this); }

		// The average user shouldn't need these, but they might come in handy.
		void* Left()   const { return RBTreeNode::Left->Object;   }
		void* Right()  const { return RBTreeNode::Right->Object;  }
		void* Parent() const { return RBTreeNode::Parent->Object; }
		int   Color()        { return RBTreeNode::Color; }

		// These let you use your nodes as a sequence.
		void* Next()   const { return RBTree_GetNext(this)->Object; }
		void* Prev()   const { return RBTree_GetPrev(this)->Object; }

		bool IsSentinel() { return RBTreeNode_IsSentinel(this); }

		friend RBTree;
	};

private:
	RBTreeNode RootSentinel; // the left child of the sentinel is the root node
public:
	RBTree_inorder_func *inorder;
	RBTree_compare_func *compare;

	RBTree() { RBTree_InitRootSentinel(RootSentinel); }
	~RBTree() { Clear(); }

	void Clear() { RBTree_Clear(RootSentinel); }
	bool IsEmpty() const { return RBTreeNode_IsSentinel(RootSentinel.Left); }

	void* GetRoot()  const { return RootSentinel.Left->Object; }
	void* GetFirst() const { return RBTree_GetLeftmost(RootSentinel.Left)->Object; }
	void* GetLast()  const { return RBTree_GetRightmost(RootSentinel.Left)->Object; }

	void Add( Node* NewNode ) { if (!RBTree_Add(RootSentinel, NewNode, inorder)) throw ECannotAddNode(); }

	void* Find( const void *SearchKey ) const { return RBTree_Find(RootSentinel, SearchKey, compare)->Object; }

	static void Remove( Node* Node ) { if (!RBTree_Prune(Node)) throw ECannotRemoveNode(); }
};
*/

typedef struct RBTree_t {
	RBTreeNode RootSentinel; // the left child of the sentinel is the root node
	RBTree_inorder_func *inorder;
	RBTree_compare_func *compare;
} RBTree;

/******************************************************************************\
*   Type-Safe Contained Red/Black Tree Class                                   *
\******************************************************************************/
/*
template <class T>
class TypedRBTree: public RBTree {
public:
	typedef RBTree Inherited;

	class Node: public RBTree::Node {
	public:
		typedef RBTree::Node Inherited;

		// The average user shouldn't need these, but they might come in handy.
		T* Left()   const { return (T*) Inherited::Left();   }
		T* Right()  const { return (T*) Inherited::Right();  }
		T* Parent() const { return (T*) Inherited::Parent(); }
		int Color() const { return Inherited::Color(); }

		// These let you use your nodes as a sequence.
		T* Next()   const { return (T*) Inherited::Next(); }
		T* Prev()   const { return (T*) Inherited::Prev(); }

		friend TypedRBTree<T>;
	};

public:
	T* GetRoot()  const { return (T*) Inherited::GetRoot();  }
	T* GetFirst() const { return (T*) Inherited::GetFirst(); }
	T* GetLast()  const { return (T*) Inherited::GetLast();  }

	void Add( Node* NewNode ) { Inherited::Add(NewNode); }

	T* Find( const void *Val ) const { return (T*) Inherited::Find(Val); }

	static void Remove( Node* Node ) { Inherited::Remove(Node); }
};

}
*/
#endif



================================================
FILE: src/Font.c
================================================
#include <config.h>
#include "Global.h"
#include "ParseGL.h"
#include "ProcessInput.h"
#include "SymbolHash.h"

#ifdef HAVE_LIBFTGL

/*=head2 FTGL Font Functions

These functions come from the FTGL library.  They can open any font file that the FreeType
library can open.  A symbolic name takes the place of FTGL's C<FTfont*> pointer. You must
call one of the Create methods (and it must succeed) to initialize a symbolic name before
it can be used in any of the rendering functions.

=item ftglCreateBitmapFont NAME "FONT_FILE_PATH"

Create a 2-color font that renders directly to the framebuffer in 2D.

=item ftglCreatePixmapFont NAME "FONT_FILE_PATH"

Create a 256-color-grayscale font that renders directly to the framebuffer in 2D.

=item ftglCreateTextureFont NAME "FONT_FILE_PATH"

Create a font that renders each glyph into a texture, so that a small set of textures can
supply all your rendering needs.

=item ftglCreateBufferFont NAME "FONT_FILE_PATH"

Create a texture-based font that renders each line of text into its own texture, so that common
phrases can be drawn as a single polygon.

=item ftglCreateExtrudeFont NAME "FONT_FILE_PATH"

Create 3D models to represent each glyph extruded as a solid object.

=item ftglCreateOutlineFont NAME "FONT_FILE_PATH"

Create 2D model of GL lines that trace the outline of each glyph.

=item ftglCreatePolygonFont NAME "FONT_FILE_PATH"

Create each glyph as a flat 2D mesh of polygons.

=item ftglDestroyFont NAME

Free resources and un-define the symbolic NAME.

=cut */

bool InitNamedFont(SymbVarEntry *sym, FTGLfont *font) {
	if (!font) {
		/* if newly created, remove the tree node so we don't have a null pointer dangling around */
		if (!sym->Data)
			DeleteSymbVar(sym);
		return false;
	}
	/* If the font already existed, delete the old one */
	if (sym->Data)
		ftglDestroyFont( (FTGLfont*) sym->Data );
	
	sym->Data= font;
	return true;
}

COMMAND(ftglCreateBitmapFont, "F!/") {
	return InitNamedFont(parsed->objects[0], ftglCreateBitmapFont(parsed->strings[0]));
}
COMMAND(ftglCreateBufferFont, "F!/") {
	return InitNamedFont(parsed->objects[0], ftglCreateBufferFont(parsed->strings[0]));
}
COMMAND(ftglCreateExtrudeFont, "F!/") {
	return InitNamedFont(parsed->objects[0], ftglCreateExtrudeFont(parsed->strings[0]));
}
COMMAND(ftglCreateOutlineFont, "F!/") {
	return InitNamedFont(parsed->objects[0], ftglCreateOutlineFont(parsed->strings[0]));
}
COMMAND(ftglCreatePixmapFont, "F!/") {
	return InitNamedFont(parsed->objects[0], ftglCreatePixmapFont(parsed->strings[0]));
}
COMMAND(ftglCreatePolygonFont, "F!/") {
	return InitNamedFont(parsed->objects[0], ftglCreatePolygonFont(parsed->strings[0]));
}
COMMAND(ftglCreateTextureFont, "F!/") {
	return InitNamedFont(parsed->objects[0], ftglCreateTextureFont(parsed->strings[0]));
}

COMMAND(ftglDestroyFont, "F") {
	SymbVarEntry *sym= parsed->objects[0];
	ftglDestroyFont( (FTGLfont*) sym->Data );
	DeleteSymbVar(sym);
	return true;
}

/*=item ftglSetFontCharMap NAME CMAP_CODE

Set the charmap to one of the codes known by the FreeType library.

=item ftglSetFontFaceSize NAME SIZE

For texture and raster fonts, render each glyph at SIZE pixels.  For Vector fonts, render each
glyph at SIZE logical units.

=item ftglSetFontDepth NAME SIZE

For extruded fonts, set the depth to SIZE units.

=item ftglSetFontOutset NAME FRONT BACK

For extruded fonts.

=cut */

COMMAND(ftglSetFontCharMap, "Fi") {
	FTGLfont *font= (FTGLfont*) parsed->objects[0]->Data;
	ftglSetFontCharMap(font, parsed->ints[0]);
	return true;
}

COMMAND(ftglSetFontFaceSize, "Fii") {
	FTGLfont *font= (FTGLfont*) parsed->objects[0]->Data;
	ftglSetFontFaceSize(font, parsed->ints[0], parsed->ints[1]);
	return true;
}

COMMAND(ftglSetFontDepth, "Ff") {
	FTGLfont *font= (FTGLfont*) parsed->objects[0]->Data;
	ftglSetFontDepth(font, parsed->floats[0]);
	return true;
}

COMMAND(ftglSetFontOutset, "Fff") {
	FTGLfont *font= (FTGLfont*) parsed->objects[0]->Data;
	ftglSetFontOutset(font, parsed->floats[0], parsed->floats[1]);
	return true;
}

/*=item ftglRenderFont NAME TEXT FLAGS[...]

Renter TEXT using NAMEd font, affected by FLAGS.  In the C API, the flags are combined, but in
this command each flag is given as another parameter.  Flags are: FTGL_RENDER_FRONT,
FTGL_RENDER_BACK, FTGL_RENDER_SIDE, FTGL_RENDER_ALL, FTGL_ALIGN_LEFT, FTGL_ALIGN_RIGHT,
FTGL_ALIGN_CENTER, FTGL_ALIGN_JUSTIFY.

=cut */

COMMAND(ftglRenderFont, "Fsi*") {
	FTGLfont *font= (FTGLfont*) parsed->objects[0]->Data;
	int flags= 0, i;
	for (i=0; i< parsed->iCnt; i++)
		flags |= parsed->ints[i];
	ftglRenderFont(font, parsed->strings[0], flags);
	return true;
}

#endif


================================================
FILE: src/Font.h
================================================
#ifndef FONT_H
#define FONT_H

#endif


================================================
FILE: src/Global.c
================================================
#include <config.h>
#include "Global.h"

#ifdef _WIN32
void WinPerror(char *msg) {
	char* BuffPtr= NULL;
	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		GetLastError(),
		0,
		(LPTSTR) &BuffPtr,
		0,
		NULL);
	fprintf(stderr, "%s: %s", msg, BuffPtr? BuffPtr : "Error message lookup failed");
	if (BuffPtr) LocalFree(BuffPtr);
}
#endif

#ifndef NDEBUG
void DebugMsg(char *msg, ...) {
	va_list ap;
	va_start(ap, msg);
	vfprintf(stderr, msg, ap);
	va_end(ap);
	fflush(stderr);
}
#endif



================================================
FILE: src/Global.h
================================================
#ifndef GLOBAL_H
#define GLOBAL_H

/* ------------------------------------
 * Settings for the program.
 */

#ifndef MAX_COMMAND_BATCH
#  define MAX_COMMAND_BATCH 8
#endif
#ifndef MAX_GL_PARAMS
#  define MAX_GL_PARAMS 32
#endif

struct ParseParamsResult;
#define COMMAND(name,fmt) bool cmd_##name(struct ParseParamsResult *parsed)

#ifndef NDEBUG
#  define DEBUGMSG(a) DebugMsg a
  void DebugMsg(char *str, ...);
#else
  #define DEBUGMSG(a) do{}while(0)
#endif

#ifdef _WIN32
void WinPerror(char *msg);
#endif

#endif


================================================
FILE: src/ImageLoader.c
================================================
#define INCLUDE_GL
#define INCLUDE_SDL
#include <config.h>
#include "Global.h"
#include "ParseGL.h"
#include "ImageLoader.h"
#include "ProcessInput.h"

bool UsableByGL(SDL_Surface *Img) {
	int dim;
	// images must be square (could get into the ARB_rectangular_texture mess, but then
	// users of CmdlineGL would have to deal with the detection)
	if (Img->w != Img->h) {
		fprintf(stderr, "OpenGL requires square images. (image is %dx%d)\n", (int)Img->w, (int)Img->h);
		return false;
	}
	// image dimensions must be a power of 2
	for (dim= Img->w; dim != 1; dim>>=1)
		if (dim&1) {
			fprintf(stderr, "OpenGL requires image dimensions to be a power of 2. (%d is not)\n", (int)Img->w);
			return false;
		}
	if (Img->format->BytesPerPixel < 2 || Img->format->BytesPerPixel > 4) {
		fprintf(stderr, "Image pixel format not handled by CmdlineGL. Expecting 16/24/32bpp\n");
		return false;
	}
	// we need a contiguous byte array of pixels
	//if (Img->pitch != Img->w * Img->format->BytesPerPixel) {
	//	fprintf(stderr, "SDL did not load the pixels as a contiguous array of bytes.\n");
	//	return false;
	//}
	return true;
}

SDL_Surface* LoadImg(const char *FName) {
	SDL_Surface *Img;
#ifdef HAVE_LIBSDL_IMAGE
	Img= IMG_Load(FName);
#else
	Img= SDL_LoadBMP(FName);
#endif
	if (!Img)
		fprintf(stderr, "Error loading image '%s'"
		#ifndef HAVE_LIBSDL_IMAGE
			"; SDL_image not available, so file must be plain bitmap"
		#endif
			, FName);
	else if (!UsableByGL(Img)) {
		fprintf(stderr, "Unable to use image '%s'\n", FName);
		SDL_FreeSurface(Img);
		Img= NULL;
	}
	else
		DEBUGMSG(("Loaded image '%s', %dx%d %dbpp\n", FName, Img->w, Img->h, Img->format->BitsPerPixel));
	return Img;
}

// This code seems dirty... but I don't have enough experience with SDL & OpenGL
// on both architecture endiannesses to optimize it.
bool LoadImgIntoTexture(SDL_Surface *Img) {
	int format, type, internalformat, pitchpix=0, remainder=0;
	SDL_PixelFormat *fmt= Img->format;
	#if SDL_BYTEORDER == SDL_BIG_ENDIAN
	if (fmt->Rmask == 0xFF000000 && fmt->Gmask == 0x00FF0000 && fmt->Bmask == 0x0000FF00) {
	#else
	if (fmt->Rmask == 0x000000FF && fmt->Gmask == 0x0000FF00 && fmt->Bmask == 0x00FF0000) {
	#endif
		format= fmt->Amask? GL_RGBA : GL_RGB;
		internalformat= format;
		type= GL_UNSIGNED_BYTE;
		DEBUGMSG(("Image seems to be %s/GL_UNSIGNED_BYTE\n", format==GL_RGBA?"GL_RGBA":"GL_RGB"));
	}
	#if SDL_BYTEORDER == SDL_BIG_ENDIAN
	else if (fmt->Bmask == 0xFF000000 && fmt->Gmask == 0x00FF0000 && fmt->Rmask == 0x0000FF00) {
	#else
	else if (fmt->Bmask == 0x000000FF && fmt->Gmask == 0x0000FF00 && fmt->Rmask == 0x00FF0000) {
	#endif
		format= fmt->Amask? GL_BGRA : GL_BGR;
		internalformat= format;
		type= GL_UNSIGNED_BYTE;
		DEBUGMSG(("Image seems to be %s/GL_UNSIGNED_BYTE\n", format==GL_BGRA?"GL_BGRA":"GL_BGR"));
	}
	else {
		// I don't want to implement the rest until I actually have some way to test it.
		// Really, I haven't even tested anything other than BGR.
		fprintf(stderr, "Currently the only supported formats are BGR(A) or RGB(A).\n");
		return false;
	}
	
	// This should probably never happen, since dimensions are powers of two, but just in case...
	if (Img->pitch != Img->w * Img->format->BytesPerPixel) {
		pitchpix= Img->pitch / Img->format->BytesPerPixel;
		remainder= Img->pitch % Img->format->BytesPerPixel;
		glPixelStorei(GL_UNPACK_ROW_LENGTH, pitchpix);
		if (remainder)
			glPixelStorei(GL_UNPACK_ALIGNMENT, 1+(remainder|(remainder>>1)|(remainder>>2)));
	}
	glTexImage2D(GL_TEXTURE_2D, 0, internalformat, Img->w, Img->h, 0, format, type, Img->pixels);
	// Restore defaults
	if (pitchpix)  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
	if (remainder) glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	return true;
}

/*=head2 Texture Commands

=item cglLoadImage2D "FILE_PATH"

Load image data into the texture currently bound to GL_TEXTURE_2D.  If compiled with support
for the SDL_image library, you may supply any image format handled by that library, else you
are limited to Windows Bitmaps ".BMP".  The image must contain RGB or RGBA pixels.

=cut */

COMMAND(cglLoadImage2D, "/") {
	int success;
	SDL_Surface *Img;

	if (!(Img= LoadImg(parsed->strings[0])))
		return false;
	// Then, load the image data into OpenGL
	SDL_LockSurface(Img);
	success= LoadImgIntoTexture(Img);
	SDL_UnlockSurface(Img);
	SDL_FreeSurface(Img);
	return success;
}



================================================
FILE: src/ImageLoader.h
================================================
#ifndef IMAGE_LOADER_H
#define IMAGE_LOADER_H

#endif


================================================
FILE: src/ParseGL.c
================================================
#define INCLUDE_GL
#include <config.h>
#include "Global.h"
#include "ParseGL.h"
#include "Server.h"
#include "SymbolHash.h"
#include "ImageLoader.h"
#include "Font.h"

bool PointsInProgress= false; // Whenever glBegin is active, until glEnd
bool FrameInProgress= false;  // True after any gl command, until cglSwapBuffers

/*=head2 GL Setup Commands

=item glEnable FEATURE [FEATURE...]

Enable one or more GL feature bits.

=item glDisable FEATURE [FEATURE...]

Disable one or more GL feature bits.

=item glHint TARGET MODE

Adjust GL optional behavior.

=item glBlendFunc SOURCE_FACTOR DEST_FACTOR

Control how new pixel colors are blended into the existing frame buffer.  SOURCE_FACTOR and
DEST_FACTOR each an enum constant describing the equation used.

=item glShadeModel GL_SMOOTH|GL_FLAT

Control whether lighting is calculated per-pixel or per-vertex.

=cut */

COMMAND(glEnable, "i*") {
	while (parsed->iCnt > 0)
		glEnable(parsed->ints[--parsed->iCnt]);
	return true;
}

COMMAND(glDisable, "i*") {
	while (parsed->iCnt > 0)
		glDisable(parsed->ints[--parsed->iCnt]);
	return true;
}

COMMAND(glHint, "ii") {
	glHint(parsed->ints[0], parsed->ints[1]);
	return true;
}

COMMAND(glBlendFunc, "ii") {
	glBlendFunc(parsed->ints[0], parsed->ints[1]);
	return true;
}

COMMAND(glShadeModel, "i") {
	glShadeModel(parsed->ints[0]);
	return true;
}

/*=head2 GL Lighting Setup Commands

These settings affect the overall lighting calculation

=item glColorMaterial FACE MODE

Set one or both faces of polygons to use the current color as one or more color components of
the "material" in the lighting equation.  FACE is: GL_FRONT, GL_BACK, GL_FRONT_AND_BACK, and
MODE is: GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_AMBIENT_AND_DIFFUSE.

=item glFog PNAME [PARAMS...]

Apply a fog setting.  PNAME selects which value to change, and PARAMS depends on PNAME.
If PNAME is a color, you may use HTML color notation.  See L</Colors>.

=item glLightModel PNAME [PARAMS...]

Apply a lighting setting.  PNAME is the setting to change, and PARAMS depend on PNAME.  If the
PARAMS are a color, you may use HTML color notation.  See L</Colors>.

=item glLight LIGHT PNAME [PARAMS...]

Apply a per-light setting.  LIGHT is the enum or number of the light to be altered, and PNAME
selects which parameter to change.  PARAMS depends on PNAME. If PARAMS is a color, you may use
HTML color notation.  See L</Colors>.

=cut */

COMMAND(glColorMaterial, "ii") {
	glColorMaterial(parsed->ints[0], parsed->ints[1]);
	return true;
}

COMMAND(glFog, "ib") {
	int mode= parsed->ints[0];

	// The parameter to this one really matters, since floats get fixed-point
	//  multiplied, and colors need special treatment.
	switch (mode) {
	case GL_FOG_MODE:
	case GL_FOG_INDEX:
		if (!ParseParams(&parsed->strings[0], "i", parsed))
			return false;
		glFogi(mode, parsed->ints[1]);
		return true;
	case GL_FOG_DENSITY:
	case GL_FOG_START:
	case GL_FOG_END:
		if (!ParseParams(&parsed->strings[0], "f", parsed))
			return false;
		glFogf(mode, parsed->floats[0]);
		return true;
	case GL_FOG_COLOR:
		if (!ParseParams(&parsed->strings[0], "c", parsed))
			return false;
		glFogfv(mode, parsed->floats);
		return true;
	default:
		snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Unsupported mode constant for glFog: %d", mode);
		parsed->errmsg= parsed->errmsg_buf;
	}
	return false;
}

COMMAND(glLightModel, "ib") {
	int pname= parsed->ints[0];

	switch (pname) {
	case GL_LIGHT_MODEL_AMBIENT:
		if (!ParseParams(&parsed->strings[0], "c", parsed))
			return false;
		glLightModelfv(pname, parsed->floats);
		return true;
	#ifdef GL_LIGHT_MODEL_COLOR_CONTROL
	case GL_LIGHT_MODEL_COLOR_CONTROL:
	#endif
	case GL_LIGHT_MODEL_LOCAL_VIEWER:
	case GL_LIGHT_MODEL_TWO_SIDE:
		if (!ParseParams(&parsed->strings[0], "i", parsed))
			return false;
		glLightModeliv(pname, parsed->ints+1);
		return true;
	default:
		snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Unsupported pname constant for glLightModel: %d", pname);
		parsed->errmsg= parsed->errmsg_buf;
	}
	return false;
}

COMMAND(glLight, "iib") {
	int light= parsed->ints[0], pname= parsed->ints[1];

	switch (pname) {
	case GL_AMBIENT:
	case GL_DIFFUSE:
	case GL_SPECULAR:
		if (!ParseParams(&parsed->strings[0], "c", parsed))
			return false;
		assert(parsed->fCnt == 4);
		break;
	case GL_POSITION:
	#ifdef GL_SPOT_DIRECTION
	case GL_SPOT_DIRECTION:
	#endif
		if (!ParseParams(&parsed->strings[0], "ffff?", parsed))
			return false;
		assert(parsed->fCnt >= 3 && parsed->fCnt <= 4);
		break;
	#ifdef GL_SPOT_EXPONENT
	case GL_SPOT_EXPONENT:
	case GL_SPOT_CUTOFF:
	#endif
	#ifdef GL_CONSTANT_ATTENUATION
	case GL_CONSTANT_ATTENUATION:
	case GL_LINEAR_ATTENUATION:
	case GL_QUADRATIC_ATTENUATION:
	#endif
		if (!ParseParams(&parsed->strings[0], "f", parsed))
			return false;
		assert(parsed->fCnt == 1);
		break;
	default:
		snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Unsupported pname constant for glLight: %d", pname);
		parsed->errmsg= parsed->errmsg_buf;
		return false;
	}
	glLightfv(light, pname, parsed->floats);
	return true;
}

/*=head2 Render Loop Commands

=item glClear ENUM [ENUM...]

Clear one or more aspects of the frame buffer.  Normally the constants are combined as a bit
mask, but for this protocol they are just given as multiple arguments.

=item glClearColor COLOR | R G B [A]

Specify what color to use when clearing the frame buffer.  You may use a single HTML-style
color code, or individual floating point components.  See L</Colors>

=item glClearDepth DEPTH

Select what depth to assign when clearing the depth buffer.

=item glFlush

Push any un-written commands through to the graphics layer.

=cut */
COMMAND(glClear, "i*") {
	int flags= 0;
	while (parsed->iCnt > 0)
		flags|= parsed->ints[--parsed->iCnt];
	glClear(flags);
	FrameInProgress= true;
	return true;
}

COMMAND(glClearColor, "c") {
	glClearColor(parsed->floats[0], parsed->floats[1], parsed->floats[2], parsed->floats[3]);
	return true;
}

COMMAND(glClearDepth, "d") {
	glClearDepth(parsed->doubles[0]);
	return true;
}

COMMAND(glFlush, "") {
	glFlush();
	return true;
}

/*=head2 Geometry Plotting Commands

=item glBegin MODE

Begin plotting points of geometry type MODE.

=item glEnd

Stop plotting points.

=item glVertex X Y [Z] [w]

Plot a vertex at (X,Y), (X,Y,Z), or (X,Y,Z,W)

=item glNormal X Y Z

Specify the Normal vector for the next Vertex.

=item glTexCoord S T [R] [Q]

Specify texture coordinates to use for the next Vertex.

=item glColor COLORCODE | R G B [A]

Specify a color value to be used in the calculation of the color for the next vertex.
The color may be specified in HTML notation or as separate components.  See L</Colors>.

=item glMaterial FACE PNAME [PARAMS...]

Apply a Material property that affects how future vertices will be affected by various lighting
parameters.  See OpenGL documentation for details, but FACE and PNAME should be symbolic
constants, and PARAMS depends on PNAME.  If PARAMS are a color, you may use HTML notation.
See L</Colors>.

=cut */

COMMAND(glBegin, "i") {
	if (PointsInProgress) {
		parsed->errmsg= "multiple calls to glBegin without glEnd";
		return false;
	}
	glBegin(parsed->ints[0]);
	PointsInProgress= true;
	FrameInProgress= true;
	return true;
}

COMMAND(glEnd, "") {
	if (!PointsInProgress) {
		parsed->errmsg= "glEnd without glBegin";
		return false;
	}
	glEnd();
	PointsInProgress= false;
	return true;
}

COMMAND(glVertex, "ddd?d?") {
	switch (parsed->dCnt) {
	case 2: glVertex2dv(parsed->doubles); break;
	case 3: glVertex3dv(parsed->doubles); break;
	case 4: glVertex4dv(parsed->doubles); break;
	default:
		return false;
	}
	return true;
}

COMMAND(glNormal, "ddd") {
	glNormal3dv(parsed->doubles);
	return true;
}

COMMAND(glTexCoord, "dd?d?d?") {
	assert( parsed->dCnt >= 1 && parsed->dCnt <= 4 );
	switch (parsed->dCnt) {
	case 1: glTexCoord1dv(parsed->doubles); break;
	case 2: glTexCoord2dv(parsed->doubles); break;
	case 3: glTexCoord3dv(parsed->doubles); break;
	case 4: glTexCoord4dv(parsed->doubles); break;
	}
	return true;
}

COMMAND(glColor, "c") {
	glColor4fv(parsed->floats);
	return true;
}

COMMAND(glMaterial, "iib") {
	int face= parsed->ints[0], pname= parsed->ints[1];
	
	switch (pname) {
	case GL_COLOR_INDEXES:
		if (!ParseParams(&parsed->strings[0], "iii", parsed))
			return false;
		glMaterialiv(face, pname, parsed->ints+2);
		return true;
	case GL_AMBIENT:
	case GL_DIFFUSE:
	case GL_AMBIENT_AND_DIFFUSE:
	case GL_SPECULAR:
	case GL_EMISSION:
		if (!ParseParams(&parsed->strings[0], "c", parsed))
			return false;
		glMaterialfv(face, pname, parsed->floats);
		return true;
	case GL_SHININESS:
		if (!ParseParams(&parsed->strings[0], "f", parsed))
			return false;
		glMaterialfv(face, pname, parsed->floats);
		return true;
	default:
		snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Unsupported pname constant for glMaterial: %d", pname);
		parsed->errmsg= parsed->errmsg_buf;
	}
	return false;
}

/*=head2 Texture Commands

=item glBindTextue TARGET TEXTURENAME

Select the current texture object for purpose TARGET.  (TARGET is usually GL_TEXTURE_2D)

=item glTexParameter TARGET PNAME [PARAMS...]

Change an attribute of the texture object currently bount to TARGET.  PNAME is the name of
the attribute, and PARAMS depend on PNAME.  If PARAMS is a color, you may use HTML hex
notation; see L</Colors>.

=cut */

COMMAND(glBindTexture, "iT!") {
	glBindTexture(parsed->ints[0], parsed->objects[0]->Value);
	return true;
}
COMMAND(glTexParameter, "iib") {
	int target= parsed->ints[0], pname= parsed->ints[1];
	
	switch (pname) {
	/* one enum */
	case GL_TEXTURE_MIN_FILTER:
	case GL_TEXTURE_MAG_FILTER:
	case GL_TEXTURE_WRAP_S:
	case GL_TEXTURE_WRAP_T:
	case GL_TEXTURE_WRAP_R:
	#ifdef GL_TEXTURE_COMPARE_MODE
	case GL_TEXTURE_COMPARE_MODE:
	case GL_TEXTURE_COMPARE_FUNC:
	case GL_DEPTH_TEXTURE_MODE:
	case GL_GENERATE_MIPMAP:
	#endif
	/* one integer */
	case GL_TEXTURE_BASE_LEVEL:
	case GL_TEXTURE_MAX_LEVEL:
		if (!ParseParams(&parsed->strings[0], "i", parsed))
			return false;
		glTexParameteri(target, pname, parsed->ints[2]);
		return true;

	/* one float */
	#ifdef GL_TEXTURE_MIN_LOD
	case GL_TEXTURE_MIN_LOD:
	case GL_TEXTURE_MAX_LOD:
	#endif
	case GL_TEXTURE_PRIORITY:
		if (!ParseParams(&parsed->strings[0], "f", parsed))
			return false;
		glTexParameterf(target, pname, parsed->floats[0]);
		return true;

	/* one color */
	case GL_TEXTURE_BORDER_COLOR:
		if (!ParseParams(&parsed->strings[0], "c", parsed))
			return false;
		glTexParameterfv(target, pname, parsed->floats);
		return true;
	default:
		snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Unsupported pname constant for glTexparameter: %d", pname);
		parsed->errmsg= parsed->errmsg_buf;
	}
	return false;
}

/*=head2 Matrix Commands

=item glMatrixMode MODE

Select which matrix stack will be affected by future matrix commands.

=item glPushMatrix

Save a copy of the current matrix onto the current matrix stack.

=item glPopMatrix

Restore the previous saved matrix.

=item glLoadIdentity

Overwrite the current matrix with a matrix that has no effect on vertices.

=item glLoadMatrix I1 I2 I3 I4 J1 J2 J3 J4 K1 K2 K3 K4 L1 L2 L3 L4

Directly overwrite the matrix with 16 values.

=item glMultMatrix I1 I2 I3 I4 J1 J2 J3 J4 K1 K2 K3 K4 L1 L2 L3 L4

Multiply the current matrix by this specified matrix

=item glScale SCALE [SCALE_Y [SCALE_Z]]

When given one argument, scale the X, Y, and Z axis by the specified value.
When given two arguments, scale X and Y, leaving Z unchanged.
When given three arguments,scale X, Y, and Z.

=item glTranslate X Y [Z]

Apply a translation to the matrix.  The Z coordinate is optional and
defaults to 0.

=item glRotate DEGREES X Y Z

Rotate DEGREES around the axis defined by (X,Y,Z)

=item gluLookAt EYE_X EYE_Y EYE_Z CENTER_X CENTER_Y CENTER_Z UP_X UP_Y UP_Z

Change the current matrix to "look at" CENTER from EYE.

=item glViewport X Y WIDTH HEIGHT

Define the 2D region of the screen to be rendered by the current matrix.

=item glOrtho

Set up a projection matrix that maps the given coordinate values to the
edges of the viewport, and sets the near and far clipping plane.

=item glFrustum

Set up a projection matrix where the given coordinates are the edges of the
screen B<at> the near clipping plane, and scale proportionally as the Z
coordinate gets farther from the near plane.

=cut */

COMMAND(glMatrixMode, "i") {
	glMatrixMode((GLint) parsed->ints[0]);
	return true;
}
COMMAND(glPushMatrix, "") {
	glPushMatrix();
	return true;
}
COMMAND(glPopMatrix, "") {
	glPopMatrix();
	return true;
}
COMMAND(glLoadIdentity, "") {
	glLoadIdentity();
	return true;
}
COMMAND(glLoadMatrix, "dddddddddddddddd") {
	glLoadMatrixd(parsed->doubles);
	return true;
}
COMMAND(glMultMatrix, "dddddddddddddddd") {
	glMultMatrixd(parsed->doubles);
	return true;
}
COMMAND(glScale, "dd?d?") {
	assert( parsed->dCnt >= 1 && parsed->dCnt <= 3 );
	if (parsed->dCnt == 1)
		parsed->doubles[2]= parsed->doubles[1]= parsed->doubles[0];
	else if (parsed->dCnt == 2)
		parsed->doubles[2]= 1.0;
	glScaled(parsed->doubles[0], parsed->doubles[1], parsed->doubles[2]);
	return true;
}
COMMAND(glTranslate, "ddd?") {
	assert( parsed->dCnt >= 2 && parsed->dCnt <= 3 );
	if (parsed->dCnt == 2) parsed->doubles[2]= 0.0;
	glTranslated(parsed->doubles[0], parsed->doubles[1], parsed->doubles[2]);
	return true;
}
COMMAND(glRotate, "dddd") {
	glRotated(parsed->doubles[0], parsed->doubles[1], parsed->doubles[2], parsed->doubles[3]);
	return true;
}

COMMAND(glViewport, "iiii") {
	glViewport(parsed->ints[0], parsed->ints[1], parsed->ints[2], parsed->ints[3]);
	return true;
}
COMMAND(glOrtho, "dddddd") {
	glOrtho(parsed->doubles[0], parsed->doubles[1], parsed->doubles[2], parsed->doubles[3], parsed->doubles[4], parsed->doubles[5]);
	return true;
}
COMMAND(glFrustum, "dddddd") {
	glFrustum(parsed->doubles[0], parsed->doubles[1], parsed->doubles[2],
	          parsed->doubles[3], parsed->doubles[4], parsed->doubles[5]);
	return true;
}

/*=head2 Display List Commands

=item glNewList LISTNAME MODE

Begin recording a new display list, either creating or overwriting LISTNAME.
MODE can either be GL_COMPILE or GL_COMPILE_AND_EXECUTE.  LISTNAME can be any
string of text and is not limited to OpenGL's integer "names".

=item glEndList

End the recording.

=item glCallList LISTNAME

Replay a recorded list.

=cut */

COMMAND(glNewList, "L!i") {
	glNewList(parsed->objects[0]->Value, parsed->ints[0]);
	return true;
}
COMMAND(glEndList, "") {
	glEndList();
	return true;
}
COMMAND(glCallList, "L") {
	glCallList(parsed->objects[0]->Value);
	return true;
}

/*=head2 Quadric Commands

=item gluNewQuadric NAME

Quadric objects must be created before they can be used.

=item gluQuadricDrawStyle NAME DRAW

Set the DRAW style of the NAMEd quadric: GLU_FILL, GLU_LINE, GLU_SILHOUETTE, GLU_POINT

=item gluQuadricNormals NAME NORMAL

Set the type of NORMALs to calculate when using the NAMEd quadric: GLU_NONE, GLU_FLAT, GLU_SMOOTH

=item gluQuadricOrientation NAME ORIENTATION

Set the in/out ORIENTATION of polygons when using the NAMEd quadric: GLU_OUTSIDE, GLU_INSIDE

=item gluQuadricTexture NAME MODE

Set whether or not to generate texture coordinates when drawing with the NAMEd quadric: GLU_TRUE, GLU_FALSE

=item gluCylinder NAME BASE TOP HEIGHT SLICES STACKS

Draw a cylinder (or cone) with BASE radius at z=0 and TOP radius at z=height, traveling HEIGHT
distance on the Z axis, composed of n=SLICES polygons radially around the body and n=STACKS
polygons along the height.  NAME selects a set of quadric parameters for the polygons.

=item gluSphere NAME RADIUS SLICES STACKS

Draw a sphere of RADIUS around the origin, composed of n=SLICES polygons radially around the
Z axis and n=STACKS polygons along the z axis.

=item gluDisk NAME INNER OUTER SLICES LOOPS

Draw a flat disk (C<inner = 0>) or donought (C<<inner > 0>>) around the Z axis with the given
OUTER radius and INNER radius, composed of SLICES polygons radially around Z and LOOPS
concentric rings.

=item gluPartialDisk NAME INNER OUTER SLICES LOOPS START SWEEP

Same as above, but only from START degrees around Z axis until and SWEEP degrees afterward.

=cut */

COMMAND(gluLookAt, "ddddddddd") {
	gluLookAt(
		parsed->doubles[0], parsed->doubles[1], parsed->doubles[2],
		parsed->doubles[3], parsed->doubles[4], parsed->doubles[5],
		parsed->doubles[6], parsed->doubles[7], parsed->doubles[8]
	);
	return true;
}
COMMAND(gluNewQuadric, "Q!") {
	/* auto-create handled by ParseParams */
	return true;
}
COMMAND(gluQuadricDrawStyle, "Qi") {
	gluQuadricDrawStyle((GLUquadric*) parsed->objects[0]->Data, parsed->ints[0]);
	return true;
}
COMMAND(gluQuadricNormals, "Qi") {
	gluQuadricNormals((GLUquadric*) parsed->objects[0]->Data, parsed->ints[0]);
	return true;
}
COMMAND(gluQuadricOrientation, "Qi") {
	gluQuadricOrientation((GLUquadric*) parsed->objects[0]->Data, parsed->ints[0]);
	return true;
}
COMMAND(gluQuadricTexture, "Qi") {
	gluQuadricTexture((GLUquadric*) parsed->objects[0]->Data, parsed->ints[0]);
	return true;
}
COMMAND(gluCylinder, "Qdddii") {
	gluCylinder((GLUquadric*) parsed->objects[0]->Data,
		parsed->doubles[0], parsed->doubles[1], parsed->doubles[2],
		parsed->ints[0], parsed->ints[1]);
	FrameInProgress= true;
	return true;
}
COMMAND(gluSphere, "Qdii") {
	gluSphere((GLUquadric*) parsed->objects[0]->Data, parsed->doubles[0],
		parsed->ints[0], parsed->ints[1]);
	FrameInProgress= true;
	return true;
}
COMMAND(gluDisk, "Qddii") {
	gluDisk((GLUquadric*) parsed->objects[0]->Data, parsed->doubles[0], parsed->doubles[1],
		parsed->ints[0], parsed->ints[1]);
	FrameInProgress= true;
	return true;
}
COMMAND(gluPartialDisk, "Qddiidd") {
	gluPartialDisk((GLUquadric*) parsed->objects[0]->Data, parsed->doubles[0], parsed->doubles[1],
		parsed->ints[0], parsed->ints[1], pars
Download .txt
gitextract_pgnbvikv/

├── .gitignore
├── Changes
├── LICENSE
├── Makefile
├── README.md
├── configure
├── doc/
│   ├── Design.txt
│   ├── Fonts.html
│   └── Intro.html
├── script/
│   ├── Makefile.in
│   ├── build-cmdhash.pl
│   ├── build-consthash.pl
│   ├── build-constlist.sh
│   ├── build-dist.pl
│   ├── build-manual.pl
│   ├── build-version.sh
│   ├── config.h.in
│   ├── configure.ac
│   └── dev-rules.mak
├── share/
│   ├── CmdlineGL.lib
│   ├── examples/
│   │   ├── BrowseFonts.sh
│   │   ├── FlightSim.sh
│   │   ├── ImgCube.sh
│   │   ├── ModelViewer.sh
│   │   ├── Pyramids.sh
│   │   ├── Robot.sh
│   │   └── SpinText.sh
│   ├── lib-bash/
│   │   ├── CmdlineGL.lib
│   │   ├── Cube.lib
│   │   ├── Geom.lib
│   │   ├── LaserBeam.lib
│   │   ├── LinInterpolate.lib
│   │   ├── ModelViewer.lib
│   │   ├── RenderLoop.lib
│   │   ├── Ship.lib
│   │   ├── Timing.lib
│   │   └── Trig.lib
│   └── lib-sh/
│       └── CmdlineGL.lib
├── src/
│   ├── ConstList.Win32.txt
│   ├── ConstList.works_for_me
│   ├── Contained_RBTree.c
│   ├── Contained_RBTree.h
│   ├── Font.c
│   ├── Font.h
│   ├── Global.c
│   ├── Global.h
│   ├── ImageLoader.c
│   ├── ImageLoader.h
│   ├── ParseGL.c
│   ├── ParseGL.h
│   ├── ProcessInput.c
│   ├── ProcessInput.h
│   ├── Server.c
│   ├── Server.h
│   ├── SymbolHash.c
│   ├── SymbolHash.h
│   ├── Version.h
│   ├── manual.head.pod
│   └── manual.tail.pod
└── test/
    └── 01-version.t
Download .txt
SYMBOL INDEX (75 symbols across 11 files)

FILE: src/Contained_RBTree.c
  function RBTreeNode_IsSentinel (line 30) | bool RBTreeNode_IsSentinel( RBTreeNode *Node ) {
  function RBTreeNode_Init (line 34) | void RBTreeNode_Init( RBTreeNode* Node ) {
  function RBTree_InitRootSentinel (line 38) | void RBTree_InitRootSentinel( RBTreeNode *RootSentinel ) {
  function RBTree_Clear (line 46) | void RBTree_Clear( RBTreeNode *RootSentinel ) {
  function RBTree_Add (line 55) | bool RBTree_Add( RBTreeNode *RootSentinel, RBTreeNode* NewNode, RBTree_i...
  function RBTreeNode (line 96) | RBTreeNode* RBTree_Find( const RBTreeNode *RootSentinel, const void* Sea...
  function RBTreeNode (line 107) | RBTreeNode* RBTree_GetLeftmost( RBTreeNode* Node ) {
  function RBTreeNode (line 113) | RBTreeNode* RBTree_GetRightmost( RBTreeNode* Node ) {
  function RBTreeNode (line 119) | RBTreeNode* RBTree_GetPrev( RBTreeNode* Node ) {
  function RBTreeNode (line 141) | RBTreeNode* RBTree_GetNext( RBTreeNode* Node ) {
  function RBTree_RightSide_RightRotate (line 164) | void RBTree_RightSide_RightRotate( RBTreeNode* Node ) {
  function RBTree_LeftSide_LeftRotate (line 180) | void RBTree_LeftSide_LeftRotate( RBTreeNode* Node ) {
  function RBTree_LeftSide_RightRotate (line 196) | void RBTree_LeftSide_RightRotate( RBTreeNode* Node ) {
  function RBTree_RightSide_LeftRotate (line 212) | void RBTree_RightSide_LeftRotate( RBTreeNode* Node ) {
  function RBTree_Balance (line 229) | void RBTree_Balance( RBTreeNode* Current ) {
  function RBTree_Prune (line 291) | bool RBTree_Prune( RBTreeNode* Current ) {
  function RBTree_PruneLeaf (line 329) | void RBTree_PruneLeaf( RBTreeNode* Node ) {

FILE: src/Contained_RBTree.h
  type RBTreeNode (line 27) | typedef struct RBTreeNode_t {
  type RBTree (line 123) | typedef struct RBTree_t {

FILE: src/Font.c
  function InitNamedFont (line 52) | bool InitNamedFont(SymbVarEntry *sym, FTGLfont *font) {

FILE: src/Global.c
  function WinPerror (line 5) | void WinPerror(char *msg) {
  function DebugMsg (line 21) | void DebugMsg(char *msg, ...) {

FILE: src/Global.h
  type ParseParamsResult (line 15) | struct ParseParamsResult

FILE: src/ImageLoader.c
  function UsableByGL (line 9) | bool UsableByGL(SDL_Surface *Img) {
  function SDL_Surface (line 35) | SDL_Surface* LoadImg(const char *FName) {
  function else (line 74) | else if (fmt->Bmask == 0xFF000000 && fmt->Gmask == 0x00FF0000 && fmt->Rm...

FILE: src/ProcessInput.c
  function InitLineBuffer (line 95) | void InitLineBuffer(HANDLE FileHandle /*, LineBuffer *this */) {
  function ShiftBuffer (line 104) | void ShiftBuffer(/* LineBuffer *this */) {
  function ProcessCommand (line 209) | int ProcessCommand(char *Line) {
  function ParseParams (line 259) | bool ParseParams(char **line, const char *format, struct ParseParamsResu...
  function ParseInt (line 384) | bool ParseInt(char **line, struct ParseParamsResult *parsed) {
  function ParseFloat (line 425) | bool ParseFloat(char **line, struct ParseParamsResult *parsed) {
  function ParseDouble (line 435) | bool ParseDouble(char **line, struct ParseParamsResult *parsed) {
  function ParseToken (line 469) | bool ParseToken(char **line, struct ParseParamsResult *parsed) {
  function CaptureRemainder (line 484) | bool CaptureRemainder(char **line, struct ParseParamsResult *parsed) {
  function ParseColor (line 496) | bool ParseColor(char **line, struct ParseParamsResult *parsed) {
  function ParseHexColor (line 530) | bool ParseHexColor(const char* Text, float Result[4]) {
  function ParseHexByte (line 544) | bool ParseHexByte(const char *str, float *Result) {
  function FileExists (line 560) | bool FileExists(const char *Name) {

FILE: src/ProcessInput.h
  type SymbVarEntry_t (line 21) | struct SymbVarEntry_t
  type ParseParamsResult (line 36) | struct ParseParamsResult {
  type ParseParamsResult (line 47) | struct ParseParamsResult
  type ParseParamsResult (line 49) | struct ParseParamsResult
  type ParseParamsResult (line 50) | struct ParseParamsResult
  type ParseParamsResult (line 52) | struct ParseParamsResult
  type ParseParamsResult (line 53) | struct ParseParamsResult
  type ParseParamsResult (line 54) | struct ParseParamsResult
  type ParseParamsResult (line 56) | struct ParseParamsResult

FILE: src/Server.c
  type CmdlineOptions (line 13) | typedef struct {
  function main (line 56) | int main(int Argc, char**Args) {
  function SetParamDefaults (line 163) | void SetParamDefaults(CmdlineOptions *Options) {
  function PrintMissingParamMessage (line 170) | void PrintMissingParamMessage(char* ArgName, CmdlineOptions *Options) {
  function ReadParams (line 175) | void ReadParams(char **Args, CmdlineOptions *Options) {
  function PrintUsage (line 291) | void PrintUsage(bool error) {
  function CheckInput (line 410) | void CheckInput() {
  function CheckSDLEvents (line 431) | void CheckSDLEvents() {
  function FinishResize (line 482) | void FinishResize() {
  function InitGL (line 493) | void InitGL(int w, int h) {
  function EmitKey (line 533) | void EmitKey(const SDL_KeyboardEvent *E) {
  function encode_utf8 (line 551) | void encode_utf8(char *buffer, unsigned codepoint) {

FILE: src/SymbolHash.c
  function DumpConstList (line 15) | void DumpConstList(FILE* DestStream) {
  function DumpCommandList (line 28) | void DumpCommandList(FILE* DestStream) {
  function CalcHash (line 37) | unsigned int CalcHash(const char* str) {
  function InitSymbVarEntry (line 44) | void InitSymbVarEntry(SymbVarEntry *Entry, const char* Name, int Type) {
  function SymbVar_inorder_func (line 53) | bool SymbVar_inorder_func(const void* ObjA, const void* ObjB) {
  type SymbVarSearchKey (line 67) | typedef struct {
  function SymbVar_compare_func (line 73) | int SymbVar_compare_func(const void* SearchKey, const void* Object) {
  function SymbVarEntry (line 86) | SymbVarEntry *GetSymbVar(const char *Name, int Type) {
  function SymbVarEntry (line 102) | SymbVarEntry *CreateSymbVar(const char *Name, int Type) {
  function DeleteSymbVar (line 114) | void DeleteSymbVar(SymbVarEntry *Entry) {
  function DumpVarList (line 119) | void DumpVarList(FILE* DestStream) {

FILE: src/SymbolHash.h
  type ParseParamsResult (line 6) | struct ParseParamsResult
  type CmdListEntry (line 8) | typedef struct {
  type IntConstListEntry (line 14) | typedef struct {
  type SymbVarEntry (line 23) | typedef struct SymbVarEntry_t {
Condensed preview — 60 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (258K chars).
[
  {
    "path": ".gitignore",
    "chars": 117,
    "preview": "/build/\n/src/*.autogen.*\n/src/ConstList.txt\n/script/configure\n/script/config-defs.h.in\n/script/autom4te.cache\n/dist/\n"
  },
  {
    "path": "Changes",
    "chars": 1035,
    "preview": "Version 2.1.0 - 2024-01-02\n  * New option \"--geometry\"\n  * Documentation improvements\n\nVersion 2.0.0 - 2018-03-25\n  * Re"
  },
  {
    "path": "LICENSE",
    "chars": 1508,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, M Conrad\nAll rights reserved.\n\nRedistribution and use in source and binary for"
  },
  {
    "path": "Makefile",
    "chars": 291,
    "preview": "MAKE = make\nPROVE = prove\nPERL = perl\n\nall: build\n\t$(MAKE) -C build all\n\nclean: build\n\t$(MAKE) -C build clean\n\ninstall: "
  },
  {
    "path": "README.md",
    "chars": 2151,
    "preview": "CmdlineGL\n=========\n\nCmdlineGL is an interpreter for a \"text-friendly\" variation of a subset of the\nOpenGL 1.4 API, Glut"
  },
  {
    "path": "configure",
    "chars": 628,
    "preview": "#! /bin/sh\n\n# This configure script is just a place-holder that sets up an out-of-tree\n# build in a directory named \"bui"
  },
  {
    "path": "doc/Design.txt",
    "chars": 419,
    "preview": "\n\n\tNew design:\n\n\t\tServer.c\n\t\t\tEverything to do with setting up the window and watching file descriptors.\n\n\t\tProcessInput"
  },
  {
    "path": "doc/Fonts.html",
    "chars": 1778,
    "preview": "<html>\n<head>\n\t<title>Working with CmdlineGL Fonts</title>\n</head>\n\n<body>\n\t<h3>Preface</h3>\n\t<p>In CmdlineGL, I wanted "
  },
  {
    "path": "doc/Intro.html",
    "chars": 10742,
    "preview": "<html>\n<head>\n\t<style>\n\tBODY { background-color:black; color:#55FF55; font-family:\"Courier New\", monospace, sans-serif; "
  },
  {
    "path": "script/Makefile.in",
    "chars": 3864,
    "preview": "SHELL = /bin/sh\nPERL = perl\nPOD2MAN = pod2man\nPROVE = prove\nGZIP = gzip\nINSTALL = install\nCURRENT_UNIX_TIMESTAMP = $(she"
  },
  {
    "path": "script/build-cmdhash.pl",
    "chars": 3364,
    "preview": "#! /usr/bin/env perl\n\n=head1 DESCRIPTION\n\nGenerates static hash table of commands by parsing C source.\n\n=cut\n\nuse strict"
  },
  {
    "path": "script/build-consthash.pl",
    "chars": 3172,
    "preview": "#! /usr/bin/env perl\n\n=head1 DESCRIPTION\n\nGenerates static hash table of commands by parsing C source.\n\n=cut\n\nuse strict"
  },
  {
    "path": "script/build-constlist.sh",
    "chars": 943,
    "preview": "#! /bin/sh\nset -eu\n\n[ -n \"${CPP}\" ] && [ -n \"$PROJROOT\" ] \\\n\t|| { echo \"Require variables PROJROOT, CPP, CFLAGS, CPPFLAG"
  },
  {
    "path": "script/build-dist.pl",
    "chars": 2325,
    "preview": "#! /usr/bin/perl\nuse strict;\nuse warnings;\nopen(STDERR, '>&STDOUT');\n\n# Run a command like 'make', but capture the outpu"
  },
  {
    "path": "script/build-manual.pl",
    "chars": 3737,
    "preview": "#! /usr/bin/env perl\nuse strict;\nuse warnings;\nuse Pod::Man;\nuse Getopt::Long;\n\nGetOptions(\n\t'as=s'      => \\(my $opt_as"
  },
  {
    "path": "script/build-version.sh",
    "chars": 1520,
    "preview": "#! /bin/sh\n\n[ -n \"$PROJROOT\" ] || { echo \"Require environment var PROJROOT\" >&2; exit 1; }\n\necho \" -> Checking Version\" "
  },
  {
    "path": "script/config.h.in",
    "chars": 1735,
    "preview": "/* This header pulls in everything system-dependent.  No other files in the project\n * should depend on any header other"
  },
  {
    "path": "script/configure.ac",
    "chars": 2077,
    "preview": "AC_PREREQ([2.68])\nAC_INIT([CmdlineGL])\nAC_CONFIG_SRCDIR([config.h.in])\nAC_CONFIG_HEADER([config-defs.h])\n\nAC_ARG_ENABLE("
  },
  {
    "path": "script/dev-rules.mak",
    "chars": 669,
    "preview": "# These rules are only needed for authoring CmdlineGL and don't need to be\n# active in the release tarball.  I wanted th"
  },
  {
    "path": "share/CmdlineGL.lib",
    "chars": 1159,
    "preview": "#! /bin/bash\n# This script is a trampoline to detect shells and versions\n# ...but only Bash 3+ is supported at the momen"
  },
  {
    "path": "share/examples/BrowseFonts.sh",
    "chars": 2001,
    "preview": "#! /bin/bash\n#\n#  This example displays each font in the /usr/share directory by\n#  rendering it as an extruded font.  I"
  },
  {
    "path": "share/examples/FlightSim.sh",
    "chars": 8059,
    "preview": "#! /bin/bash\n[ -n \"$BASH_VERSION\" ] || exec bash $0\n\n# Define our handy die function\ndie() { echo \"$@\" >&2; exit 2; }\nse"
  },
  {
    "path": "share/examples/ImgCube.sh",
    "chars": 2114,
    "preview": "#! /bin/bash\n[ -n \"$BASH_VERSION\" ] || exec bash $0\nset -u\n# Define our handy die function\ndie() { echo \"$@\" >&2; exit 2"
  },
  {
    "path": "share/examples/ModelViewer.sh",
    "chars": 1634,
    "preview": "#! /bin/bash\n[ -n \"$BASH_VERSION\" ] || exec bash $0 \"$@\"\nset -u\n# Define our handy die function\ndie() { echo \"$@\" >&2; e"
  },
  {
    "path": "share/examples/Pyramids.sh",
    "chars": 4074,
    "preview": "#! /bin/bash\n[ -n \"$BASH_VERSION\" ] || exec bash $0\n\n# Define our handy die function\ndie() { echo \"$@\" >&2; exit 2; }\n\ns"
  },
  {
    "path": "share/examples/Robot.sh",
    "chars": 13476,
    "preview": "#! /bin/bash\n[ -n \"$BASH_VERSION\" ] || exec bash $0\nset -u\n# Define our handy die function\ndie() { echo \"$@\" >&2; exit 2"
  },
  {
    "path": "share/examples/SpinText.sh",
    "chars": 2069,
    "preview": "#! /bin/bash\n#\n#  This is an extremely minimal example which rotates a string of text\n#  rendered in 3D.  The only compl"
  },
  {
    "path": "share/lib-bash/CmdlineGL.lib",
    "chars": 6144,
    "preview": "# CmdlineGL Base API\n#--------------------------------------\n# Functions:\n#   CmdlineGL_LoadLib LIBRARY [LIBRARY...]\n#  "
  },
  {
    "path": "share/lib-bash/Cube.lib",
    "chars": 1202,
    "preview": "# Simple 2x2x2 cube model, with colored faces, which auto-compiles to a display list\n\nCube_InitGfx() {\n\tglNewList Cube G"
  },
  {
    "path": "share/lib-bash/Geom.lib",
    "chars": 6314,
    "preview": "# Library of some stuff mostly to do with cartesian points, vectors\n# and coordinate-systems.\n#\nCmdlineGL_LoadLib Trig\n\n"
  },
  {
    "path": "share/lib-bash/LaserBeam.lib",
    "chars": 540,
    "preview": "LaserBeam_InitGfx() {\n\tglNewList LaserBeam GL_COMPILE\n\n\t# Front\n\tglBegin GL_TRIANGLE_FAN\n\tglColor 1 0.7 0.7\n\tglVertex 0 "
  },
  {
    "path": "share/lib-bash/LinInterpolate.lib",
    "chars": 1881,
    "preview": "# Title: LinInterpolate.sh\n# Author: Michael Conrad\n# Date:\t2005-05-29\n#\n\n## Perform a linear interpolation on a table o"
  },
  {
    "path": "share/lib-bash/ModelViewer.lib",
    "chars": 3339,
    "preview": "# ModelView API\n#--------------------------------------\n#\n# This module sets up the translations and handles user input "
  },
  {
    "path": "share/lib-bash/RenderLoop.lib",
    "chars": 1950,
    "preview": "CmdlineGL_LoadLib Timing\n\n# RenderLoop API\n#--------------------------------------\n# Functions:\n#   RenderLoop_Run\n# Cal"
  },
  {
    "path": "share/lib-bash/Ship.lib",
    "chars": 4905,
    "preview": "# This is the model for the player's ship.\n# It was originally designed facing the -Z axis, but then I decided to make\n#"
  },
  {
    "path": "share/lib-bash/Timing.lib",
    "chars": 3135,
    "preview": "# Prevent multiple inclusion\n[[ -z ${TIMING_LIB+x} ]] || return 0\nTIMING_LIB=1;\n\n# Timing API\n#-------------------------"
  },
  {
    "path": "share/lib-bash/Trig.lib",
    "chars": 4655,
    "preview": "# Title: Trig.sh\n# Author: Michael Conrad\n# Date: 2005-05-29\n#\n# Some useful trig functions.\n# All are approximated usin"
  },
  {
    "path": "share/lib-sh/CmdlineGL.lib",
    "chars": 1175,
    "preview": "CmdlineGL_setup_fifo() {\n\t# Create a FIFO unless one is already active\n\tif [ \"z$CMDLINEGL_FIFO_DIR\" = \"z\" ]; then\n\t\tCMDL"
  },
  {
    "path": "src/ConstList.Win32.txt",
    "chars": 12087,
    "preview": "GL_VERSION_1_1\nGL_ACCUM\nGL_LOAD\nGL_RETURN\nGL_MULT\nGL_ADD\nGL_NEVER\nGL_LESS\nGL_EQUAL\nGL_LEQUAL\nGL_GREATER\nGL_NOTEQUAL\nGL_G"
  },
  {
    "path": "src/ConstList.works_for_me",
    "chars": 20110,
    "preview": "GL_VERSION_1_1\nGL_VERSION_1_2\nGL_VERSION_1_3\nGL_ARB_imaging\nGL_FALSE\nGL_TRUE\nGL_2_BYTES\nGL_3_BYTES\nGL_4_BYTES\nGL_POINTS\n"
  },
  {
    "path": "src/Contained_RBTree.c",
    "chars": 15018,
    "preview": "/******************************************************************************\\\n*   Contained_RBTree.cpp            by:"
  },
  {
    "path": "src/Contained_RBTree.h",
    "chars": 6697,
    "preview": "/******************************************************************************\\\n*   Contained_RBTree.h              by:"
  },
  {
    "path": "src/Font.c",
    "chars": 4628,
    "preview": "#include <config.h>\n#include \"Global.h\"\n#include \"ParseGL.h\"\n#include \"ProcessInput.h\"\n#include \"SymbolHash.h\"\n\n#ifdef H"
  },
  {
    "path": "src/Font.h",
    "chars": 38,
    "preview": "#ifndef FONT_H\n#define FONT_H\n\n#endif\n"
  },
  {
    "path": "src/Global.c",
    "chars": 523,
    "preview": "#include <config.h>\n#include \"Global.h\"\n\n#ifdef _WIN32\nvoid WinPerror(char *msg) {\n\tchar* BuffPtr= NULL;\n\tFormatMessage("
  },
  {
    "path": "src/Global.h",
    "chars": 518,
    "preview": "#ifndef GLOBAL_H\n#define GLOBAL_H\n\n/* ------------------------------------\n * Settings for the program.\n */\n\n#ifndef MAX"
  },
  {
    "path": "src/ImageLoader.c",
    "chars": 4347,
    "preview": "#define INCLUDE_GL\n#define INCLUDE_SDL\n#include <config.h>\n#include \"Global.h\"\n#include \"ParseGL.h\"\n#include \"ImageLoade"
  },
  {
    "path": "src/ImageLoader.h",
    "chars": 54,
    "preview": "#ifndef IMAGE_LOADER_H\n#define IMAGE_LOADER_H\n\n#endif\n"
  },
  {
    "path": "src/ParseGL.c",
    "chars": 17931,
    "preview": "#define INCLUDE_GL\n#include <config.h>\n#include \"Global.h\"\n#include \"ParseGL.h\"\n#include \"Server.h\"\n#include \"SymbolHash"
  },
  {
    "path": "src/ParseGL.h",
    "chars": 220,
    "preview": "#ifndef PARSEGL_H\n#define PARSEGL_H\n\n#include \"Global.h\"\n\nextern bool PointsInProgress; // Whenever glBegin is active, u"
  },
  {
    "path": "src/ProcessInput.c",
    "chars": 16423,
    "preview": "#define INCLUDE_GL\n#include <config.h>\n\n#include \"ProcessInput.h\"\n#include \"ParseGL.h\"\n#include \"SymbolHash.h\"\n\n#define "
  },
  {
    "path": "src/ProcessInput.h",
    "chars": 1940,
    "preview": "#ifndef PROCESS_INPUT_H\n#define PROCESS_INPUT_H\n\n#include \"Global.h\"\n\n#define P_SUCCESS 0\n#define P_IGNORE 1\n#define P_C"
  },
  {
    "path": "src/Server.c",
    "chars": 15032,
    "preview": "#define INCLUDE_GL\n#define INCLUDE_SDL\n#include <config.h>\n#include \"Global.h\"\n#include \"Version.h\"\n#include \"Server.h\"\n"
  },
  {
    "path": "src/Server.h",
    "chars": 119,
    "preview": "#ifndef SERVER_H\n#define SERVER_H\n\n#include \"Global.h\"\n\nextern bool PendingResize;\nextern void FinishResize();\n\n#endif\n"
  },
  {
    "path": "src/SymbolHash.c",
    "chars": 3660,
    "preview": "#include <config.h>\n#include \"Global.h\"\n#include \"SymbolHash.h\"\n\nconst char *SymbVarTypeName[]= { \"?\", \"Display List\", \""
  },
  {
    "path": "src/SymbolHash.h",
    "chars": 1156,
    "preview": "#ifndef SYMBOL_HASH_H\n#define SYMBOL_HASH_H\n\n#include \"ProcessInput.h\"\n\nstruct ParseParamsResult;\n\ntypedef struct {\n\tcon"
  },
  {
    "path": "src/Version.h",
    "chars": 237,
    "preview": "#ifndef VERSION_H\n#define VERSION_H\n\nextern const int CGLVER_Major;\nextern const int CGLVER_Minor;\nextern const int CGLV"
  },
  {
    "path": "src/manual.head.pod",
    "chars": 2825,
    "preview": "=head1 NAME\n\nCmdlineGL - Interpreter for subset of OpenGL 1.4 API\n\n=head1 SYNOPSIS\n\n  CmdlineGL [OPTIONS] <opengl_comman"
  },
  {
    "path": "src/manual.tail.pod",
    "chars": 2984,
    "preview": "=head1 EXAMPLE\n\nThe typical render loop, taking advantage of CmdlineGL's ability to\nsynchronize frames to a clock, looks"
  },
  {
    "path": "test/01-version.t",
    "chars": 84,
    "preview": "#! /usr/bin/env perl\nuse strict;\nuse warnings;\nuse Test::More;\n\npass;\ndone_testing;\n"
  }
]

About this extraction

This page contains the full source code of the nrdvana/CmdlineGL GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 60 files (232.9 KB), approximately 75.2k tokens, and a symbol index with 75 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!