Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 20 Sep 2006 14:01:41 -0700 (PDT)
From:      rossiya@gmail.com
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   ports/103441: New port:math/ffff
Message-ID:  <200609202101.k8KL1fGD074019@peppercon.cologroup.net>
Resent-Message-ID: <200609202110.k8KLAI8v081192@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         103441
>Category:       ports
>Synopsis:       New port:math/ffff
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Sep 20 21:10:18 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     rossiya
>Release:        FreeBSD 6.1-RELEASE-p3 i386
>Organization:
>Environment:
System: FreeBSD peppercon 6.1-RELEASE-p3 FreeBSD 6.1-RELEASE-p3 #0: Sat Aug 12 15:47:47 PDT 2006 root@:/usr/obj/usr/src/sys/MC2 i386
>Description:
FFFF is the fastest Win32/OSX/Linux/IRIX Mandelbrot generator. Features OpenGL,
realtime zoom, SSE/AltiVec QuadPixel, SSE2/3DNow! DualPixel calc, FPU per
pixel calc, GPU asm (Fragment/Vertex) calc, multiprocessor support, and
benchmarking. Opt asm code!

This port does the standard Mandelbrot at near-Xaos speed, yet every pixel is
computed.  There is also an interesting parameter ray algorithm using your
3D card.  It detects my Nvidia fine.  Requires X11 and a 3D card strongly
suggested for speed and additional coprocessing power.

WWW: http://sourceforge.net/projects/ffff/
>How-To-Repeat:
>Fix:

