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 ================================================ Working with CmdlineGL Fonts

Preface

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.

This font system allows you to "make a font" 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.

Using the commands

I added 2 "GL-ish" commands to work with fonts: cglNewFont, and cglText.

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 valid name, and the file name is any valid system-dependant file path.

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. ================================================ FILE: doc/Intro.html ================================================

Introducing

OpenGL Bindings for BASH

"What???"

A while back, I came accross a game called Frozen Bubble, which is a Snood 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 Mike's Abuse of Technology #6....

"Oh, God!  No!!"

  <-- (common actual quote)

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!

"BUT WHY, DAMNIT? WHY??"

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.
(tribute to Bob Zimbinski [1])

"But how? Bash doesn't support modules..."

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.

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.

"This sounds Bad and Wrong"

I'd argue for Bad and Right. Keep reading.

"So, how bad does the performance suck?"

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 Red/Black Tree to manage the names of objects like display lists, quadrics, textures, and fonts.

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 ;-)

"And you actually wrote a game using this kluge?"

Well, mostly just demos, but a game engine of sorts, yes.
(I never finished the flight simulator)

Lets review some of the obstacles involved with a real-time game, and our old friend/nemesis Bash:

"How much time did you waste on this??"

Less time than I wasted on World of Warcraft

"So, what can I do to... 'Repay' you for this?"

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.

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.

"Where's the Windows version?"

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. ;-)

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.

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.

Credits

================================================ 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 $@.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 () { # 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 < #include #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 () { $_ =~ /(\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 <> $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= < CmdlineGL END $html =~ s,.*?,$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 <&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 < /* ------------------------------------ * Basics */ #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # include #else # ifndef _GNU_SOURCE # define _GNU_SOURCE # endif # include # include # include # include # include # include #endif #include #include #include #include #include #include #include #include /* ------------------------------------ * 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 #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/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$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 /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 /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 /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 /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 #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(Node)); } //inline const RBTreeNode* RBTree_GetRightmost( const RBTreeNode* Node ) { return RBTree_GetRightmost(const_cast(Node)); } //inline const RBTreeNode* RBTree_GetPrev ( const RBTreeNode* Node ) { return RBTree_GetPrev (const_cast(Node)); } //inline const RBTreeNode* RBTree_GetNext ( const RBTreeNode* Node ) { return RBTree_GetNext (const_cast(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 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; }; 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 #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 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 #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 #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 #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. =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. =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. =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 =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. =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. =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. =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 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) or donought (C< 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], parsed->doubles[2], parsed->doubles[3]); FrameInProgress= true; return true; } ================================================ FILE: src/ParseGL.h ================================================ #ifndef PARSEGL_H #define PARSEGL_H #include "Global.h" extern bool PointsInProgress; // Whenever glBegin is active, until glEnd extern bool FrameInProgress; // True after any gl command, until cglSwapBuffers #endif ================================================ FILE: src/ProcessInput.c ================================================ #define INCLUDE_GL #include #include "ProcessInput.h" #include "ParseGL.h" #include "SymbolHash.h" #define READ_BUFFER_SIZE 1024 /* CmdlineGL supports fixed-point numbers on stdin, by multiplying * all float values by this multiplier. Naturally, it defaults to 1.0 */ double FixedPtMultiplier= 1.0; double DivisorStack[16]; int DivisorStackPos= -1; const int DivisorStackMax= sizeof(DivisorStack)/sizeof(double) - 1; /*=head2 Default-Divisor Commands =item cglPushDivisor DIVISOR All future floating-point numbers receive this implied "/DIVISOR". This does not replace a divisor that is manually specified. For example, cglPushDivisor 100 glRotate 12/1 100 100 100 becomes glRotated(12, 1, 1, 1); =item cglPopDivisor Clear the current default divisor, returning to whatever default was set before that. The initial default is 1. =cut*/ COMMAND(cglPushDivisor, "t") { char *EndPtr; double newval; if (DivisorStackPos >= DivisorStackMax) { parsed->errmsg= "divisor stack overflow"; return false; } // We don't want to scale the new scale... so don't use ParseParams("d"). newval= strtod(parsed->strings[0], &EndPtr); if (*EndPtr != '\0') { parsed->errmsg= "expected Float"; return false; } DivisorStack[++DivisorStackPos]= newval; FixedPtMultiplier= 1.0 / newval; return true; } COMMAND(cglPopDivisor, "") { if (DivisorStackPos < 0) { parsed->errmsg= "divisor stack underflow"; return false; } FixedPtMultiplier= (--DivisorStackPos >= 0)? 1.0 / DivisorStack[DivisorStackPos] : 1.0; return true; } /* Below is "class LineBuffer". * I had to write my own because I wanted a readline function that wouldn't * block, and wouldn't return a partial string. * * There's only one used in the whole prog, and I see it staying that way, so I * 'optimized' a bit by making it global. * * If it ever becomes necessary to make more than one, uncomment and propogate. * Also, it will then be necessary to make a constructor function, and call it * before each gets used the first time. */ //typedef struct LineBuffer_t { char ReadBuffer[READ_BUFFER_SIZE]; char const *StopPos; char *Pos; char *DataPos; char *LineStart; #ifndef _WIN32 int fd; #else HANDLE fd; #endif //} LineBuffer; #ifndef _WIN32 void InitLineBuffer(int FileHandle /*, LineBuffer *this */) { #else void InitLineBuffer(HANDLE FileHandle /*, LineBuffer *this */) { #endif fd= FileHandle; StopPos= ReadBuffer+READ_BUFFER_SIZE; Pos= ReadBuffer; DataPos= ReadBuffer; LineStart= ReadBuffer; } void ShiftBuffer(/* LineBuffer *this */) { int shift= LineStart - ReadBuffer; if (DataPos != LineStart) memmove(ReadBuffer, LineStart, DataPos - LineStart); DataPos-= shift; Pos-= shift; LineStart= ReadBuffer; } char* ReadLine(/* LineBuffer *this */) { int red; char *Result; if (LineStart != ReadBuffer && DataPos - LineStart < 16) ShiftBuffer(); do { // do we need more? if (Pos == DataPos) { // do we need to shift? if (DataPos == StopPos) { // can we shift? if (LineStart != ReadBuffer) ShiftBuffer(); else { // Full buffer. Abort, and reset the buffer pointers. LineStart= DataPos= Pos= ReadBuffer; fprintf(stderr, "Input line too long\n"); return NULL; } } #ifndef _WIN32 red= read(fd, DataPos, StopPos-DataPos); if (red <= 0) return NULL; #else // no non-blocking mode, so we need to test the status of the object if (WaitForSingleObject(fd, 0) == WAIT_OBJECT_0) success= ReadFile(fd, DataPos, StopPos-DataPos, &red, NULL); else return NULL; if (!success || red<=0) return NULL; #endif DataPos+= red; } while (Pos < DataPos && *Pos != '\n') Pos++; } while (*Pos != '\n'); *Pos= '\0'; if (Pos > LineStart) if (Pos[-1] == '\r') Pos[-1]= 0; Result= LineStart; LineStart= ++Pos; return Result; } char * next_token(char **input) { char q, *out, *in, *rewrite; if (!input) return NULL; in= *input; /* skip delim characters */ while (*in == ' ' || *in == '\t') in++; /* quoting support */ if (*in == '\'' || *in == '"') { q= *in++; out= rewrite= in; while (*in && *in != q) { if (*in == '\\') { switch (* ++in) { case 'n': *in= '\n'; break; case 'r': *in= '\r'; break; case '\0': --in; break; /* don't run over end of string */ default: break; /* every other value remains itself */ } } *rewrite++ = *in++; } if (*in) in++; /* advance past the end-quote, if not end of string */ *rewrite= '\0'; /* always consider a token found, even if empty or missing end quote */ } /* else just look for end of token */ else { out= in; while (*in && *in != ' ' && *in != '\t') in++; if (*in) *in++= '\0'; if (!*out) out= NULL; /* return null unless found a token */ } while (*in == ' ' || *in == '\t') in++; *input= in; /* give caller back the modified pointer */ return out; } char *sanitize_for_print(char *string) { char *c; for (c= string; *c; c++) if (*c <= ' ' || *c == 0x7F) *c= '_'; return string; } /* ProcessCommand bites off the first word and dispatches to a command of that name. * It destroys "Line" with strtok in the process. */ int ProcessCommand(char *Line) { const CmdListEntry *Cmd; struct ParseParamsResult parsed; int GLErr; parsed.iCnt= parsed.lCnt= parsed.fCnt= parsed.dCnt= parsed.sCnt= parsed.oCnt= 0; parsed.errmsg= NULL; char *command= next_token(&Line); // Ignore blank lines or comments if (!command || !*command || *command == '#') return P_IGNORE; Cmd= GetCmd(command); sanitize_for_print(command); if (!Cmd) { fprintf(stderr, "Unknown command: \"%s\".\n", command); return P_NOT_FOUND; } /* use command's format string to parse each argument to the correct type */ if (!ParseParams(&Line, Cmd->ArgFormat, &parsed)) { fprintf(stderr, "Error parsing params of %s%s%s\n", command, parsed.errmsg? ": ":"\n", parsed.errmsg? parsed.errmsg : ""); return P_CMD_ERR; } /* dispatch command. Command may return extra error info in errbuf */ if (!Cmd->Handler(&parsed)) { fprintf(stderr, "Error during execution of %s%s%s\n", command, parsed.errmsg? ": ":"\n", parsed.errmsg? parsed.errmsg : ""); return P_CMD_ERR; } /* Check GL status, unless in the middle of begin/end */ if (!PointsInProgress) { GLErr= glGetError(); if (GLErr) { // command was successful while (GLErr) { fprintf(stderr, "GL error while executing %s: %s.\n", command, gluErrorString(GLErr)); GLErr= glGetError(); } return P_CMD_ERR; } } return P_SUCCESS; } SymbVarEntry* CreateNamedObj(const char* Name, int Type); bool ParseParams(char **line, const char *format, struct ParseParamsResult *parsed) { const char *tok= "", *fmt_next; SymbVarEntry *obj; const char *autocreate_name[sizeof(parsed->objects)/sizeof(parsed->objects[0])]; int autocreate_type[sizeof(parsed->objects)/sizeof(parsed->objects[0])]; int objtyp, autocreate= 0, optional= 0, repeat= 0, oCnt_start= parsed->oCnt, i; while (*format) { //DEBUGMSG(("Parse %s from \"%s\"\n", format, *line)); /* parse out the next format specifier */ fmt_next= format+1; autocreate= (*fmt_next == '!'); if (autocreate) fmt_next++; optional= (*fmt_next == '?'); if (optional) fmt_next++; repeat= (*fmt_next == '*'); if (repeat) { ++fmt_next; /* repeat must be end of list, otherwise no way to know how many it captured */ assert(*fmt_next == '\0'); } /* if there is no input left to parse, consider 'optional' and return accordingly */ if (!**line) { if (optional || repeat) break; else { parsed->errmsg= "Not enough arguments"; return false; } } switch (*format) { // Integer value case 'i': if (!ParseInt(line, parsed)) return false; break; //case 'l': if (!ParseLong(line, parsed)) return false; break; // Floating value case 'f': if (!ParseFloat(line, parsed)) return false; break; case 'd': if (!ParseDouble(line, parsed)) return false; break; // Color, either "#xxxxxx" or 3xFloat or 4xFloat (always stored as 4x float) case 'c': if (!ParseColor(line, parsed)) return false; break; // Single Token string case 't': if (!ParseToken(line, parsed)) return false; break; // remainder of buffer case 'b': if (!CaptureRemainder(line, parsed)) return false; break; // string, either one token or rest of line if quoted case 's': // File name, same as string but check that it exists case '/': if (!(**line == '"'? ParseToken(line, parsed) : CaptureRemainder(line, parsed))) return false; assert(parsed->sCnt > 0); tok= parsed->strings[parsed->sCnt - 1]; if (*format == '/' && !FileExists(tok)) { snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "No such file '%s'", tok); parsed->errmsg= parsed->errmsg_buf; return false; } break; // Display list if (0) case 'L': objtyp= NAMED_LIST; // Quadric if (0) case 'Q': objtyp= NAMED_QUADRIC; // Texture if (0) case 'T': objtyp= NAMED_TEXTURE; // Font if (0) case 'F': objtyp= NAMED_FONT; if (parsed->oCnt >= sizeof(parsed->objects)/sizeof(parsed->objects[0])) { parsed->errmsg= "Argument count exceeded (object)"; return false; } tok= next_token(line); if (!tok || !*tok) { snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Expected named %s", SymbVarTypeName[objtyp]); parsed->errmsg= parsed->errmsg_buf; return false; } else if ((obj= GetSymbVar(tok, objtyp))) { parsed->objects[parsed->oCnt++]= obj; } else if (autocreate) { /* Doesn't exist. If autocreate, wait until rest of parsing has succeeded. Otherwise the handler won't get to see it, and could end up with dangling NULL pointers in the variables tree for things like fonts. */ autocreate_name[parsed->oCnt]= tok; autocreate_type[parsed->oCnt]= objtyp; parsed->objects[parsed->oCnt++]= NULL; } else { snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Named %s '%s' does not exist", SymbVarTypeName[objtyp], tok); parsed->errmsg= parsed->errmsg_buf; return false; } break; default: assert(0 == "all formats handled"); return false; } /* advance to next format specifier */ if (!repeat) format= fmt_next; } /* all format specifiers have been satisfied. Check for leftover input */ if (**line) { parsed->errmsg= "unpexpected extra arguments"; return false; } /* Now, for any object that didn't exist and was marked autocreate, create it. */ for (i= oCnt_start; i < parsed->oCnt; i++) { if (!parsed->objects[i]) { assert(autocreate_type[i] >= 1 && autocreate_type[i] <= 4); assert(autocreate_name[i] != NULL && *autocreate_name[i]); parsed->objects[i]= CreateSymbVar(autocreate_name[i], autocreate_type[i]); switch (autocreate_type[i]) { case NAMED_LIST: parsed->objects[i]->Value= glGenLists(1); break; case NAMED_QUADRIC: parsed->objects[i]->Data= gluNewQuadric(); break; case NAMED_TEXTURE: glGenTextures(1, (GLuint*) &(parsed->objects[i]->Value)); break; /* can't auto-create FTfont* object. Caller needs to handle that. */ default: parsed->objects[i]->Data= NULL; break; } } } return true; } bool ParseInt(char **line, struct ParseParamsResult *parsed) { const IntConstListEntry *SymConst; char *tok, *EndPtr; long l; if (parsed->iCnt >= sizeof(parsed->ints)/sizeof(parsed->ints[0])) { parsed->errmsg= "Argument count exceeded (int)"; return false; } if (!(tok= next_token(line)) || !*tok) { parsed->errmsg= "expected Integer"; return false; } if (tok[0] >= '0' && tok[0] <= '9') { l= strtol(tok, &EndPtr, (tok[0] == '0' && tok[1] == 'x')? 16 : 10); if (*EndPtr == '\0') { parsed->ints[parsed->iCnt++]= l; return true; } else { snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Can't parse int '%s'", tok); parsed->errmsg= parsed->errmsg_buf; return false; } } else { //DEBUGMSG(("Searching for %s...", tok)); if ((SymConst= GetIntConst(tok))) { //DEBUGMSG((" Found: %i\n", SymConst->Value)); parsed->ints[parsed->iCnt++]= SymConst->Value; return true; } else { //DEBUGMSG(("not found.\n")); snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Unknown constant '%s'", tok); parsed->errmsg= parsed->errmsg_buf; return false; } } } bool ParseFloat(char **line, struct ParseParamsResult *parsed) { if (parsed->fCnt >= sizeof(parsed->floats)/sizeof(parsed->floats[0])) { parsed->errmsg= "Argument count exceeded (float)"; return false; } if (!ParseDouble(line, parsed)) return false; parsed->floats[parsed->fCnt++]= parsed->doubles[--parsed->dCnt]; return true; } bool ParseDouble(char **line, struct ParseParamsResult *parsed) { char *tok, *EndPtr; double num, denom; if (parsed->dCnt >= sizeof(parsed->doubles)/sizeof(parsed->doubles[0])) { parsed->errmsg= "Argument count exceeded (double)"; return false; } if (!(tok= next_token(line))) { parsed->errmsg= "expected Float"; return false; } /* ignore double negatives */ if (tok[0] == '-' && tok[1] == '-') tok+= 2; num= strtod(tok, &EndPtr); if (*EndPtr == '/') { denom= strtod(EndPtr+1, &EndPtr); if (!denom) { parsed->errmsg= "Division by zero"; return false; } num /= denom;; } else { num *= FixedPtMultiplier; } if (*EndPtr != '\0') { snprintf(parsed->errmsg_buf, sizeof(parsed->errmsg_buf), "Can't parse float '%s'", tok); parsed->errmsg= parsed->errmsg_buf; return false; } parsed->doubles[parsed->dCnt++]= num; return true; } bool ParseToken(char **line, struct ParseParamsResult *parsed) { char *tok; /* buffer space check */ if (parsed->sCnt >= sizeof(parsed->strings)/sizeof(parsed->strings[0])) { parsed->errmsg= "Argument count exceeded (string)"; return false; } if (!(tok= next_token(line))) { parsed->errmsg= "expected Token"; return false; } parsed->strings[parsed->sCnt++]= tok; return true; } bool CaptureRemainder(char **line, struct ParseParamsResult *parsed) { if (parsed->sCnt >= sizeof(parsed->strings)/sizeof(parsed->strings[0])) { parsed->errmsg= "Argument count exceeded (string)"; return false; } parsed->strings[parsed->sCnt++]= *line; *line += strlen(*line); return true; } bool ParseHexByte(const char *str, float *Result); bool ParseHexColor(const char* str, float Result[4]); bool ParseColor(char **line, struct ParseParamsResult *parsed) { int n; /* always stored as four floating point components */ if (parsed->fCnt + 4 > sizeof(parsed->floats)/sizeof(parsed->floats[0])) { parsed->errmsg= "Argument count exceeded (float)"; return false; } // hash indicates a hex code if (**line == '#' || ((**line == '"' || **line == '\'') && (*line)[1] == '#')) { if (!ParseHexColor(next_token(line), parsed->floats + parsed->fCnt)) { parsed->errmsg= "Invalid color code"; return false; } else { parsed->fCnt+= 4; return true; } } else { // else look for 3 or 4 numbers for (n= 0; n < 4; n++) { if (!ParseFloat(line, parsed)) break; } if (n < 3) { parsed->fCnt -= n; parsed->errmsg= "Not enough components for color"; return false; } /* alpha= 1.0 unless specified otherwise */ if (n < 4) parsed->floats[parsed->fCnt++]= 1.0; return true; } } bool ParseHexColor(const char* Text, float Result[4]) { if (!Text || Text[0] != '#') return false; // could use strtol instead of this, but then would have to worry about endian issues, // made more awkward by presence or absence of Alpha channel if (!ParseHexByte(Text+1, Result+0)) return false; if (!ParseHexByte(Text+3, Result+1)) return false; if (!ParseHexByte(Text+5, Result+2)) return false; if (Text[7]) { if (!ParseHexByte(Text+7, Result+3)) return false; } else { Result[3]= 0xFF; } return true; } bool ParseHexByte(const char *str, float *Result) { GLubyte x; char h= str[0], l= str[1]; if (h >= '0' && h <= '9') x= h - '0'; else if (h >= 'A' && h <= 'F') x= h - ('A' - 10); else if (h >= 'a' && h <= 'f') x= h - ('a' - 10); else return false; x <<= 4; if (l >= '0' && l <= '9') x|= l - '0'; else if (l >= 'A' && l <= 'F') x|= l - ('A' - 10); else if (l >= 'a' && l <= 'f') x|= l - ('a' - 10); else return false; *Result= x * 1.0/255; return true; } bool FileExists(const char *Name) { #ifdef _WIN32 return PathFileExists(Name); #else struct stat s; return stat(Name, &s) == 0; #endif } ================================================ FILE: src/ProcessInput.h ================================================ #ifndef PROCESS_INPUT_H #define PROCESS_INPUT_H #include "Global.h" #define P_SUCCESS 0 #define P_IGNORE 1 #define P_CMD_ERR 2 #define P_NOT_FOUND 3 #define P_EOF 4 #ifndef _WIN32 void InitLineBuffer(int FileHandle); #else void InitLineBuffer(HANDLE FileHandle); #endif char* ReadLine(); int ProcessCommand(char *Line); struct SymbVarEntry_t; /* The ParseParams function family returns all parsed values into a collection * of fixed-size arrays. So, a format of "iiff" will store two items into the * array of ints, and two items into the array of floats. This saves a lot of * bookkeeping over dynamic allocation, or messing with unions, or trusting that * the union is of the type you think it is. */ #define PARSEPARAMS_MAX_OBJECTS 32 #define PARSEPARAMS_MAX_FLOATS 32 #define PARSEPARAMS_MAX_STRINGS 32 #define PARSEPARAMS_MAX_INTS 32 struct ParseParamsResult { //long longs[PARSEPARAMS_MAX_INTS]; GLint ints [PARSEPARAMS_MAX_INTS]; GLfloat floats[PARSEPARAMS_MAX_FLOATS]; GLdouble doubles[PARSEPARAMS_MAX_FLOATS]; char* strings[PARSEPARAMS_MAX_STRINGS]; struct SymbVarEntry_t *objects[PARSEPARAMS_MAX_OBJECTS]; int iCnt, lCnt, fCnt, dCnt, sCnt, oCnt; char *errmsg, errmsg_buf[128]; }; extern bool ParseParams (char **line, const char *format, struct ParseParamsResult *result); extern char* next_token (char **line); extern bool ParseToken (char **line, struct ParseParamsResult *result); extern bool ParseInt (char **line, struct ParseParamsResult *result); //extern bool ParseLong (char **line, struct ParseParamsResult *result); extern bool ParseFloat (char **line, struct ParseParamsResult *result); extern bool ParseDouble (char **line, struct ParseParamsResult *result); extern bool ParseColor (char **line, struct ParseParamsResult *result); extern bool FileExists (const char *Name); extern bool CaptureRemainder(char **line, struct ParseParamsResult *result); #endif ================================================ FILE: src/Server.c ================================================ #define INCLUDE_GL #define INCLUDE_SDL #include #include "Global.h" #include "Version.h" #include "Server.h" #include "ProcessInput.h" #include "SymbolHash.h" #include "ParseGL.h" bool Shutdown= false; typedef struct { char *FifoName; bool TerminateOnEOF; bool ShowCmds, ShowConsts; bool WantHelp; bool NeedHelp; bool VersionOnly; char *WndTitle; bool NoEmitEvents; bool ManualSDLSetup; bool ManualViewport; bool ManualProjection; int Width; int Height; int X, Y; int Bpp; } CmdlineOptions; void SetParamDefaults(CmdlineOptions *Options); void ReadParams(char **args, CmdlineOptions *Options); void PrintUsage(bool error); void CheckInput(); void CheckSDLEvents(); void InitGL(int, int); void EmitKey(const SDL_KeyboardEvent *E); bool PendingResize= false; int Resize_w, Resize_h; void FinishResize(); char Buffer[1024]; #ifndef _WIN32 int InputFD= 0; #else HANDLE InputFD; #endif SDL_Surface *MainWnd= NULL; long StartTime; int DefaultSDLFlags= SDL_OPENGL | SDL_ANYFORMAT | SDL_RESIZABLE; CmdlineOptions Options; int main(int Argc, char**Args) { SetParamDefaults(&Options); ReadParams(Args, &Options); char wnd_xy_buf[48]; if (Options.WantHelp || Options.NeedHelp) { PrintUsage(!Options.WantHelp); return Options.WantHelp? 0 : -1; } if (Options.VersionOnly) { printf("Version %s\n", CGLVER_String); return 0; } if (Options.ShowCmds) { DumpCommandList(stdout); return 0; } if (Options.ShowConsts) { DumpConstList(stdout); return 0; } #ifndef WIN32 if (Options.FifoName) { if (mkfifo(Options.FifoName, 0x1FF) < 0) { perror("mkfifo"); return 2; } InputFD= open(Options.FifoName, O_RDONLY | O_NONBLOCK); if (InputFD < 0) { perror("open(fifo, read|nonblock)"); return 3; } close(0); // Done with stdin. } else if (isatty(InputFD)) { fprintf(stderr, "Warning: STDIN is a terminal; will use blocking reads\n"); } else { DEBUGMSG(("Enabling nonblocking mode on stdin\n")); if (fcntl(InputFD, F_SETFL, O_NONBLOCK) < 0) { perror("fnctl(stdin, F_SETFL, O_NONBLOCK)"); return 3; } } #else InputFD= GetStdHandle(STD_INPUT_HANDLE); #endif InitLineBuffer(InputFD); // this initializes a static global LineBuffer. See ProcessInput.c if (!Options.ManualSDLSetup) { DEBUGMSG(("Initializing SDL\n")); if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "CmdlineGL: SDL_Init: %s\n", SDL_GetError()); return 4; } atexit(SDL_Quit); // SdlVid= SDL_GetVideoInfo(); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); DEBUGMSG(("Setting video mode\n")); // Silly, but don't see a better way right now if (Options.X || Options.Y) { snprintf(wnd_xy_buf, sizeof(wnd_xy_buf), "%d,%d", Options.X, Options.Y); setenv("SDL_VIDEO_WINDOW_POS", wnd_xy_buf, 1); } MainWnd= SDL_SetVideoMode(Options.Width, Options.Height, Options.Bpp, DefaultSDLFlags); if (!MainWnd) { fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError()); exit(5); } SDL_EnableUNICODE(1); if (SDL_EnableKeyRepeat(0, 0) < 0) fprintf(stderr, "Can't disable key repeat. %s\n", SDL_GetError()); InitGL(Options.Width, Options.Height); if (!Options.WndTitle) Options.WndTitle= "CmdlineGL"; SDL_WM_SetCaption(Options.WndTitle, Options.WndTitle); } DEBUGMSG(("Recording time\n")); StartTime= SDL_GetTicks(); DEBUGMSG(("Entering main loop\n")); while (!Shutdown) { CheckInput(); CheckSDLEvents(); SDL_Delay(1); // yield processor to the script } DEBUGMSG(("Shutting down.\n")); #ifndef _WIN32 close(InputFD); #else CloseHandle(InputFD); #endif // this might be a socket if (Options.FifoName) { unlink(Options.FifoName); } return 0; } void SetParamDefaults(CmdlineOptions *Options) { memset(Options, 0, sizeof(CmdlineOptions)); Options->Width= 640; Options->Height= 480; // Options->Bpp= 0; zero tells to use display default } void PrintMissingParamMessage(char* ArgName, CmdlineOptions *Options) { fprintf(stderr, "Option %s requires a parameter\n", ArgName); Options->NeedHelp= true; } void ReadParams(char **Args, CmdlineOptions *Options) { char **NextArg= Args+1, *tmp; bool ReadingArgs= true; int n, w, h, x, y; // Note: this function takes advantage of the part of the program argument specification // that says all argv lists must end with a NULL pointer. while (ReadingArgs && *NextArg && (*NextArg)[0] == '-') { switch ((*NextArg)[1]) { #ifndef WIN32 case 'f': Options->FifoName= *++NextArg; if (!Options->FifoName) { PrintMissingParamMessage("-f", Options); return; } break; // '-f ' = read from FIFO "blah" #endif case 't': Options->TerminateOnEOF= true; break; case 'v': Options->VersionOnly= true; break; case 'h': case '?': Options->WantHelp= true; break; case '\0': ReadingArgs= false; break; // "-" = end of arguments case '-': // "--" => long-option if (strcmp(*NextArg, "--help") == 0) { Options->WantHelp= true; break; } if (strcmp(*NextArg, "--showcmds") == 0) { Options->ShowCmds= true; break; } if (strcmp(*NextArg, "--showconsts") == 0) { Options->ShowConsts= true; break; } if (strcmp(*NextArg, "--noevents") == 0) { Options->NoEmitEvents= true; break; } if (strcmp(*NextArg, "--manual-viewport") == 0) { Options->ManualViewport= true; break; } if (strcmp(*NextArg, "--manual-projection") == 0) { Options->ManualProjection= true; break; } if (strcmp(*NextArg, "--title") == 0) { Options->WndTitle= *++NextArg; if (!Options->WndTitle) { PrintMissingParamMessage("--title", Options); return; } break; } if (strcmp(*NextArg, "--version") == 0) { Options->VersionOnly= true; break; } if (strcmp(*NextArg, "--geometry") == 0) { tmp= *++NextArg; if (!tmp || (n=sscanf(tmp, "%dx%d%d%d", &w, &h, &x, &y)) < 2) { PrintMissingParamMessage("--geometry", Options); return; } Options->Width= w; Options->Height= h; if (n > 2) Options->X= x; if (n > 3) Options->Y= y; break; } default: fprintf(stderr, "Unrecognized argument: %s\n", *NextArg); Options->NeedHelp= true; return; } NextArg++; } } /* =head1 OPTIONS =over =item -t Terminate after a zero-length read (EOF) =item --manual-viewport Don't automatically reinitialize glViewport after the window has changed size. This allows the application to control the viewport area. =item --manual-projection Don't automatically set up a projection matrix or rescale it after the window changes size. =item -f FIFO Create a named pipe at this path, and read from it. This option is not available on Windows. =item --geometry WIDTHxHEIGHT+X+Y Using Unix X11 notation, specify the window geometry. Negative X and Y are not yet supported. =item --title TEXT Set the title of the window. =item --noevents Don't report user input activity on stdout. (other messages and events will still be printed) =item --showcmds Dump a list of all available commands on stdout. =item --showconsts Dump a list of all symbolic constants on stdout. These constants may be given to any command which takes an integer argument. =item -v =item --version Print the version and exit. =item -h Print a brief usage summary =back =cut */ void PrintUsage(bool error) { fprintf(error? stderr : stdout, "CmdlineGL %s\n" "Usage:\n" " | CmdlineGL [options] | \n" " Reads commands from stdin, and writes user input to stdout.\n" "\n" "Options:\n" " -t Terminate after a zero-length read (EOF).\n" " --manual-viewport No automatic glViewport on window resize\n" " --manual-projection No default GL_PROJECTION matrix\n" #ifndef WIN32 " -f FIFO Create the named fifo (file path+name) and read from it.\n" #endif " --geometry WxH+X+Y Set initial window geometry (defaults to 640x480+0+0)\n" " --title TEXT Set the title of the window to \"text\".\n" " --noevents Don't print any input event messages to stdout.\n" " --showcmds List all the available commands in this version of CmdlineGL.\n" " --showconsts List all the constants (GL_xxxx) that are available.\n" " -v --version Print version and exit.\n" " -h Display this help message.\n" "\n" "Note: Each line of input is broken on space characters and treated as a\n" " command. There is currently no escaping mechanism, although I'm not\n" " opposed to the idea.\n\n", CGLVER_String ); } /*=head1 COMMANDS =head2 Render Loop Commands These commands control CmdlineGL itself, and assist with setting up a render loop. =item cglEcho ANY_TEXT Repeat a string of text on stdout (may be confused for user input events, but maybe that's what you want) =cut */ COMMAND(cglEcho, "b") { printf("%s\n", parsed->strings[0]); fflush(stdout); return true; } /*=item cglExit Cause CmdlineGL to terminate (with error code 0) =item cglQuit Alias for cglExit =cut */ COMMAND(cglExit, "") { Shutdown= true; return true; } COMMAND(cglQuit, "") { Shutdown= true; return true; } /*=item cglGetTime Return the number of milliseconds since start of the program. =item cglSleep DELAY_MS Sleep for a number of milliseconds from when the command is received =item cglSync UNTIL_TIME_MS Sleep until the specified time, measured as milliseconds from start of program. =cut */ COMMAND(cglGetTime, "") { long t= SDL_GetTicks() - StartTime; printf("t=%ld\n", t); fflush(stdout); return true; } COMMAND(cglSleep, "i") { fflush(stdout); fflush(stderr); SDL_Delay(parsed->ints[0]); return true; } COMMAND(cglSync, "i") { int target= parsed->ints[0]; int t= SDL_GetTicks() - StartTime; if (target - t > 0) { DEBUGMSG(("sleeping for %d\n", target - t)); fflush(stdout); fflush(stderr); SDL_Delay(target - t); } return true; } /*=item cglSwapBuffers Swap front and back buffer, showing the frame you were drawing and beginning a new frame. (you still need to call glClear yourself) If a window resize is pending, it will be performed at this point. =cut */ COMMAND(cglSwapBuffers, "") { SDL_GL_SwapBuffers(); FrameInProgress= false; // If we were waiting to resize the window, now is the time if (PendingResize) FinishResize(); return true; } void CheckInput() { int CmdCount; char *Line; errno= 0; CmdCount= 0; while ((CmdCount < MAX_COMMAND_BATCH || PointsInProgress) && (Line= ReadLine())) { if (*Line && *Line != '#') { //fprintf(stderr, "Starting '%s'\n", Line); ProcessCommand(Line); //fprintf(stderr, "Ending '%s'\n", Line); CmdCount++; } } if (CmdCount < MAX_COMMAND_BATCH && errno != EAGAIN) { DEBUGMSG(("Received EOF.\n")); if (Options.TerminateOnEOF) Shutdown= 1; } } void CheckSDLEvents() { SDL_Event Event; while (SDL_PollEvent(&Event)) switch (Event.type) { case SDL_KEYDOWN: case SDL_KEYUP: if (!Options.NoEmitEvents) EmitKey(&Event.key); break; case SDL_MOUSEMOTION: if (!Options.NoEmitEvents) printf("M @ %d %d %d %d\n", Event.motion.x, Event.motion.y, Event.motion.xrel, Event.motion.yrel); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if (!Options.NoEmitEvents) printf("M %c %d %d %d\n", (Event.button.state == SDL_PRESSED)? '+':'-', Event.button.button, Event.button.x, Event.button.y); break; case SDL_VIDEORESIZE: Resize_w= Event.resize.w; Resize_h= Event.resize.h; // Can't resize if user is in the middle of rendering if (FrameInProgress) PendingResize= true; else FinishResize(); break; case SDL_ACTIVEEVENT: if (!Options.NoEmitEvents) { if (Event.active.state & SDL_APPMOUSEFOCUS) printf("W MOUSEFOCUS %c\n", Event.active.gain? '+':'-'); if (Event.active.state & SDL_APPINPUTFOCUS) printf("W INPUTFOCUS %c\n", Event.active.gain? '+':'-'); if (Event.active.state & SDL_APPACTIVE) printf("W ACTIVE %c\n", Event.active.gain? '+':'-'); } break; case SDL_QUIT: if (!Options.NoEmitEvents) printf("W QUIT\n"); Shutdown= true; break; } if (!Options.NoEmitEvents) fflush(stdout); } // This should only be called when FrameInProgress is false // Might get called from ParseGL.c, so args are stored in globals. void FinishResize() { assert(!FrameInProgress); MainWnd= SDL_SetVideoMode(Resize_w, Resize_h, Options.Bpp, DefaultSDLFlags); if (!Options.NoEmitEvents) { printf("W RESIZE %d %d\n", Resize_w, Resize_h); fflush(stdout); } PendingResize= false; InitGL(Resize_w, Resize_h); } void InitGL(int w, int h) { GLfloat top, bottom, left, right; GLfloat dist= 20; // 20 units from near clipping plane to the origin assert(!FrameInProgress); if (!Options.ManualViewport) // Use the entire window glViewport(0, 0, w, h); if (!Options.ManualProjection) { // Recalculate the projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (wkeysym.unicode; strncpy(kname, SDL_GetKeyName(E->keysym.sym), sizeof(kname)); kname[sizeof(kname)-1]= '\0'; // remove any whitespace in the key name for (c= kname; *c; c++) if (*c <= ' ') *c= '_'; // If unicode available, encode as utf8 if (codepoint > (unsigned) ' ') encode_utf8(uni, codepoint); else uni[0]= 0; printf("K %c %s %d %04x %s\n", (E->state == SDL_PRESSED)? '+':'-', kname, E->keysym.scancode, codepoint, uni); } // Yeah this exists elsewhere but I don't feel like depending on a lib for just this one function void encode_utf8(char *buffer, unsigned codepoint) { if (codepoint < 0x80) *buffer++= (char) codepoint; else if (codepoint < 0x800) { *buffer++= (char) 0xC0 | ((codepoint >> 6) & 0x1F); *buffer++= (char) 0x80 | ((codepoint ) & 0x3F); } else if (codepoint < 0x10000) { *buffer++= (char) 0xE0 | ((codepoint >> 12) & 0x0F); *buffer++= (char) 0x80 | ((codepoint >> 6) & 0x3F); *buffer++= (char) 0x80 | ((codepoint ) & 0x3F); } else if (codepoint < 0x110000) { *buffer++= (char) 0xF0 | ((codepoint >> 18) & 0x07); *buffer++= (char) 0x80 | ((codepoint >> 12) & 0x3F); *buffer++= (char) 0x80 | ((codepoint >> 6) & 0x3F); *buffer++= (char) 0x80 | ((codepoint ) & 0x3F); } *buffer= 0; } ================================================ FILE: src/Server.h ================================================ #ifndef SERVER_H #define SERVER_H #include "Global.h" extern bool PendingResize; extern void FinishResize(); #endif ================================================ FILE: src/SymbolHash.c ================================================ #include #include "Global.h" #include "SymbolHash.h" const char *SymbVarTypeName[]= { "?", "Display List", "Quadric", "Texture", "Font" }; bool SymbVarTreeInit= false; RBTree SymbVarTree; /* ConstList dynamically generated variables */ extern const int IntConstListCount; extern const IntConstListEntry IntConstList[]; extern const int IntConstHashTableSize; extern const uint16_t IntConstHashTable[]; void DumpConstList(FILE* DestStream) { int i; for (i=1; i<=IntConstListCount; i++) /* 1-based list, since hashtable 0 isn't a valid item */ if (IntConstList[i].Name) fprintf(DestStream, "%s %d\n", IntConstList[i].Name, IntConstList[i].Value); } /* CmdList dynamically generated variables */ extern const int CmdListCount; extern const CmdListEntry CmdList[]; extern const int CmdHashTableSize; extern const uint16_t CmdHashTable[]; void DumpCommandList(FILE* DestStream) { int i; for (i=1; i<=CmdListCount; i++) /* 1-based list, since hashtable 0 isn't a valid item */ if (CmdList[i].Name) fprintf(DestStream, "%s\n", CmdList[i].Name); } /* The rest is related to the red/black tree that stored dynamic named objects */ unsigned int CalcHash(const char* str) { unsigned int Result= 0; while (*str) Result= ((Result<<1) + (Result ^ *str++)); return Result; } void InitSymbVarEntry(SymbVarEntry *Entry, const char* Name, int Type) { Entry->Hash= Type+CalcHash(Name); Entry->Type= Type; strncpy(Entry->Name, Name, SYMB_VAR_MAX_LEN-1); Entry->Name[SYMB_VAR_MAX_LEN-1]= '\0'; RBTreeNode_Init(&(Entry->node)); Entry->node.Object= Entry; } bool SymbVar_inorder_func(const void* ObjA, const void* ObjB) { SymbVarEntry *EntryA= (SymbVarEntry*) ObjA; SymbVarEntry *EntryB= (SymbVarEntry*) ObjB; if (EntryA->Hash == EntryB->Hash) { if (EntryA->Type == EntryB->Type) return strcmp(EntryA->Name, EntryB->Name) <= 0; else return EntryA->Type < EntryB->Type; } else return (EntryA->Hash < EntryB->Hash); } /** Structure used to pass the key to the compare function */ typedef struct { int Hash; const char *Name; int Type; } SymbVarSearchKey; int SymbVar_compare_func(const void* SearchKey, const void* Object) { SymbVarSearchKey *Key= (SymbVarSearchKey*) SearchKey; SymbVarEntry *Node= (SymbVarEntry*) Object; if (Key->Hash == Node->Hash) { if (Key->Type == Node->Type) return strcmp(Key->Name, Node->Name); else return Key->Type - Node->Type; } else return Key->Hash - Node->Hash; } SymbVarEntry *GetSymbVar(const char *Name, int Type) { SymbVarSearchKey Key; RBTreeNode *Node; if (!SymbVarTreeInit) { RBTree_InitRootSentinel(&SymbVarTree.RootSentinel); SymbVarTreeInit= true; } Key.Hash= Type+CalcHash(Name); Key.Name= Name; Key.Type= Type; Node= RBTree_Find(&SymbVarTree.RootSentinel, &Key, SymbVar_compare_func); if (Node) return (SymbVarEntry*) Node->Object; else return NULL; } SymbVarEntry *CreateSymbVar(const char *Name, int Type) { SymbVarEntry *Entry; if (!SymbVarTreeInit) { RBTree_InitRootSentinel(&SymbVarTree.RootSentinel); SymbVarTreeInit= true; } Entry= (SymbVarEntry*) malloc(sizeof(SymbVarEntry)); InitSymbVarEntry(Entry, Name, Type); RBTree_Add(&SymbVarTree.RootSentinel, &(Entry->node), SymbVar_inorder_func); return Entry; } void DeleteSymbVar(SymbVarEntry *Entry) { RBTree_Prune(&Entry->node); free(Entry); } void DumpVarList(FILE* DestStream) { RBTreeNode *Current= RBTree_GetLeftmost(SymbVarTree.RootSentinel.Left); while (Current != &Sentinel) { fprintf(DestStream, "%s %s\n", SymbVarTypeName[((SymbVarEntry*)Current->Object)->Type], ((SymbVarEntry*)Current->Object)->Name ); Current= RBTree_GetNext(Current); } } ================================================ FILE: src/SymbolHash.h ================================================ #ifndef SYMBOL_HASH_H #define SYMBOL_HASH_H #include "ProcessInput.h" struct ParseParamsResult; typedef struct { const char* Name; const char* ArgFormat; bool (*Handler)(struct ParseParamsResult *parsed); } CmdListEntry; typedef struct { const char* Name; int Value; } IntConstListEntry; #ifndef SYMB_VAR_MAX_LEN #define SYMB_VAR_MAX_LEN 32 #endif #include "Contained_RBTree.h" typedef struct SymbVarEntry_t { int Hash; char Name[SYMB_VAR_MAX_LEN]; union { int Value; void *Data; }; int Type; RBTreeNode node; } SymbVarEntry; #define NAMED_LIST 1 #define NAMED_QUADRIC 2 #define NAMED_TEXTURE 3 #define NAMED_FONT 4 extern const char *SymbVarTypeName[]; extern const CmdListEntry *GetCmd(const char *Name); extern void DumpCommandList(FILE* DestStream); extern const IntConstListEntry *GetIntConst(const char *Name); extern void DumpConstList(FILE* DestStream); extern SymbVarEntry *CreateSymbVar(const char *Name, int Type); extern SymbVarEntry *GetSymbVar(const char *Name, int Type); extern void DeleteSymbVar(SymbVarEntry *Entry); // does not free contents of the variable!! extern void DumpVarList(FILE* DestStream); #endif ================================================ FILE: src/Version.h ================================================ #ifndef VERSION_H #define VERSION_H extern const int CGLVER_Major; extern const int CGLVER_Minor; extern const int CGLVER_Release; extern const int CGLVER_Build; extern const char* CGLVER_Host; extern const char* CGLVER_String; #endif ================================================ FILE: src/manual.head.pod ================================================ =head1 NAME CmdlineGL - Interpreter for subset of OpenGL 1.4 API =head1 SYNOPSIS CmdlineGL [OPTIONS] user_events # Play some recorded 3D graphics bzcat graphics.txt.bz2 | CmdlineGL >/dev/null # Experiment from Bash source CmdlineGL.lib glClearColor '#00FF00' glClear GL_COLOR_BUFFER_BIT cglSwapBuffers =head1 DESCRIPTION CmdlineGL is an interpreter for parsing and executing OpenGL functions, which also simplifies the OpenGL API in several ways to make it more user-friendly. It comes with shell bindings for bash, allowing for experiments right from the terminal (if you have a graphical desktop). It only covers the functions a beginner would need, and doesn't aspire to anything newer than the 1.4 API. It also provides some basic access to the GLU, L (loading image files), and L (font rendering) libraries, for the ability to make useful demos that the raw OpenGL API can't provide on its own. =head1 OPTIONS =head1 COMMANDS =head2 Parameter Parsing All commands take parameters separated by whitespace, in much the same way as a shell would break its arguments. There is also a quoting mechanism that may be used for any type of parameter. If the parameter begins with C<'> or C<">, then whitespace (other than newlines) will be included in the parameter until the matching quote character. Literal newline characters always end the command. Within the string, C<\n> will be converted to newline charachers, and any other character preceeded by a backslash will be included as-is, allowing you to escape the quote character and the backslash. =head3 Integers All integer parameters of commands may be given as decimal, hexidecimal or symbolic C constants. If an integer parameter does not begin with a digit, CmdlineGL will look it up in the table of symbolic codes, and if it is not found, the command will fail and not execute. =head3 Floats All floating point values may be specified as a normal decimal notation. However, to make things easier for Bash scripts, they may also contain a divisor, like "34/100". To facilitate fixed-point math on lots of parameters, CmdlineGL supports a feature of a "default divisor" that will be applied to EVERY floating point parameter of every command, unless the parameter contains a divisor already. See C below. =head3 Colors Many OpenGL commands take a color parameter, usually as 3 or 4 floating point numbers in the range [0..1]. In each case, you may supply a single HTML-style hex code like '#RRGGBB' or '#RRGGBBAA' in their place. The floating point values ARE affected by any implied divisor in effect (see above) but the HTML color codes are NOT. =head2 Default-Divisor Commands =cut ================================================ FILE: src/manual.tail.pod ================================================ =head1 EXAMPLE The typical render loop, taking advantage of CmdlineGL's ability to synchronize frames to a clock, looks like this: =over =item 1 read output of CmdlineGL until "t=" event indicating current timestamp =item 2 clear frame buffer =item 3 render OpenGL graphics =item 4 call glFlush and cglSwapBuffers to show the rendering =item 5 call cglSync telling it to delay until T + framerate_period =item 6 call cglGetTime, so that new timestamp will be available immediately after sleep =item 7 repeat =back There are many examples in the C<./share/examples> directory that ships with this project, though you may or may not have them installed. Reproduced below, SpinText.sh is a minimal example of something visually interesting. It requires variables C<$font> (a path to a .ttf font file) and C<$text> (the text to render). spin_rate=70 # Degree per second R=0 # current rotation T=0 # "game-time", in milliseconds # Initialize CmdlineGL for rendering only (no input or feedback) source CmdlineGL.lib 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 string mid 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 window width glCallList mytext glFlush cglSwapBuffers cglSync $((T+=16)) # blindly assume we can maintain 60fps done More advanced timing and handling of user input can be seen in ModelViewer.sh, usage of Quadrics for quick rendering of solids can be seen in Robot.sh, and direct manipulation of the modelview matrix as a coordinate system can be seen in FlightSim.sh =head1 BUGS Please report bugs in the issue tracker at L =head1 SEE ALSO =over 15 =item Homepage L =item GitHub Page L =back =head1 AUTHOR Michael Conrad =cut ================================================ FILE: test/01-version.t ================================================ #! /usr/bin/env perl use strict; use warnings; use Test::More; pass; done_testing;