--- sendFile begins here ---
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	./Makefile
#	./distinfo
#	./pkg-descr
#	./pkg-plist
#	./files
#	./files/patch-file
#
echo x - ./Makefile
sed 's/^X//' >./Makefile << 'END-of-./Makefile'
X# ex:ts=8
X# Ports collection makefile for:	ffff
X# Date created:			Sep 20, 2006
X# Whom:				rossiya
X#
X# $FreeBSD: ports/math/ffff/Makefile,v 1.16 2006/09/20 10:37:52 ade Exp $
X#
X
XPORTNAME=	ffff
XPORTVERSION=	323
XCATEGORIES=	math
XMASTER_SITES=	${MASTER_SITE_SOURCEFORGE_EXTENDED}
XMASTER_SITE_SUBDIR=	${PORTNAME}
XDISTNAME=	FFFF${PORTVERSION}-src
X
XMAINTAINER=	rossiya@gmail.com
XCOMMENT=	A Freebsd port of the fast Win32/OSX/Linux/IRIX Mandelbrot generator
X
XLIB_DEPENDS=	glut.4:${PORTSDIR}/graphics/libglut
X
XUSE_GMAKE=	yes
XUSE_ZIP=	yes
XUSE_GL=		yes
XUSE_XLIB=	yes
XMAKE_ARGS+=	ACLOCAL="${TRUE}" AUTOCONF="${TRUE}" AUTOMAKE="${TRUE}" \
X		AUTOHEADER="${TRUE}"
X
X# contains x86 assembler
XONLY_FOR_ARCHS=	i386 amd64
X
X.undef HAS_CONFIGURE
X
Xdo-install:
X	@${INSTALL_PROGRAM} ${WRKSRC}/debug/ffff ${X11BASE}/bin/
X
Xpost-install:
X.if !defined(NOPORTDOCS)
X#	${MKDIR} ${DOCSDIR}
X#	cd ${WRKSRC} && pwd && ${INSTALL_DATA} -v  ${PORTDOCS} ${DOCSDIR}
X.endif
X	${ECHO_MSG} "===>  Program is installed as ${X11BASE}/bin/ffff" \
X	&& ${ECHO_MSG} "      [Some modes don't yet work.  Let me know if you have interest/patches]"
X
X.include <bsd.port.mk>
END-of-./Makefile
echo x - ./distinfo
sed 's/^X//' >./distinfo << 'END-of-./distinfo'
XMD5 (FFFF323-src.zip) = ba5c63ccedfb61f5ceea431286d6d749
XSHA256 (FFFF323-src.zip) = 4781384b4c285fe61f19ba776d4a656fd5e809b9a88198a0705c8f7ca7dca715
XSIZE (FFFF323-src.zip) = 127442
END-of-./distinfo
echo x - ./pkg-descr
sed 's/^X//' >./pkg-descr << 'END-of-./pkg-descr'
XFFFF is the fastest Win32/OSX/Linux/IRIX Mandelbrot generator. Features OpenGL,
Xrealtime zoom, SSE/AltiVec QuadPixel, SSE2/3DNow! DualPixel calc, FPU per
Xpixel calc, GPU asm (Fragment/Vertex) calc, multiprocessor support, and
Xbenchmarking. Opt asm code!
X
XThis port does the standard Mandelbrot at near-Xaos speed, yet every pixel is
Xcomputed.  There is also an interesting parameter ray algoritymn using your
X3D card.  It detects my Nvidia GeForce FX5500 fine, at any rate.  Requires X11
Xand a 3D card strongly suggested for screen speed and additional coprocessing
Xpower.
X
XWWW: http://sourceforge.net/projects/ffff/
END-of-./pkg-descr
echo x - ./pkg-plist
sed 's/^X//' >./pkg-plist << 'END-of-./pkg-plist'
X@cwd /usr/X11R6
Xbin/ffff
END-of-./pkg-plist
echo c - ./files
mkdir -p ./files > /dev/null 2>&1
echo x - ./files/patch-file
sed 's/^X//' >./files/patch-file << 'END-of-./files/patch-file'
X--- ./extensions.cpp.orig	Tue Sep 19 21:06:09 2006
X+++ ./extensions.cpp	Thu Sep  7 07:18:59 2006
X@@ -1,6 +1,21 @@
X #include <stdlib.h>
X #include <string.h>
X 
X+#define __linux__
X+
X+  #include <GL/gl.h>
X+  #include <GL/glext.h>
X+  #include <GL/glx.h>
X+  #include <GL/glxext.h>
X+  #include <GL/glut.h>
X+//typedef void (*__GLXextFuncPtr)(void);
X+//extern __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *);
X+extern __GLXextFuncPtr glXGetProcAddressARB (GLubyte *);
X+//extern void ((*(glXGetProcAddressARB))(const GLubyte *procName))( void );
X+//extern void (*glXGetProcAddressARB( GLubyte *))( void );
X+
X+__GLXextFuncPtr glXGetProcAddressARB(unsigned char*){}
X+
X #ifdef __APPLE__
X   #include <mach-o/dyld.h>
X   #include <OpenGL/gl.h>
X@@ -96,18 +111,26 @@
X {
X #if defined(__APPLE__) || defined(__linux__)
X 	// GL_ARB_multitexture
X-	pfglActiveTextureARB          = (glActiveTextureARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glActiveTextureARB");
X-	pfglClientActiveTextureARB    = (glClientActiveTextureARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glClientActiveTextureARB");
X+//	pfglActiveTextureARB          = (glActiveTextureARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glActiveTextureARB");
X+	pfglActiveTextureARB          = (glActiveTextureARBProcPtr) GLGETPROCADDRESS((GLCHAR *)"glActiveTextureARB");
X+//	pfglClientActiveTextureARB    = (glClientActiveTextureARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glClientActiveTextureARB");
X+	pfglClientActiveTextureARB    = (glClientActiveTextureARBProcPtr) GLGETPROCADDRESS((GLCHAR *)"glClientActiveTextureARB");
X 	if (!pfglActiveTextureARB) return false;
X 	if (!pfglClientActiveTextureARB) return false;
X 
X         // GL_ARB_fragment_program
X-        pfglGenProgramsARB           = (glGenProgramsARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glGenProgramsARB");
X-	pfglDeleteProgramsARB        = (glDeleteProgramsARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glDeleteProgramsARB");
X-	pfglBindProgramARB           = (glBindProgramARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glBindProgramARB");
X-	pfglProgramStringARB         = (glProgramStringARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glProgramStringARB");
X-	pfglProgramEnvParameter4fARB = (glProgramEnvParameter4fARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glProgramEnvParameter4fARB");
X-	pfglGetProgramivARB          = (glGetProgramivARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glGetProgramivARB");
X+//        pfglGenProgramsARB           = (glGenProgramsARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glGenProgramsARB");
X+        pfglGenProgramsARB           = (glGenProgramsARBProcPtr) GLGETPROCADDRESS((GLCHAR *)"glGenProgramsARB");
X+//	pfglDeleteProgramsARB        = (glDeleteProgramsARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glDeleteProgramsARB");
X+	pfglDeleteProgramsARB        = (glDeleteProgramsARBProcPtr) GLGETPROCADDRESS((GLCHAR *)"glDeleteProgramsARB");
X+//	pfglBindProgramARB           = (glBindProgramARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glBindProgramARB");
X+	pfglBindProgramARB           = (glBindProgramARBProcPtr) GLGETPROCADDRESS((GLCHAR *)"glBindProgramARB");
X+//	pfglProgramStringARB         = (glProgramStringARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glProgramStringARB");
X+	pfglProgramStringARB         = (glProgramStringARBProcPtr) GLGETPROCADDRESS((GLCHAR *)"glProgramStringARB");
X+//	pfglProgramEnvParameter4fARB = (glProgramEnvParameter4fARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glProgramEnvParameter4fARB");
X+	pfglProgramEnvParameter4fARB = (glProgramEnvParameter4fARBProcPtr) GLGETPROCADDRESS((GLCHAR *)"glProgramEnvParameter4fARB");
X+//	pfglGetProgramivARB          = (glGetProgramivARBProcPtr) GLGETPROCADDRESS((GLCHAR const*)"glGetProgramivARB");
X+	pfglGetProgramivARB          = (glGetProgramivARBProcPtr) GLGETPROCADDRESS((GLCHAR *)"glGetProgramivARB");
X 	if (!pfglGenProgramsARB) return false;
X 	if (!pfglDeleteProgramsARB) return false;
X 	if (!pfglBindProgramARB) return false;
X--- ./FFFF3.cpp.orig	Tue Sep 19 21:06:09 2006
X+++ ./FFFF3.cpp	Thu Sep  7 21:02:57 2006
X@@ -1,3 +1,4 @@
X+#define __linux__
X /*******************************************************************
X FFFF v3.2.3
X 
X@@ -24,6 +25,7 @@
X ... and all the others who kindly sent code, fixes, suggestions and feedback !
X *******************************************************************/
X 
X+int get_nprocs() {return 1;}
X 
X // WARNING: This source is a real mess ! :)))
X // WARNING: This is only meant as some "portable" glue for assembly.
X@@ -70,7 +72,7 @@
X   #include <sys/select.h>
X   #include <sys/types.h>
X   #include <sys/sysctl.h>
X-  #include <sys/sysinfo.h>
X+//  #include <sys/sysinfo.h>
X #else
X   #include "GL/glut.h"
X   #include "GL/gl.h"
X@@ -381,7 +383,8 @@
X 	}
X #endif
X  
X-  avail_SSE = checkSSE();
X+  //avail_SSE = checkSSE();
X+  avail_SSE = 1;
X   if (avail_SSE)  {
X #if defined(__APPLE__) && __BIG_ENDIAN__
X     // PowerPC
X@@ -397,7 +400,8 @@
X     printf("Switching to machine code FPU mode.\n");
X   }
X 
X-  avail_SSE2 = checkSSE2();
X+  //avail_SSE2 = checkSSE2();
X+  avail_SSE2 = 1;
X   if (avail_SSE2)  {
X #if defined (sgi)
X     printf("MIPS dual FPU units supported.\n");
X--- ./FragmentProgramARB10.cpp.orig	Tue Sep 19 21:06:09 2006
X+++ ./FragmentProgramARB10.cpp	Wed Sep  6 23:33:11 2006
X@@ -1,3 +1,4 @@
X+#define __linux__
X #include "FragmentProgramARB10.h"
X 
X #include <stdio.h>
X--- ./GPUProgram.h.orig	Tue Sep 19 21:06:09 2006
X+++ ./GPUProgram.h	Wed Sep  6 23:45:32 2006
X@@ -1,4 +1,5 @@
X #pragma once
X+#define __linux__
X 
X #if defined(__APPLE__)
X   #include <OpenGL/gl.h>
X--- ./vpext.cpp.orig	Tue Sep 19 21:06:09 2006
X+++ ./vpext.cpp	Thu Sep  7 07:01:47 2006
X@@ -1,3 +1,4 @@
X+#define __linux__
X #include "vpext.h"
X 
X // This file includes the declarations of all the VP functions
X--- ./Makefile.orig	Tue Sep 19 21:06:09 2006
X+++ ./Makefile	Tue Sep 19 20:17:05 2006
X@@ -0,0 +1,112 @@
X+MAKE=make
X+AR=ar
X+ARFLAGS=-rv
X+AS=as
X+ASFLAGS=-mips4 -64
X+YACC=yacc
X+YFLAGS=
X+LEX=lex
X+LFLAGS= LDFLAGS=
X+CC=gcc
X+#CC=/usr/local/bin/gcc42
X+#CC=/usr/local/bin/gcc41
X+CFLAGS=-O
X+FC=fort77
X+FFLAGS=-O 1
X+GET=get
X+GFLAGS= SCCSFLAGS=
X+SCCSGETFLAGS=-s
X+
X+RM=rm
X+#CP=/usr/local/bin/gcc42
X+#CP=/usr/local/bin/gcc41
X+CP=gcc
X+MKDIR=mkdir -p
X+CHMOD=chmod
X+
X+
X+SHELL = /bin/sh
X+
X+ASSOURCES=$(wildcard *.s)
X+ASOBJ=$(patsubst %.s,$(CFG)/%.o,$(ASSOURCES))
X+
X+SOURCES=$(wildcard *.cpp)
X+SOURCES+=$(wildcard *.cxx)
X+OBJ_=$(patsubst %.cpp,$(CFG)/%.obj,$(SOURCES))
X+OBJ=$(patsubst %.cxx,$(CFG)/%.objx,$(OBJ_))
X+
X+ifeq ($(CFG),)
X+CFG=debug
X+endif
X+
X+ifeq ($(CFG),release)
X+	DBG_FLAG=
X+	DEPENDENCIES=
X+	OUTDIR=$(CFG)
X+	TARGETNAME=$(OUTDIR)/ffff
X+#	CCFLAGS= -g -O3 -ffast-math -mfpmath=sse,387  -msse2    -msse  -mmmx -march=pentium4
X+#	CCFLAGS= -g -O6 -funit-at-a-time -funroll-loops -ffast-math -mfpmath=sse  -msse2 -msse3 -msse  -mmmx -march=pentium4
X+	CCFLAGS= -g -O6 -funit-at-a-time -funroll-loops -ffast-math -mfpmath=sse  -msse2 -msse  -mmmx -march=pentium4
X+	INCLUDEFLAGS= -I/usr/X11R6/include -I/usr/X11R6/include/GL
X+	LINKFLAGS= -L/usr/X11R6/lib -lglut -lGL -lXext -lX11 -lXmu -lGLU -lpthread -lm
X+else
X+	DBG_FLAG=
X+	DEPENDENCIES=
X+	OUTDIR=$(CFG)
X+	TARGETNAME=$(OUTDIR)/ffff
X+#	CCFLAGS= -g -O2 -ggdb3 -funit-at-a-time -funroll-loops -ffast-math -mfpmath=sse  -msse2  -msse3  -msse  -mmmx -march=pentium4
X+	CCFLAGS= -g -ggdb3 -funit-at-a-time -funroll-loops -ffast-math -mfpmath=sse  -msse2  -msse3  -msse  -mmmx -march=pentium4
X+#	CCFLAGS= -ggdb3 -O6 -funit-at-a-time -funroll-loops -ffast-math -mfpmath=sse -msse3  -msse2    -msse  -mmmx -march=pentium4
X+	INCLUDEFLAGS= -I/usr/X11R6/include -I/usr/X11R6/include/GL
X+	LINKFLAGS= -L/usr/X11R6/lib -lglut -lGL -lXext -lX11 -lXmu -lGLU -lpthread -lm
X+endif
X+
X+
X+
X+ifneq ($(DEPENDENCIES),)
X+	DEPM=$(foreach d,$(DEPENDENCIES),$(join $(dir $(d)), Makefile))
X+	DEPB=$(patsubst %,%.build, $(DEPM))
X+	DEPC=$(patsubst %,%.clean, $(DEPM))
X+endif
X+
X+
X+%.build : %
X+	-cd $(dir $<) && $(MAKE) -f $(notdir $<) CFG=$(CFG)
X+
X+%.clean : %
X+	-cd $(dir $<) && $(MAKE) -f $(notdir $<) CFG=$(CFG) clean
X+
X+all:  deps binaries
X+
X+deps: $(DEPB)
X+
X+binaries: $(OUTDIR) $(TARGETNAME)
X+	$(CHMOD) -R a+rw $(OUTDIR)
X+ifeq ($(CFG),release)
X+	strip $(TARGETNAME)
X+endif
X+
X+clean-deps: $(DEPC)
X+
X+$(CFG)/%.obj : %.cpp
X+	$(CC) -c $(CCFLAGS) $(INCLUDEFLAGS) $< -o $@
X+
X+$(CFG)/%.objx : %.cxx
X+	$(CC) -c $(CCFLAGS) $(INCLUDEFLAGS) $< -o $@
X+
X+$(CFG)/%.o : %.s
X+	as $(ASFLAGS) $(INCLUDEFLAGS) $< -o $@
X+
X+$(OUTDIR):
X+	-$(MKDIR) $(OUTDIR)
X+
X+$(TARGETNAME): $(CFG) $(OBJ) $(DEPENDENCIES)
X+	$(CC) -o $@ $(OBJ) $(DEPENDENCIES) $(LINKFLAGS)
X+
X+rebuild: clean all
X+
X+clean: clean-deps
X+	-rm -rf core
X+	-rm -rf $(TARGETNAME)
X+	-rm -rf $(CFG)
X+
X--- ./hole.cpp.save.orig	Tue Sep 19 21:06:09 2006
X+++ ./hole.cpp.save	Thu Sep  7 07:44:39 2006
X@@ -0,0 +1,1122 @@
X+// ----------------------
X+// OpenGL Black Hole Simulator.
X+//
X+// Written by and Copyright Chris Halsall (chalsall@chalsall.com).
X+// First published on the O'Reilly Network on Linux.com
X+// (oreilly.linux.com).  September 2000.  All rights reserved.
X+//
X+// This code is licensed under the GNU GPL Version 2.0.
X+// See (URL: http://www.gnu.org/copyleft/gpl.html ) for details.
X+//
X+// Coded to the groovy tunes of Fluke: Risotto.
X+//
X+// Dedicated to Stephen W. Hawking, one of the greatest explorers 
X+// of our time.
X+
X+#define PROGRAM_TITLE "O'Reilly Net: Black Hole -- C.Halsall"
X+
X+
X+#include <stdio.h>   // Always a good idea.
X+#include <stdlib.h>  // For RAND_MAX.
X+#include <time.h>    // For our FPS stats.
X+#include <math.h>    // For M_PI
X+#include <GL/gl.h>   // OpenGL itself.
X+#include <GL/glu.h>  // GLU support library.
X+#include <GL/glut.h> // GLUT support library.
X+
X+
X+// A quick macro to populate a 1x3 vector array. 
X+#define ourVectInit(a,x,y,z) { (a)[0]=x; (a)[1]=y; (a)[2]=z; }
X+
X+// Structure to hold all the data for each particle.
X+typedef struct {
X+   int Running;
X+   float Pos[3];  // Position.
X+   float Vel[3];  // Velocity.
X+   float Grav[3]; // Acceleration.
X+   float Color[3];
X+} Particle;
X+
X+
X+// ------
X+// Some global variables.
X+
X+// Window and Texture IDs, Window sizes.
X+int Texture_ID;
X+int Window_ID;
X+int Window_Width=640;
X+int Window_Height=480;
X+
X+// We'll request a sphere from the GLU library at run-time.
X+struct GLUquadric *Black_Hole;
X+
X+// "Name" for first (and only) OpenGL display list.
X+#define STAR_FIELD 1
X+
X+
X+// Pointer for allocated array of Particles.
X+Particle *Parts;
X+
X+// Particle status variables.
X+int Parts_Running=0;
X+int Parts_Allocated=0;
X+int Parts_LastUnused=1;
X+
X+// Number of parts initially in the system.  Make sure there's at least
X+// one over a hundred (101, 801), so our Root particle doesn't get deleted.
X+int Parts_Num = 801;
X+
X+float Parts_Brightness = 0.15;
X+
X+
X+// Drawing flags.
X+int Draw_Axis = 0;
X+int Draw_Vectors = 0;
X+int Draw_Stars = 1;
X+int Heads_Up = 0;
X+int Texture_On = 1;
X+
X+
X+// Particle Gun variables.
X+float Gun_PX = 0;
X+float Gun_PY = 0;
X+float Gun_PZ = 4;
X+
X+float Gun_VX =-0.135;
X+float Gun_VY = 0.0125;
X+float Gun_VZ = 0.0;
X+float Gun_R = 0.005;
X+float Gun_OffR = 0.050;
X+
X+// Backwards firing and off-target probabilities.
X+float Gun_Back = 0.9, Gun_Off = 0.9;
X+
X+// '.' key toggels between keypad adjusting gun position and eject vect.
X+int Gun_Control = 1;
X+
X+
X+// Orbit and motion settings.
X+int On_Orbit=1;
X+int Move_Enable=1;
X+int Move_Step=0;
X+
X+// Observer initial placement.
X+float Obs_Angle=114.0;
X+float Obs_Height=.2;
X+float Obs_Dist=4.6;
X+
X+// Calculated observer location.
X+float Obs[3];
X+
X+// How quickly do the orbits decay? 
X+// Lower number (limit 1) is faster decay.
X+
X+int Decay_Factor = 6000;
X+
X+// The force of gravity exterted by the black hole.
X+float Grav = 0.075;
X+
X+// Somewhat arbitrary values for Event and Escape horizons.
X+
X+#define EVENT_HORIZON_GRAV .5
X+
X+#define ESCAPE_HORIZON 30 
X+#define ESCAPE_HORIZON_SQR (ESCAPE_HORIZON * ESCAPE_HORIZON)
X+
X+
X+// ------
X+// Frames per second (FPS) statistic variables and routine.
X+
X+#define FRAME_RATE_SAMPLES 50
X+int FrameCount=0;
X+float FrameRate=0;
X+
X+static void ourDoFPS(
X+   void
X+)
X+{
X+   static clock_t last=0;
X+   clock_t now;
X+   float delta;
X+
X+   if (++FrameCount >= FRAME_RATE_SAMPLES) {
X+      now  = clock();
X+      delta= (now - last) / (float) CLOCKS_PER_SEC;
X+      last = now;
X+
X+      FrameRate = FRAME_RATE_SAMPLES / delta;
X+      FrameCount = 0;
X+   }
X+}
X+
X+
X+// ------
X+// String rendering routine; leverages on GLUT routine.
X+
X+static void ourPrintString(
X+   void *font,
X+   char *str
X+)
X+{
X+   int i,l=strlen(str);
X+
X+   for(i=0;i<l;i++)
X+      glutBitmapCharacter(font,*str++);
X+}
X+
X+
X+// ------
X+// Reallocates our array of particles, adding to or removing as
X+// requested.
X+
X+Particle *ourAllocParticles(
X+   int Num
X+)
X+{
X+   int i;
X+   Particle *P;
X+
X+   P = realloc(Parts, sizeof(Particle) * Num);
X+
X+   if (!P)
X+      return 0;
X+
X+   if (Parts_Allocated < Num)
X+      memset( &P[Parts_Allocated],
X+         0,sizeof(Particle) * (Num-Parts_Allocated));
X+
X+   if (Parts_LastUnused > Num)
X+      Parts_LastUnused = Num;
X+
X+   Parts_Running = 0;
X+   for (i = 0; i < Num; i++)
X+      if (P[i].Running) 
X+         Parts_Running++;
X+
X+   Parts_Allocated = Num;
X+   Parts = P;
X+
X+   return P;
X+}
X+
X+
X+// ------
X+// Function to return a random floating number between 0 and the passed
X+// parameter.
X+
X+float ourRand(
X+   float Max
X+)
X+{
X+   return( (Max * rand()) / RAND_MAX );
X+}
X+
X+
X+// ------
X+// Builds a Display List containing a random star field.
X+//
X+// Note: this could also be done by calculating the star points in this 
X+// routine, which would be faster than having OpenGL perform two 
X+// rotations (matrix multiplications) for each star.  However, this 
X+// technique is simpler and faster for the programmer, and demonstrates 
X+// how successive transformations can be a powerful tool.
X+
X+void ourBuildStarfield(
X+   int Stars
X+)
X+{
X+   int Cnt;
X+
X+   glNewList(STAR_FIELD, GL_COMPILE);
X+
X+   glMatrixMode(GL_MODELVIEW);
X+   glPushMatrix();
X+
X+   for ( Cnt = 0; Cnt < Stars; Cnt++) {
X+
X+      // Vary the color for each star.
X+      glColor4f(
X+         0.8 + ourRand(0.2),
X+         0.8 + ourRand(0.2),
X+         0.8 + ourRand(0.2),
X+         .95); 
X+
X+      // Vary the size.  Ensure integer sizes to avoid alias shimmering.
X+      glPointSize(ourRand(2) > 1 ? 1.0 : 2.0);
X+
X+      // Spin your Universe, round and round....
X+      glRotatef(ourRand(100),0.0f,1.0f,0.0f);
X+      glRotatef(ourRand(100),1.0f,0.0f,0.0f);
X+
X+      glBegin(GL_POINTS);
X+         glVertex3f(15.0, 0.0f, 0.0f);
X+      glEnd();
X+   }
X+
X+   glPopMatrix();
X+   glEndList();
X+}
X+
X+
X+// ------
X+// Function builds a simple alpha channel texture of a dot,
X+// and then creates mipmaps.  This could instead load textures from
X+// graphics files from disk, or render textures based on external
X+// input.
X+
X+void ourBuildTextures(
X+   void
X+)
X+{
X+   GLenum gluerr;
X+   GLubyte tex[128][128];
X+   int x,y,t;
X+   int hole_size = 3300; // ~ == 57.45 ^ 2.
X+
X+   // Generate a texture index, then bind it for future operations.
X+   glGenTextures(1,&Texture_ID);
X+   glBindTexture(GL_TEXTURE_2D,Texture_ID);
X+
X+   // Iterate across the texture array.
X+  
X+   for(y=0;y<128;y++) {
X+      for(x=0;x<128;x++) {
X+
X+        // Make a round dot in the texture's alpha-channel.
X+
X+         // Calculate distance to center (squared).
X+         t = (x-64)*(x-64) + (y-64)*(y-64) ;
X+
X+         if ( t < hole_size) // Don't take square root; compare squared.
X+            tex[x][y]= 240 - (240 * t) / hole_size + ourRand(15);
X+         else
X+            tex[x][y]=0;   // Outside of the dot, it's transparent.
X+
X+      }
X+   }
X+
X+   // The GLU library helps us build MipMaps for our texture.
X+
X+   if ((gluerr=gluBuild2DMipmaps(GL_TEXTURE_2D, 1, 128, 128, GL_ALPHA,
X+                 GL_UNSIGNED_BYTE, (void *)tex))) {
X+
X+      fprintf(stderr,"GLULib%s\n",gluErrorString(gluerr));
X+      exit(-1);
X+   }
X+
X+   // Some pretty standard settings for wrapping and filtering.
X+   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
X+   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
X+
X+   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
X+   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
X+
X+   // We start with GL_MODULATE mode.
X+   glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
X+}
X+
X+
X+// ------
X+// Callback routine executed whenever our window is resized.  Lets us
X+// request the newly appropriate perspective projection matrix for
X+// our needs.  Try removing the gluPerspective() call to see what happens.
X+
X+void cbResizeScene(
X+   int Width,
X+   int Height
X+)
X+{
X+   // Let's not core dump, no matter what.
X+   if (Height == 0)
X+      Height = 1;
X+
X+   glViewport(0, 0, Width, Height);
X+
X+   glMatrixMode(GL_PROJECTION);
X+   glLoadIdentity();
X+   gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.05f,100.0f);
X+
X+   glMatrixMode(GL_MODELVIEW);
X+
X+   Window_Width  = Width;
X+   Window_Height = Height;
X+}
X+
X+
X+// ------
X+// Fires the Particle Gun, or, sets up the passed Particle to be
X+// placed at the Particle Gun location, and fired in a direction
X+// specified in Gun_Va, with 'a' being "X", "Y", or "Z".
X+//
X+// The particles are normally fired with a randomness of Gun_R.
X+// Occationally (by default 10%) a larger randomness of Gun_OffR is
X+// added.  Also, 10% of the time, the particles are fired backwards.
X+// This is used to introduce a bit of non-uniformity.  Gun_R and
X+// Gun_OffR can be controled with the '3' and '6', and '/' and '*'
X+// keys, respectively.  If set to zero, effects are removed.
X+
X+static void ourFireParticleGun(
X+   Particle *p
X+)
X+{
X+   float r;
X+   int c;
X+   int Dir = 1;
X+
X+   if (!p->Running) {
X+      p->Running=1;
X+      Parts_Running++;
X+   }
X+
X+   if (p == Parts) {  // Root part.
X+       ourVectInit(p->Color,1,1,0);                // Bright Yellow
X+       ourVectInit(p->Pos,5.0,0,0);                // Location
X+       ourVectInit(p->Vel,0,Gun_VY,-Gun_VX*0.95);  // Velocity
X+       return;
X+   }
X+
X+   // Regular particle.
X+
X+   ourVectInit(p->Pos,Gun_PX,Gun_PY,Gun_PZ);
X+   r = Gun_R;
X+
X+// This creates a few a very unpredicatable trajectories.  It actually
X+// works out to be much less than a full 10 percent, as many are eatten
X+// or escape within a very short period of time.  Only a few enter a 
X+// stable orbit. 
X+
X+   if (ourRand(1) > Gun_Off) r += Gun_OffR; 
X+
X+   if (ourRand(1) > Gun_Back) Dir = -1;
X+
X+   ourVectInit(p->Vel,
X+      (Gun_VX + r-ourRand(2*r)) * Dir,
X+      (Gun_VY + r-ourRand(2*r)) * Dir,
X+      (Gun_VZ + r-ourRand(2*r)) * Dir);
X+
X+   c = (int)(ourRand(5) + 1.5); // Range of 1 to 6.
X+
X+   // The last set of numbers bias the colors to blue.  Red is nice too.
X+
X+   ourVectInit(p->Color,
X+      (c & 0x01 ? 0.9 : 1.0) * 0.7,
X+      (c & 0x02 ? 0.9 : 1.0) * 0.7,
X+      (c & 0x04 ? 0.9 : 1.0) * 1.0
X+      );
X+}
X+
X+
X+// ------
X+// Calculates the next position based on the current velocity vector,
X+// then calculates the new velocity vector based on the particle's 
X+// proximity to the black hole.
X+//
X+// We do the motion calculation before updating the velocity vector
X+// (and calculating the acceleration-because-of-gravity vector) so 
X+// that our Vector Display option will be correct.  If we didn't do
X+// this, the gravity vector would not point towards (0,0,0) when
X+// we drew it, outside of this function.
X+
X+static void ourMoveParticle(
X+   Particle *p
X+)
X+{
X+   float dp2, dsx, dsy, dsz, G, d;
X+
X+   //  Used to randomly kill and re-create particles.
X+   if (p != Parts)
X+      if (ourRand(1) >  0.9998) {
X+      ourFireParticleGun(p);
X+      return;
X+   }
X+
X+   // We're actually going to move this particle...
X+
X+   // We first move it based on the LAST iteration's 
X+   // calculation of the Velocity...
X+   p->Pos[0] += p->Vel[0];
X+   p->Pos[1] += p->Vel[1];
X+   p->Pos[2] += p->Vel[2];
X+
X+   // ...and then proceed to calculate the force of gravity at the new 
X+   // position, and update our velocity vector.
X+
X+   dsx = p->Pos[0] * p->Pos[0];
X+   dsy = p->Pos[1] * p->Pos[1];
X+   dsz = p->Pos[2] * p->Pos[2];
X+
X+   // Calculate the square of the distance.
X+   dp2 = dsx + dsy + dsz;
X+
X+   if (dp2) {
X+      // May wish to scale dp2 (change 1.0); effects gravity gradiant.
X+      G = Grav / (dp2 * 1.0);  
X+      d = sqrt(dp2);
X+   }
X+
X+   // If the force of gravity is too strong, our algorithim breaks
X+   // down and conservation of energy isn't maintained.  We consider 
X+   // this the event horizon, and recycle the particle.
X+   if (G > EVENT_HORIZON_GRAV) {
X+      ourFireParticleGun(p);
X+      return;
X+   }
X+
X+   if (dp2 > ESCAPE_HORIZON_SQR) { 
X+      // Particle escaped; lucky it.
X+      ourFireParticleGun(p);
X+      return;
X+   }
X+
X+   // OK, this particle is staying in the system.  Calculate the 
X+   // vectors....
X+
X+   // We store the components of the force of gravity for 
X+   // our Vectors display.  Note the negative magnitude; the vector
X+   // must point _from_ our particle _towards_ (0,0,0).
X+   p->Grav[0] = - G * p->Pos[0] / d;
X+   p->Grav[1] = - G * p->Pos[1] / d;
X+   p->Grav[2] = - G * p->Pos[2] / d;
X+
X+   // Simply add the gravity vector to the current velocity vector.
X+   p->Vel[0] += p->Grav[0];
X+   p->Vel[1] += p->Grav[1];
X+   p->Vel[2] += p->Grav[2];
X+
X+   if (p != Parts) {
X+      // This handles orbit decay; not correctly, but well enough.
X+      // (Decay should be a ratio to the vector length, applied to each 
X+      // vector component here, rather than each component being effected
X+      // based on its individual size.  The effect is to circlurize the
X+      // orbit, which we want anyway.)
X+
X+      p->Vel[0] -= p->Vel[0] / Decay_Factor;
X+      p->Vel[1] -= p->Vel[1] / Decay_Factor;
X+      p->Vel[2] -= p->Vel[2] / Decay_Factor;
X+   }
X+}
X+
X+
X+// ------
X+// Angle to Radian conversion.
X+
X+float ourA2R(
X+   float Angle
X+)
X+{
X+   return Angle * M_PI/180;
X+}
X+
X+
X+// ------
X+// Calculates the observer's XYZ position from their Distance from 
X+// the origin, the angle and the height.
X+
X+static void ourCalcObs(void)
X+{
X+  Obs[0]=Obs_Dist * sin(ourA2R(Obs_Angle));
X+  Obs[1]=Obs_Height;
X+  Obs[2]=Obs_Dist * cos(ourA2R(Obs_Angle));
X+}
X+
X+
X+// ------
X+// Draws the X, Y, and Z axis lines.
X+
X+void ourRenderAxis(
X+   void
X+)
X+{
X+   glBegin(GL_LINES);
X+
X+   glColor4f(0.5,0.5,0.0,1.0);  // Mid-level yellow.
X+
X+   // Three primary axis lines.
X+   glVertex3f(100,0,0);
X+   glVertex3f(-100,0,0);
X+   glVertex3f(0,100,0);
X+   glVertex3f(0,-100,0);
X+   glVertex3f(0,0,100);
X+   glVertex3f(0,0,-100);
X+
X+   glColor4f(0.25,0.25,0.0,1.0);  // Low-level yellow.
X+
X+   // Two pairs of secondary lines for X and Z axis.
X+   glVertex3f(100,1,0);
X+   glVertex3f(-100,1,0);
X+   glVertex3f(100,-1,0);
X+   glVertex3f(-100,-1,0);
X+
X+   glVertex3f(0,1,100);
X+   glVertex3f(0,1,-100);
X+   glVertex3f(0,-1,100);
X+   glVertex3f(0,-1,-100);
X+
X+   glColor4f(0.0,0.5,0.0,1.0); // Mid-level green.
X+
X+   // Lable the X axis.
X+   glVertex3f(1.0,0.9,0);
X+   glVertex3f(1.1,0.8,0);
X+   glVertex3f(1.1,0.9,0);
X+   glVertex3f(1.0,0.8,0);
X+
X+   // And the Z.
X+   glVertex3f(0,0.9,1.0);
X+   glVertex3f(0,0.9,1.1);
X+   glVertex3f(0,0.9,1.1);
X+   glVertex3f(0,0.8,1.0);
X+   glVertex3f(0,0.8,1.0);
X+   glVertex3f(0,0.8,1.1);
X+
X+   glEnd();
X+}
X+
X+
X+// ------
X+// Draws the Gravity and Velocity Vectors for each active particle.
X+
X+void ourRenderVectors(
X+   void
X+)
X+{
X+   int i;
X+
X+   glBegin(GL_LINES);
X+
X+   for (i=0; i<Parts_Num; i++) {
X+
X+      if (!Parts[i].Running) continue;
X+
X+      // Draw the velocity vector as green.
X+      glColor4f(0.0,1.0,0.0,Parts_Brightness + .5);
X+
X+      glVertex3f(
X+         Parts[i].Pos[0],
X+         Parts[i].Pos[1],
X+         Parts[i].Pos[2]
X+         );
X+
X+      glVertex3f(
X+         Parts[i].Pos[0] + Parts[i].Vel[0] ,
X+         Parts[i].Pos[1] + Parts[i].Vel[1] ,
X+         Parts[i].Pos[2] + Parts[i].Vel[2] 
X+         );
X+
X+      // Draw the gravity vector as red.
X+      glColor4f(1.0,0.0,0.0,Parts_Brightness + .5);
X+
X+      glVertex3f(
X+         Parts[i].Pos[0],
X+         Parts[i].Pos[1],
X+         Parts[i].Pos[2]
X+         );
X+
X+      glVertex3f(
X+         Parts[i].Pos[0] + Parts[i].Grav[0] ,
X+         Parts[i].Pos[1] + Parts[i].Grav[1] ,
X+         Parts[i].Pos[2] + Parts[i].Grav[2] 
X+      );
X+    }
X+
X+    glEnd();
X+}
X+
X+
X+// ------
X+// Draws the heads-up-display.
X+void ourRenderHeadsUp(
X+  void
X+)
X+{
X+   char buf[80];
X+
X+   glLoadIdentity();
X+     // We need to change the projection matrix for the text rendering.
X+   glMatrixMode(GL_PROJECTION);
X+
X+   // But we like our current view too; so we save it here.
X+   glPushMatrix();
X+
X+   // Now we set up a new projection for the text.
X+   glLoadIdentity();
X+   glOrtho(0,Window_Width,0,Window_Height,-1.0,1.0);
X+
X+   // No need for textured text.
X+   glDisable(GL_TEXTURE_2D);
X+
X+   // We don't want depth-testing either.
X+   glDisable(GL_DEPTH_TEST);
X+
X+
X+   // Draw various variables for the user.
X+
X+   glColor3f(1.0,1.0,0.0);
X+
X+   sprintf(buf,"Parts: %d / %d  Bright:%.2f", 
X+      Parts_Running, Parts_Allocated, Parts_Brightness);
X+   glRasterPos2i(10,48);
X+   ourPrintString(GLUT_BITMAP_HELVETICA_12,buf);
X+
X+   sprintf(buf,"Rnd - Normal:%.3f Extreme:%.3f",
X+      Gun_R, Gun_OffR);
X+   glRasterPos2i(10,6);
X+   ourPrintString(GLUT_BITMAP_HELVETICA_12,buf);
X+
X+   if (Gun_Control)
X+      glColor3f(1.0,1.0,0.0);
X+   else
X+      glColor3f(0.5,1.0,0.0);
X+
X+   sprintf(buf,"GunP: (%.3f,%.3f,%.3f)", Gun_PX, Gun_PY, Gun_PZ);
X+   glRasterPos2i(10,34);
X+   ourPrintString(GLUT_BITMAP_HELVETICA_12,buf);
X+
X+   if (!Gun_Control)
X+      glColor3f(1.0,1.0,0.0);
X+   else
X+      glColor3f(0.5,1.0,0.0);
X+
X+   sprintf(buf,"GunV: (%.3f,%.3f,%.3f)",
X+      Gun_VX, Gun_VY, Gun_VZ);
X+   glRasterPos2i(10,20);
X+   ourPrintString(GLUT_BITMAP_HELVETICA_12,buf);
X+
X+   // Now we want to render the calulated FPS at the top.
X+  
X+   // To ease, simply translate up.  Note we're working in screen
X+   // pixels in this projection.
X+  
X+   glTranslatef(6.0f,Window_Height - 14,0.0f);
X+
X+   glColor4f(0.9,0.2,0.2,.95);
X+   sprintf(buf,"FPS: %f F: %2d", FrameRate, FrameCount);
X+   glRasterPos2i(6,0);
X+   ourPrintString(GLUT_BITMAP_HELVETICA_12,buf);
X+
X+   // Lets also show the current position of the Root Particle
X+   sprintf(buf,"RootP: ( %-.4f, %-.4f, %-.4f)", 
X+      Parts->Pos[0], Parts->Pos[1],Parts->Pos[2]);
X+   glRasterPos2i(6,-16);
X+   ourPrintString(GLUT_BITMAP_HELVETICA_12,buf);
X+
X+   // And the Root Particle's velocity.
X+   sprintf(buf,"RootV: ( %-.4f, %-.4f, %-.4f)", 
X+      Parts->Vel[0], Parts->Vel[1],Parts->Vel[2]);
X+   glRasterPos2i(6,-32);
X+   ourPrintString(GLUT_BITMAP_HELVETICA_12,buf);
X+
X+   // Done with this special projection matrix.  Throw it away.
X+   glPopMatrix();
X+}
X+
X+
X+// ------
X+// Routine which actually does the drawing
X+
X+static void cbRenderScene(void)
X+{
X+   Particle *p;
X+   int i;
X+
X+   // For the first few objects, we want full depth-buffer testing.
X+   glEnable(GL_DEPTH_TEST);
X+   glDepthMask(GL_TRUE);
X+
X+   // Clear the screen.
X+   glClearColor(0.00,0.00,0.00,1.0);
X+   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
X+
X+   // Ensure we're working with the model matrix.
X+   glMatrixMode(GL_MODELVIEW);
X+
X+   // Reset to 0,0,0; no rotation, no scaling.
X+   glLoadIdentity();
X+
X+   // Are we on-orbit, or wandering around?
X+   if (On_Orbit) {
X+      gluLookAt(Parts->Pos[0],Parts->Pos[1],Parts->Pos[2],
X+         0.0,0.0,0.0,
X+         0.0,1.0,0.0);
X+   } else {
X+      gluLookAt(Obs[0],Obs[1],Obs[2],
X+         0.0,0.0,0.0,
X+         0.0,1.0,0.0);
X+   }
X+
X+   // No texturing.
X+   glDisable(GL_TEXTURE_2D);
X+
X+   // Black holes are BLACK!
X+   glColor4f(0.0,0.0,0,1.0);
X+   gluSphere(Black_Hole,0.02,8,8);
X+
X+   if (Draw_Stars)
X+      glCallList(STAR_FIELD);
X+
X+   if (Draw_Vectors)
X+      ourRenderVectors();
X+
X+   if (Draw_Axis)
X+      ourRenderAxis();
X+
X+   // We don't want any of the particles to obscure any others, but
X+   // we DO want the black hole to block any particles behind it.
X+   // Note that GL_DEPTH_TEST is still enabled.
X+   glDepthMask(GL_FALSE);
X+
X+   // Enable the dot texture.  "Oh no!  Not THE DOT!"
X+   if (Texture_On)
X+      glEnable(GL_TEXTURE_2D);
X+
X+   // Iterate through the array of particles, drawing all that are
X+   // active.  For those that aren't active, 0.03% of the time, we 
X+   // introduce them into the system.
X+
X+   for(i=0; i<Parts_Num; i++) {
X+      p = &Parts[i];
X+
X+      if (!p->Running) {
X+         if (Move_Enable && ourRand(1) > 0.9997)
X+            ourFireParticleGun(p);
X+      } else {
X+         // Set the part's color.
X+         glColor4f(p->Color[0],p->Color[1],p->Color[2],
X+            Parts_Brightness);
X+
X+         // Draw two intersecting quads, along XY and ZY axis.
X+         glBegin(GL_QUADS);
X+
X+         glTexCoord2f(0.0,0.0);
X+         glVertex3f(p->Pos[0]-.00,p->Pos[1]-.10,p->Pos[2]-.10);
X+         glTexCoord2f(1.0,0.0);
X+         glVertex3f(p->Pos[0]-.00,p->Pos[1]+.10,p->Pos[2]-.10);
X+         glTexCoord2f(1.0,1.0);
X+         glVertex3f(p->Pos[0]-.00,p->Pos[1]+.10,p->Pos[2]+.10);
X+         glTexCoord2f(0.0,1.0);
X+         glVertex3f(p->Pos[0]-.00,p->Pos[1]-.10,p->Pos[2]+.10);
X+
X+         glTexCoord2f(0.0,0.0);
X+         glVertex3f(p->Pos[0]-.10,p->Pos[1]-.10,p->Pos[2]-.00);
X+         glTexCoord2f(1.0,0.0);
X+         glVertex3f(p->Pos[0]-.10,p->Pos[1]+.10,p->Pos[2]-.00);
X+         glTexCoord2f(1.0,1.0);
X+         glVertex3f(p->Pos[0]+.10,p->Pos[1]+.10,p->Pos[2]+.00);
X+         glTexCoord2f(0.0,1.0);
X+         glVertex3f(p->Pos[0]+.10,p->Pos[1]-.10,p->Pos[2]+.00);
X+
X+         glEnd();
X+
X+         if (Move_Enable)
X+            ourMoveParticle(p);
X+      }
X+   }
X+
X+
X+   if (Heads_Up)
X+      ourRenderHeadsUp();
X+
X+   // All done drawing.  Let's show it.
X+   glutSwapBuffers();
X+
X+   // This handles our single-step function.
X+   if (Move_Step)
X+      Move_Step = Move_Enable = 0;
X+
X+   // Collect the FPS statistics.
X+   ourDoFPS();
X+}
X+
X+
X+// ------
X+// Callback function called when a normal key is pressed.
X+
X+void cbKeyPressed(
X+   unsigned char key, 
X+   int x, int y
X+)
X+{
X+   int t;
X+
X+   switch (key) {
X+   case 27:
X+   case 'q': case 'Q':
X+      exit(0);
X+      break;
X+
X+   // Toggle drawing.
X+   case 'a': case 'A':
X+      Draw_Axis = !Draw_Axis;
X+      break;
X+   case 'v': case 'V':
X+      Draw_Vectors = !Draw_Vectors;
X+      break;
X+   case 's': case 'S':
X+     Draw_Stars = !Draw_Stars;
X+     break;
X+
X+   // Adjust particle brightness.
X+   case 'b':
X+     Parts_Brightness+=0.01;
X+     break;
X+   case 'B':
X+     Parts_Brightness-=0.01;
X+     break;
X+
X+   // Toggle being on-orbit.
X+   case 'o': case 'O':
X+      On_Orbit = ! On_Orbit;
X+      break;
X+
X+   // Toggle Texture.
X+   case 't': case 'T':
X+      Texture_On = !Texture_On;
X+      break;
X+
X+   // Impart an impulse on the Root particle.
X+   case 'x':
X+      Parts->Vel[0]+=.0001;
X+      break;
X+   case 'X':
X+      Parts->Vel[0]-=.0001;
X+      break;
X+   case 'c': case 'y':
X+      Parts->Vel[1]+=.0001;
X+      break;
X+   case 'C': case 'Y':
X+      Parts->Vel[1]-=.0001;
X+      break;
X+   case 'z':
X+      Parts->Vel[2]+=.0001;
X+      break;
X+   case 'Z':
X+      Parts->Vel[2]-=.0001;
X+      break;
X+
X+   case 'r': case 'R':
X+      ourFireParticleGun(Parts);
X+
X+   // Single-step through motion calculations.
X+   case 'm':
X+      Move_Step = 1;
X+      Move_Enable = 1;
X+      break;
X+
X+   // Normal motion.
X+   case 'M':
X+      Move_Enable= !Move_Enable;
X+      break;
X+
X+   // Heads up display.
X+   case 'h': case 'H':
X+      Heads_Up = !Heads_Up;
X+      break;
X+
X+   // Inject one (or all) free particle(s).
X+   case 'i': case 'I':
X+      for (t=Parts_LastUnused; t<Parts_Num; t++) {
X+         if (!Parts[t].Running) {
X+            ourFireParticleGun(&Parts[t]);
X+            Parts_LastUnused = t;
X+            if (key == 'i') break;
X+         }
X+      }
X+      break;
X+
X+   // Toggle gun control between ejection velocity and position.
X+   case '.':
X+      Gun_Control= !Gun_Control;
X+      break;
X+
X+   // Control Particle Gun velocity vector.
X+   case '7':
X+      if (Gun_Control) Gun_VZ-=0.001; else Gun_PZ-=0.01;
X+      break;
X+   case '8':
X+      if (Gun_Control) Gun_VZ+=0.001; else Gun_PZ+=0.01;
X+      break;
X+
X+   case '4':
X+      if (Gun_Control) Gun_VY-=0.001; else Gun_PY-=0.01;
X+      break;
X+   case '5':
X+      if (Gun_Control) Gun_VY+=0.001; else Gun_PY+=0.01;
X+      break;
X+
X+   case '1':
X+      if (Gun_Control) Gun_VX-=0.001; else Gun_PX-=0.01;
X+      break;
X+   case '2':
X+      if (Gun_Control) Gun_VX+=0.001; else Gun_PX+=0.01;
X+      break;
X+
X+   // Range of randomness which is added to each initial velocity vector.
X+   case '3':
X+      Gun_R-=0.001;
X+      break;
X+   case '6':
X+      Gun_R+=0.001;
X+      break;
X+
X+   // Controls the large random shots which occur rarely. When set to 
X+   // 0, the system becomes highly controlled.
X+   case '/':
X+      Gun_OffR -= .001;
X+      break;
X+   case '*':
X+      Gun_OffR += .001;
X+      break;
X+
X+   // Adds or removes particles to/from the system.
X+   case '-':
X+      if (Parts_Num > 100) {
X+         Parts_Num -= 100;
X+         ourAllocParticles(Parts_Num);
X+      }
X+      break;
X+
X+   case '+':
X+      Parts_Num += 100;
X+      ourAllocParticles(Parts_Num);
X+      break;
X+
X+   default:
X+      printf("No action for %d\n", key);
X+
X+   }
X+}
X+
X+
X+// ------
X+// Callback Function called when a special key is pressed.
X+
X+static void cbSpecialKeyPressed(int key, int x, int y)
X+{
X+   switch (key) {
X+   case GLUT_KEY_PAGE_UP:
X+      Obs_Dist -= 0.05f;
X+      break;
X+
X+   case GLUT_KEY_PAGE_DOWN:
X+      Obs_Dist += 0.05f;
X+      break;
X+
X+   case GLUT_KEY_LEFT:
X+      Obs_Angle-=2.0;
X+      break;
X+
X+   case GLUT_KEY_RIGHT:
X+      Obs_Angle+=2.0;
X+      break;
X+
X+   case GLUT_KEY_DOWN:
X+      Obs_Height-=0.05;
X+      break;
X+
X+   case GLUT_KEY_UP:
X+      Obs_Height+=0.06;
X+      break;
X+   }
X+
X+   // We don't know anything changed, but it never hurts.
X+   ourCalcObs();
X+}
X+
X+
X+// ------
X+// Does everything needed before losing control to the main
X+// OpenGL event loop
X+
X+void ourInit(
X+   int Width,
X+   int Height
X+)
X+{
X+   ourBuildTextures();
X+   ourBuildStarfield(500);
X+
X+   glEnable(GL_BLEND);
X+   glDisable(GL_ALPHA_TEST);
X+
X+   // Enable flat shading -- no need for smooth.
X+   glShadeModel(GL_FLAT);
X+
X+   // Blending mode used for fire, lit gas, etc.
X+   glBlendFunc(GL_SRC_ALPHA,GL_ONE);
X+
X+   // Calculate the non-on-orbit observer's position.
X+   ourCalcObs();
X+
X+   // Load up the correct perspective matrix; using a callback directly.
X+   cbResizeScene(Width, Height);
X+
X+   if (!(Black_Hole = gluNewQuadric()))
X+      exit;
X+
X+   // Allocate our first block of particles.
X+   ourAllocParticles(Parts_Num);
X+
X+   // Fire off the first (Root) Particle.
X+   ourFireParticleGun(Parts);
X+
X+}
X+
X+
X+// ------
X+// The main() function.  Inits OpenGL.  Calls our own init function,
X+// then passes control onto OpenGL.
X+
X+int main(int argc,char **argv)
X+{
X+   glutInit(&argc,argv);
X+
X+   // To see OpenGL drawing, take out the GLUT_DOUBLE request.
X+   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
X+   glutInitWindowSize(Window_Width,Window_Height);
X+
X+   // Open a window
X+
X+   if (!(Window_ID=glutCreateWindow( PROGRAM_TITLE ))) {
X+      fprintf(stderr,"Error opening a window.\n");
X+      exit(-1);
X+   }
X+  
X+   // Register the callback function to do the drawing.
X+   glutDisplayFunc(&cbRenderScene);
X+
X+   // If there's nothing to do, draw.
X+   glutIdleFunc(&cbRenderScene);
X+
X+   // It's a good idea to know when our window's resized.
X+   glutReshapeFunc(&cbResizeScene);
X+
X+   // And let's get some keyboard input.
X+   glutKeyboardFunc(&cbKeyPressed);
X+   glutSpecialFunc(&cbSpecialKeyPressed);
X+
X+   // OK, OpenGL's ready to go.  Let's call our own init function.
X+   ourInit(Window_Width, Window_Height);
X+
X+   // Print out a bit of help dialog.
X+   printf("\n" PROGRAM_TITLE "\n\n\
X+Use arrow keys to rotate around or move along Y axis.\n\
X+Page up/down will move observer towards/away from the Y axis.\n\n\
X+'O' toggles observer onto non-decaying orbit of yellow root particle.\n\
X+'H' toggles heads-up-display of various status variables.\n\n\
X+'x'/'X', 'c'/'C', 'z'/'Z' thrusts root particle along X, Y, Z axis\n\
X+in positive/negative directions, respectively; 'R' resets.\n\n\
X+Numerical Keypad controls Particle Gun parameters.  '.' key switches\n\
X+between effecting Gun Velocity Vector and Position.\n\n\
X+'+' and '-' add to or remove particles from the system.\n\n\
X+Use first letter of shown display mode settings to alter.\n\n\
X+Q or [Esc] to quit; OpenGL window must have focus for input.\n");
X+
X+   // Pass off control to OpenGL.
X+   // Above functions are called as appropriate.
X+   glutMainLoop();
X+
X+   // Free our allocated particle array.
X+   ourAllocParticles(0);
X+
X+   return 1;
X+}
X+
END-of-./files/patch-file
exit
--- sendFile ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200609202101.k8KL1fGD074019>