From 68d41e9c63f7607bf30569316b50a5cbaa2cfc4b Mon Sep 17 00:00:00 2001 From: josh Date: Mon, 22 Sep 2008 17:27:40 +0000 Subject: [PATCH] moved files to trunk directory git-svn-id: svn://anubis/dwscr/trunk@83 5bef9df8-b654-44bb-925b-0ff18baa8f8c --- LoadFile/LoadFile.cc | 42 +++ LoadFile/LoadFile.h | 2 + LoadFile/Makefile | 17 ++ LoadFile/genLoadFile.pl | 58 ++++ Makefile | 74 +++++ displayinfo-win32.cc | 55 ++++ displayinfo.cc | 30 ++ displayinfo.h | 9 + dwscr.cc | 97 ++++++ logo/dwlogo.blend | Bin 0 -> 169904 bytes logo/dwlogo.mtl | 22 ++ logo/dwlogo.obj | 645 ++++++++++++++++++++++++++++++++++++++++ make-win32.bat | 1 + ss/LogoBox.cpp | 112 +++++++ ss/LogoBox.h | 31 ++ ss/Makefile | 15 + ss/PlainSpin.cc | 46 +++ ss/PlainSpin.h | 23 ++ ss/SSMain.cc | 151 ++++++++++ ss/SSMain.h | 41 +++ ss/SSMode.cc | 51 ++++ ss/SSMode.h | 31 ++ ss/Towers.cc | 302 +++++++++++++++++++ ss/Towers.h | 42 +++ ss/TumblingLogos.cc | 244 +++++++++++++++ ss/TumblingLogos.h | 43 +++ 26 files changed, 2184 insertions(+) create mode 100644 LoadFile/LoadFile.cc create mode 100644 LoadFile/LoadFile.h create mode 100644 LoadFile/Makefile create mode 100755 LoadFile/genLoadFile.pl create mode 100644 Makefile create mode 100644 displayinfo-win32.cc create mode 100644 displayinfo.cc create mode 100644 displayinfo.h create mode 100644 dwscr.cc create mode 100644 logo/dwlogo.blend create mode 100644 logo/dwlogo.mtl create mode 100644 logo/dwlogo.obj create mode 100755 make-win32.bat create mode 100644 ss/LogoBox.cpp create mode 100644 ss/LogoBox.h create mode 100644 ss/Makefile create mode 100644 ss/PlainSpin.cc create mode 100644 ss/PlainSpin.h create mode 100644 ss/SSMain.cc create mode 100644 ss/SSMain.h create mode 100644 ss/SSMode.cc create mode 100644 ss/SSMode.h create mode 100644 ss/Towers.cc create mode 100644 ss/Towers.h create mode 100644 ss/TumblingLogos.cc create mode 100644 ss/TumblingLogos.h diff --git a/LoadFile/LoadFile.cc b/LoadFile/LoadFile.cc new file mode 100644 index 0000000..d3112f9 --- /dev/null +++ b/LoadFile/LoadFile.cc @@ -0,0 +1,42 @@ + +#include +#include +#include "LoadFile.h" + +typedef struct { + const char * filename; + unsigned char * data; + int length; +} fileref_t; + +/* The data section is generated by a perl script. The contents of the + * included file are part of this logical program unit, which is why it + * is directly included instead of compiling it separately. + */ +#include "LoadFile-gen.inc" + +const static int numFiles = sizeof(LoadFileData)/sizeof(fileref_t); + +static std::map< std::string, fileref_t * > * LoadFileMap = NULL; + +void * LoadFile(const char * filename, unsigned int * length) +{ + if (LoadFileMap == NULL) + { + LoadFileMap = new std::map< std::string, fileref_t * >(); + for (int i = 0; i < numFiles; i++) + { + (*LoadFileMap)[std::string(LoadFileData[i].filename)] = &LoadFileData[i]; + } + } + + std::map< std::string, fileref_t * >::iterator it = + LoadFileMap->find(std::string(filename)); + + if (it == LoadFileMap->end()) + return NULL; + + *length = it->second->length; + return it->second->data; +} + diff --git a/LoadFile/LoadFile.h b/LoadFile/LoadFile.h new file mode 100644 index 0000000..7261b72 --- /dev/null +++ b/LoadFile/LoadFile.h @@ -0,0 +1,2 @@ + +void * LoadFile(const char * filename, unsigned int * length); diff --git a/LoadFile/Makefile b/LoadFile/Makefile new file mode 100644 index 0000000..ca32666 --- /dev/null +++ b/LoadFile/Makefile @@ -0,0 +1,17 @@ + +GENLOADFILE := perl genLoadFile.pl +LOGO_PATH := ../logo +LOADFILES += dwlogo.obj +LOADFILES += dwlogo.mtl +ABSLOADFILES := $(foreach lf,$(LOADFILES),$(LOGO_PATH)/$(lf)) + +all: LoadFile.o + +LoadFile.o: LoadFile.cc LoadFile-gen.inc + $(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS) + +LoadFile-gen.inc: genLoadFile.pl $(ABSLOADFILES) + $(GENLOADFILE) --root=$(LOGO_PATH) $(LOADFILES) + +clean: + -$(RM) -f *~ *.o LoadFile-gen.inc diff --git a/LoadFile/genLoadFile.pl b/LoadFile/genLoadFile.pl new file mode 100755 index 0000000..46885b6 --- /dev/null +++ b/LoadFile/genLoadFile.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Getopt::Long; + +my $root = '.'; +GetOptions('root=s' => \$root); + +my @fileList = @ARGV; +if ($#fileList < 0) +{ + print "$0 \n"; + exit(42); +} + +my %fileData; +my $index = 0; + +open(OUTPUT, '>', 'LoadFile-gen.inc'); +chdir($root); +foreach my $fileName (@fileList) +{ + local $/; + open(FILE, '<', $fileName); + my $fileContents = ; + close(FILE); + my $length = length($fileContents); + my @fileContents = split(//, $fileContents); + my $cname = "dat$index"; + print OUTPUT "\nstatic unsigned char ${cname}[] = {\n"; + for (my $byteNum = 0; $byteNum <= $#fileContents; $byteNum++) + { + print OUTPUT " " if ($byteNum % 12 == 0); + printf OUTPUT ("0x%02x", ord($fileContents[$byteNum])); + print OUTPUT ", " unless ($byteNum == $#fileContents); + print OUTPUT "\n" if ($byteNum % 12 == 11); + } + print OUTPUT "\n};\n"; + $index++; + $fileData{$fileName} = [$cname, $length]; +} + +print OUTPUT "\nfileref_t LoadFileData[] = {\n"; + +my @fileNames = keys(%fileData); +for (my $fileIndex = 0; $fileIndex <= $#fileNames; $fileIndex++) +{ + my $fileName = $fileNames[$fileIndex]; + printf OUTPUT (' {"%s", %s, %s}', + $fileName, + $fileData{$fileName}->[0], + $fileData{$fileName}->[1]); + print OUTPUT "," unless ($fileIndex == $#fileNames); + print OUTPUT "\n"; +} +print OUTPUT "};\n"; +close(OUTPUT); diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..64a535b --- /dev/null +++ b/Makefile @@ -0,0 +1,74 @@ + +# Author: Josh Holtrop +# DornerWorks screensaver + +# set this to compile in "debug" mode +#DEBUG := 1 +# set this to be in "window mode" - i.e. do not hide mouse +# cursor and grab input +#WINDOW_MODE := 1 + +OBJS = dwscr.o wfobj/WFObj.o LoadFile/LoadFile.o ss/ss.a +TARGET = dwscr +export CXXFLAGS := -O2 -Wall +export CPPFLAGS +ifdef DEBUG +CPPFLAGS += -DDEBUG +endif +ifdef WINDOW_MODE +CPPFLAGS += -DWINDOW_MODE +endif +ifdef WIN32 +export CP := copy +export MV := rename +export CC := mingw32-gcc +export CXX := mingw32-g++ +SDL_BASE := C:\apps\SDL-1.2.13 +ODE_BASE := C:\apps\ode-0.9 +export CPPFLAGS += -I$(SDL_BASE)\include -I$(ODE_BASE)\include -D_GNU_SOURCE=1 -Dmain=SDL_main +LDFLAGS += -L$(SDL_BASE)\lib -L$(ODE_BASE)\lib\releasedll -lopengl32 -lglu32 -lmingw32 -mwindows -lSDLmain -lSDL -lode +TARGET := $(TARGET).exe +SSNAME := dwscr.scr +INSTALLDIR := C:\WINDOWS +OBJS += displayinfo-win32.o +else +export CP := cp +export MV := mv +export CC := gcc +export CXX := g++ +export AR := ar +export CPPFLAGS += `sdl-config --cflags` `ode-config --cflags` +LDFLAGS += `sdl-config --libs` `ode-config --libs` -lGL -lGLU +OBJS += displayinfo.o +endif + + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +.PHONY: wfobj/WFObj.o +wfobj/WFObj.o: + $(MAKE) -C wfobj + +.PHONY: LoadFile/LoadFile.o +LoadFile/LoadFile.o: + $(MAKE) -C LoadFile + +.PHONY: ss/ss.a +ss/ss.a: + $(MAKE) -C ss + +ifdef WIN32 +.PHONY: install +install: + $(CP) $(TARGET) $(INSTALLDIR)\$(SSNAME) +endif + +.PHONY: clean +clean: + $(MAKE) -C wfobj clean + $(MAKE) -C LoadFile clean + $(MAKE) -C ss clean + -$(RM) -f *~ *.o $(TARGET) diff --git a/displayinfo-win32.cc b/displayinfo-win32.cc new file mode 100644 index 0000000..5da659b --- /dev/null +++ b/displayinfo-win32.cc @@ -0,0 +1,55 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * This Windows-specific module will get the screen size + * and return the number of monitors present. Right now the + * screensaver will only work properly with two identically + * sized monitors... + */ + +#include +#include +#include "displayinfo.h" + +static int numMonitors = 0; + +void getDisplaySize(int * width, int * height) +{ + std::string nullStr = ""; + int w = 0; + int h = 0; + + /* loop through the display devices to get their dimensions */ + for (int i = 0; i < 10; i++) + { + DISPLAY_DEVICE dev; + dev.cb = sizeof(DISPLAY_DEVICE); + if (!EnumDisplayDevices(NULL, i, &dev, 0)) + break; + /* we only want real display devices to matter */ + if (dev.DeviceID != nullStr) + { + HDC hdc = CreateDC(TEXT("DISPLAY"), dev.DeviceString, NULL, NULL); + if (hdc != NULL) + { + numMonitors++; + w += GetDeviceCaps(hdc, HORZRES); + int thisHeight = GetDeviceCaps(hdc, VERTRES); + if (thisHeight > h) + h = thisHeight; + } + } + } + + /* only update width and height if we have valid data */ + if (w != 0 && h != 0) + { + *width = w; + *height = h; + } +} + +int getNumMonitors() +{ + return numMonitors; +} diff --git a/displayinfo.cc b/displayinfo.cc new file mode 100644 index 0000000..a66df8e --- /dev/null +++ b/displayinfo.cc @@ -0,0 +1,30 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + */ + +#include +#include "displayinfo.h" +#include + +void getDisplaySize(int * width, int * height) +{ + Display * display; + if ((display = XOpenDisplay(NULL)) == NULL) + { + /* fall back on hard-coded defaults */ + *width = 1600; + *height = 1200; + } + else + { + int screen_num = DefaultScreen(display); + *width = DisplayWidth(display, screen_num); + *height = DisplayHeight(display, screen_num); + } +} + +int getNumMonitors() +{ + return 1; +} diff --git a/displayinfo.h b/displayinfo.h new file mode 100644 index 0000000..789ff22 --- /dev/null +++ b/displayinfo.h @@ -0,0 +1,9 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * Common interface for obtaining display information from either + * Windows or linux + */ + +void getDisplaySize(int * width, int * height); +int getNumMonitors(); diff --git a/dwscr.cc b/dwscr.cc new file mode 100644 index 0000000..b4e9f0c --- /dev/null +++ b/dwscr.cc @@ -0,0 +1,97 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * This was a for-fun project I started working on in November '07 + * because I like 3D graphics. I modeled the DornerWorks logo in + * blender, exported it to an Alias WaveFront object and material + * file, wrote an Alias Wavefront object loader, and then wrapped + * some OpenGL and ODE logic around it for various screensaver modes + */ + +#include +#include +#include +#include +#include "displayinfo.h" +#include "ss/SSMain.h" +using namespace std; + +#define PROGNAME "dwscr" +#define DEFAULT_WIDTH 1024 +#define DEFAULT_HEIGHT 768 + +/* main function, called upon program invocation */ +int main(int argc, char * argv[]) +{ + const string startString = "/s"; +#ifdef WIN32 + bool start = false; +#else + bool start = true; +#endif + SDL_Surface * sdlSurface; + int width = DEFAULT_WIDTH; + int height = DEFAULT_HEIGHT; + +#ifdef DEBUG + start = true; +#endif + + /* make the SDL window appear in the top-left corner of the screen */ + putenv("SDL_VIDEO_WINDOW_POS=0,0"); + + /* do not disable power management (this will allow monitors + * to power off at the normally configured timeout) */ + putenv("SDL_VIDEO_ALLOW_SCREENSAVER=1"); + + /* extremely simple command-line argument handling */ + for (int i = 1; i < argc; i++) + { + if (startString == argv[i]) + start = true; + } + + if (!start) + return 0; + + getDisplaySize(&width, &height); + + /* initialize SDL library */ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) + { + cerr << "Error initializing video!" << endl; + return -1; + } + + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + /* Enable multisampling */ + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + + if ((sdlSurface = SDL_SetVideoMode(width, height, 0, + SDL_HWSURFACE +#ifndef WINDOW_MODE + | SDL_NOFRAME +#endif + | SDL_OPENGL)) == NULL) + { + cerr << "Error setting video mode!" << endl; + return -2; + } + + SDL_WM_SetCaption("dwscr", "dwscr"); +#ifndef WINDOW_MODE + SDL_WM_GrabInput(SDL_GRAB_ON); + SDL_ShowCursor(SDL_DISABLE); +#endif + + /* after our window is created and SDL and OpenGL are initialized, + * start the main screensaver functionality */ + SSMain ss(width, height, getNumMonitors(), sdlSurface); + ss.run(); + + SDL_Quit(); + return 0; +} diff --git a/logo/dwlogo.blend b/logo/dwlogo.blend new file mode 100644 index 0000000000000000000000000000000000000000..b01601b23f49a160a8e2a03756652b4fbf0e51e8 GIT binary patch literal 169904 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 R1&wa%>?th_&j0-P{{V-q>s|l= literal 0 HcmV?d00001 diff --git a/logo/dwlogo.mtl b/logo/dwlogo.mtl new file mode 100644 index 0000000..06fff4a --- /dev/null +++ b/logo/dwlogo.mtl @@ -0,0 +1,22 @@ +# Blender3D MTL File: dwlogo.blend +# Material Count: 2 +newmtl Material.002 +Ns 96.078431 +Ka 0.396 0.392 0.078 +Kd 0.396 0.392 0.078 +Ks 0.2 0.2 0.2 +Ni 1.000000 +d 1.000000 +illum 2 + + +newmtl Material.001 +Ns 96.078431 +Ka 0.000000 0.326 0.341 +Kd 0.000000 0.326 0.341 +Ks 0.2 0.2 0.2 +Ni 1.000000 +d 1.000000 +illum 2 + + diff --git a/logo/dwlogo.obj b/logo/dwlogo.obj new file mode 100644 index 0000000..9f0a1bf --- /dev/null +++ b/logo/dwlogo.obj @@ -0,0 +1,645 @@ +# Blender3D v245 OBJ File: dwlogo.blend +# www.blender3d.org +mtllib dwlogo.mtl +v -1.967500 3.292557 0.000000 +v -1.539864 2.771481 0.000000 +v -1.222102 2.176990 0.000000 +v -1.026424 1.531928 0.000000 +v -0.969990 1.037352 0.000000 +v -0.578603 1.035548 0.000000 +v 0.007450 -0.426248 0.000000 +v 1.376684 3.048505 0.000000 +v 2.744326 -0.341223 0.000000 +v 3.879164 2.783177 0.000000 +v 4.794436 2.784227 0.000000 +v 4.794755 3.733678 0.000000 +v -2.503128 3.729895 0.000000 +v -2.503128 3.729895 0.500000 +v 4.794755 3.733678 0.500000 +v 4.794436 2.784227 0.500000 +v 3.879164 2.783177 0.500000 +v 2.744326 -0.341223 0.500000 +v 1.376684 3.048505 0.500000 +v 0.007450 -0.426248 0.500000 +v -0.578603 1.035548 0.500000 +v -0.969990 1.037352 0.500000 +v -1.026424 1.531928 0.500000 +v -1.222102 2.176990 0.500000 +v -1.539864 2.771481 0.500000 +v -1.967500 3.292557 0.500000 +vn 0.000000 0.000000 -1.000000 +vn -0.928184 -0.372121 0.000000 +vn -0.000518 1.000000 0.000000 +vn 1.000000 -0.000336 0.000000 +vn 0.001147 -0.999999 0.000000 +vn 0.939920 -0.341396 0.000000 +vn -0.927364 -0.374160 0.000000 +vn 0.930373 -0.366615 0.000000 +vn -0.004611 -0.999989 0.000000 +vn 0.000000 0.000000 1.000000 +g Cube.005_Cube.006_Material.002 +usemtl Material.002 +s off +f 11//1 13//1 12//1 +f 8//1 1//1 13//1 +f 11//1 8//1 13//1 +f 2//1 1//1 8//1 +f 3//1 2//1 8//1 +f 6//1 3//1 8//1 +f 7//1 6//1 8//1 +f 10//1 9//1 8//1 +f 11//1 10//1 8//1 +f 6//1 4//1 3//1 +f 6//1 5//1 4//1 +f 7//2 20//2 21//2 6//2 +f 13//3 14//3 15//3 12//3 +f 12//4 15//4 16//4 11//4 +f 11//5 16//5 17//5 10//5 +f 10//6 17//6 18//6 9//6 +f 9//7 18//7 19//7 8//7 +f 8//8 19//8 20//8 7//8 +f 6//9 21//9 22//9 5//9 +f 14//10 16//10 15//10 +f 26//10 19//10 14//10 +f 19//10 16//10 14//10 +f 25//10 19//10 26//10 +f 25//10 24//10 19//10 +f 24//10 21//10 19//10 +f 21//10 20//10 19//10 +f 18//10 17//10 19//10 +f 17//10 16//10 19//10 +f 23//10 21//10 24//10 +f 22//10 21//10 23//10 +v 2.768149 -1.634091 0.000000 +v 1.384947 1.793045 0.000000 +v 0.012920 -1.618155 0.000000 +v -0.883670 0.580436 0.000000 +v -0.972935 0.581641 0.000000 +v -1.028585 0.087289 0.000000 +v -1.218865 -0.539980 0.000000 +v -1.527863 -1.118075 0.000000 +v -1.943704 -1.624779 0.000000 +v -2.450409 -2.040621 0.000000 +v -2.782750 -2.236504 0.000000 +v 4.806163 -2.230783 0.000000 +v 4.807395 2.336444 0.000000 +v 4.206083 2.335061 0.000000 +v 4.206083 2.335061 0.500000 +v 4.807395 2.336444 0.500000 +v 4.806163 -2.230783 0.500000 +v -2.782750 -2.236504 0.500000 +v -2.450409 -2.040621 0.500000 +v -1.943704 -1.624779 0.500000 +v -1.527863 -1.118075 0.500000 +v -1.218865 -0.539980 0.500000 +v -1.028585 0.087289 0.500000 +v -0.972935 0.581641 0.500000 +v -0.883670 0.580436 0.500000 +v 0.012920 -1.618155 0.500000 +v 1.384947 1.793045 0.500000 +v 2.768149 -1.634091 0.500000 +vn 0.000754 -1.000000 0.000000 +vn -0.940203 0.340614 0.000000 +vn -0.002300 0.999997 0.000000 +vn 1.000000 -0.000270 0.000000 +vn 0.013502 0.999909 0.000000 +vn 0.925965 0.377610 0.000000 +vn -0.927767 0.373159 0.000000 +vn 0.927320 0.374269 0.000000 +g Cube.004_Cube.005_Material.001 +usemtl Material.001 +s off +f 38//1 40//1 39//1 +f 38//1 27//1 40//1 +f 27//1 29//1 28//1 +f 30//1 32//1 31//1 +f 29//1 32//1 30//1 +f 29//1 33//1 32//1 +f 29//1 34//1 33//1 +f 29//1 35//1 34//1 +f 27//1 35//1 29//1 +f 27//1 36//1 35//1 +f 36//1 27//1 38//1 +f 38//1 37//1 36//1 +f 38//11 43//11 44//11 37//11 +f 27//12 54//12 41//12 40//12 +f 40//13 41//13 42//13 39//13 +f 39//14 42//14 43//14 38//14 +f 31//15 50//15 51//15 30//15 +f 30//16 51//16 52//16 29//16 +f 29//17 52//17 53//17 28//17 +f 54//18 27//18 28//18 53//18 +f 41//10 43//10 42//10 +f 54//10 43//10 41//10 +f 52//10 54//10 53//10 +f 49//10 51//10 50//10 +f 49//10 52//10 51//10 +f 48//10 52//10 49//10 +f 47//10 52//10 48//10 +f 46//10 52//10 47//10 +f 46//10 54//10 52//10 +f 45//10 54//10 46//10 +f 45//10 43//10 54//10 +f 44//10 43//10 45//10 +v -3.414269 0.996186 0.000000 +v -3.360440 0.922096 0.000000 +v -3.332140 0.834999 0.000000 +v -3.332140 0.743420 0.000000 +v -3.360440 0.656322 0.000000 +v -3.414269 0.582233 0.000000 +v -3.488358 0.528404 0.000000 +v -3.575455 0.500104 0.000000 +v -3.667035 0.500104 0.000000 +v -3.754132 0.528404 0.000000 +v -3.828222 0.582233 0.000000 +v -3.882050 0.656322 0.000000 +v -3.910350 0.743420 0.000000 +v -3.910350 0.834999 0.000000 +v -3.882050 0.922097 0.000000 +v -3.828221 0.996186 0.000000 +v -3.754131 1.050015 0.000000 +v -3.667034 1.078314 0.000000 +v -3.575454 1.078314 0.000000 +v -3.488358 1.050014 0.000000 +v -3.488358 1.050014 0.500000 +v -3.575454 1.078314 0.500000 +v -3.667034 1.078314 0.500000 +v -3.754131 1.050015 0.500000 +v -3.828221 0.996186 0.500000 +v -3.882050 0.922097 0.500000 +v -3.910350 0.834999 0.500000 +v -3.910350 0.743420 0.500000 +v -3.882050 0.656322 0.500000 +v -3.828222 0.582233 0.500000 +v -3.754132 0.528404 0.500000 +v -3.667035 0.500104 0.500000 +v -3.575455 0.500104 0.500000 +v -3.488358 0.528404 0.500000 +v -3.414269 0.582233 0.500000 +v -3.360440 0.656322 0.500000 +v -3.332140 0.743420 0.500000 +v -3.332140 0.834999 0.500000 +v -3.360440 0.922096 0.500000 +v -3.414269 0.996186 0.500000 +g Cube.003_Cube.004_Material.002 +usemtl Material.002 +s off +f 73//1 71//1 72//1 +f 74//1 71//1 73//1 +f 74//1 70//1 71//1 +f 55//1 70//1 74//1 +f 55//1 69//1 70//1 +f 69//1 55//1 56//1 +f 56//1 68//1 69//1 +f 57//1 68//1 56//1 +f 57//1 67//1 68//1 +f 58//1 67//1 57//1 +f 58//1 66//1 67//1 +f 59//1 66//1 58//1 +f 59//1 65//1 66//1 +f 60//1 65//1 59//1 +f 60//1 64//1 65//1 +f 61//1 64//1 60//1 +f 61//1 63//1 64//1 +f 62//1 63//1 61//1 +f 78//10 76//10 77//10 +f 78//10 75//10 76//10 +f 79//10 75//10 78//10 +f 79//10 94//10 75//10 +f 80//10 94//10 79//10 +f 80//10 93//10 94//10 +f 81//10 93//10 80//10 +f 81//10 92//10 93//10 +f 82//10 92//10 81//10 +f 82//10 91//10 92//10 +f 83//10 91//10 82//10 +f 83//10 90//10 91//10 +f 84//10 90//10 83//10 +f 84//10 89//10 90//10 +f 85//10 89//10 84//10 +f 85//10 88//10 89//10 +f 86//10 88//10 85//10 +f 86//10 87//10 88//10 +v -2.327900 2.981822 0.000000 +v -1.946631 2.517246 0.000000 +v -1.663324 1.987213 0.000000 +v -1.488863 1.412095 0.000000 +v -1.441604 0.586849 0.000000 +v -1.488863 0.215889 0.000000 +v -1.663323 -0.359229 0.000000 +v -1.946631 -0.889262 0.000000 +v -2.327900 -1.353839 0.000000 +v -2.792477 -1.735108 0.000000 +v -3.322510 -2.018416 0.000000 +v -3.897629 -2.192876 0.000000 +v -4.747342 -2.200104 0.000000 +v -4.747336 3.727444 0.000000 +v -3.550383 3.725241 0.000000 +v -3.322504 3.646397 0.000000 +v -2.792472 3.363088 0.000000 +v -3.110140 1.315794 0.000000 +v -2.962800 1.105371 0.000000 +v -2.943537 1.031374 0.000000 +v -2.924606 0.589536 0.000000 +v -3.027266 0.368530 0.000000 +v -3.208907 0.186889 0.000000 +v -3.441719 0.078327 0.000000 +v -3.697621 0.055938 0.000000 +v -3.945747 0.122423 0.000000 +v -4.156171 0.269763 0.000000 +v -4.303511 0.480187 0.000000 +v -4.369996 0.728313 0.000000 +v -4.347608 0.984215 0.000000 +v -4.239046 1.217027 0.000000 +v -4.057405 1.398669 0.000000 +v -3.824593 1.507231 0.000000 +v -3.568691 1.529619 0.000000 +v -3.320565 1.463135 0.000000 +v -1.442270 1.039348 0.000000 +v -1.442270 1.039348 0.500000 +v -3.320565 1.463135 0.500000 +v -3.568691 1.529619 0.500000 +v -3.824593 1.507231 0.500000 +v -4.057405 1.398669 0.500000 +v -4.239046 1.217027 0.500000 +v -4.347608 0.984215 0.500000 +v -4.369996 0.728313 0.500000 +v -4.303511 0.480187 0.500000 +v -4.156171 0.269763 0.500000 +v -3.945747 0.122423 0.500000 +v -3.697621 0.055938 0.500000 +v -3.441719 0.078327 0.500000 +v -3.208907 0.186889 0.500000 +v -3.027266 0.368530 0.500000 +v -2.924606 0.589536 0.500000 +v -2.943537 1.031374 0.500000 +v -2.962800 1.105371 0.500000 +v -3.110140 1.315794 0.500000 +v -2.792472 3.363088 0.500000 +v -3.322504 3.646397 0.500000 +v -3.550383 3.725241 0.500000 +v -4.747336 3.727444 0.500000 +v -4.747342 -2.200104 0.500000 +v -3.897629 -2.192876 0.500000 +v -3.322510 -2.018416 0.500000 +v -2.792477 -1.735108 0.500000 +v -2.327900 -1.353839 0.500000 +v -1.946631 -0.889262 0.500000 +v -1.663323 -0.359229 0.500000 +v -1.488863 0.215889 0.500000 +v -1.441604 0.586849 0.500000 +v -1.488863 1.412095 0.500000 +v -1.663324 1.987213 0.500000 +v -1.946631 2.517246 0.500000 +v -2.327900 2.981822 0.500000 +vn 0.001812 0.999998 0.000000 +vn 0.005311 -0.999986 0.000000 +vn -1.000000 0.000001 0.000000 +vn 0.001840 0.999998 0.000000 +vn 0.008506 -0.999964 0.000000 +g Cylinder.001_Cylinder.001_Material.001 +usemtl Material.001 +s off +f 124//1 107//1 108//1 +f 125//1 124//1 108//1 +f 126//1 125//1 108//1 +f 109//1 126//1 108//1 +f 110//1 126//1 109//1 +f 111//1 126//1 110//1 +f 95//1 126//1 111//1 +f 126//1 95//1 96//1 +f 127//1 126//1 96//1 +f 128//1 127//1 96//1 +f 97//1 128//1 96//1 +f 98//1 128//1 97//1 +f 98//1 129//1 128//1 +f 98//1 112//1 129//1 +f 130//1 112//1 98//1 +f 130//1 113//1 112//1 +f 130//1 114//1 113//1 +f 123//1 107//1 124//1 +f 122//1 107//1 123//1 +f 99//1 116//1 115//1 +f 100//1 116//1 99//1 +f 121//1 107//1 122//1 +f 100//1 117//1 116//1 +f 120//1 107//1 121//1 +f 101//1 117//1 100//1 +f 101//1 118//1 117//1 +f 119//1 107//1 120//1 +f 101//1 119//1 118//1 +f 101//1 107//1 119//1 +f 102//1 107//1 101//1 +f 103//1 107//1 102//1 +f 104//1 107//1 103//1 +f 105//1 107//1 104//1 +f 106//1 107//1 105//1 +f 115//19 146//19 162//19 99//19 +f 130//20 131//20 147//20 114//20 +f 107//21 154//21 153//21 108//21 +f 108//22 153//22 152//22 109//22 +f 106//23 155//23 154//23 107//23 +f 154//10 137//10 153//10 +f 137//10 136//10 153//10 +f 136//10 135//10 153//10 +f 135//10 152//10 153//10 +f 135//10 151//10 152//10 +f 135//10 150//10 151//10 +f 135//10 166//10 150//10 +f 135//10 165//10 166//10 +f 135//10 134//10 165//10 +f 134//10 133//10 165//10 +f 133//10 164//10 165//10 +f 133//10 163//10 164//10 +f 132//10 163//10 133//10 +f 149//10 163//10 132//10 +f 149//10 131//10 163//10 +f 148//10 131//10 149//10 +f 147//10 131//10 148//10 +f 154//10 138//10 137//10 +f 154//10 139//10 138//10 +f 145//10 162//10 146//10 +f 145//10 161//10 162//10 +f 154//10 140//10 139//10 +f 144//10 161//10 145//10 +f 154//10 141//10 140//10 +f 144//10 160//10 161//10 +f 143//10 160//10 144//10 +f 154//10 142//10 141//10 +f 142//10 160//10 143//10 +f 154//10 160//10 142//10 +f 154//10 159//10 160//10 +f 154//10 158//10 159//10 +f 154//10 157//10 158//10 +f 154//10 156//10 157//10 +f 154//10 155//10 156//10 +v -1.967500 3.292557 0.000000 +v -1.539864 2.771481 0.000000 +v -1.222102 2.176990 0.000000 +v -1.026424 1.531928 0.000000 +v -0.969990 1.037352 0.000000 +v -2.503128 3.729895 0.000000 +v -2.503128 3.729895 0.500000 +v -0.969990 1.037352 0.500000 +v -1.026424 1.531928 0.500000 +v -1.222102 2.176990 0.500000 +v -1.539864 2.771481 0.500000 +v -1.967500 3.292557 0.500000 +vn -0.706198 -0.707968 0.000000 +vn -0.632435 -0.774590 0.000000 +vn -0.993530 -0.113346 0.000000 +vn -0.979247 -0.202643 0.000000 +vn -0.923856 -0.382672 0.000000 +vn -0.831446 -0.555559 0.000000 +g Cube.002_Cube.003_Material.002 +usemtl Material.002 +s 1 +f 167//24 178//24 173//25 172//25 +f 171//26 174//26 175//27 170//27 +f 170//27 175//27 176//28 169//28 +f 169//28 176//28 177//29 168//29 +f 178//24 167//24 168//29 177//29 +v -3.414269 0.996186 0.000000 +v -3.360440 0.922096 0.000000 +v -3.332140 0.834999 0.000000 +v -3.332140 0.743420 0.000000 +v -3.360440 0.656322 0.000000 +v -3.414269 0.582233 0.000000 +v -3.488358 0.528404 0.000000 +v -3.575455 0.500104 0.000000 +v -3.667035 0.500104 0.000000 +v -3.754132 0.528404 0.000000 +v -3.828222 0.582233 0.000000 +v -3.882050 0.656322 0.000000 +v -3.910350 0.743420 0.000000 +v -3.910350 0.834999 0.000000 +v -3.882050 0.922097 0.000000 +v -3.828221 0.996186 0.000000 +v -3.754131 1.050015 0.000000 +v -3.667034 1.078314 0.000000 +v -3.575454 1.078314 0.000000 +v -3.488358 1.050014 0.000000 +v -3.488358 1.050014 0.500000 +v -3.575454 1.078314 0.500000 +v -3.667034 1.078314 0.500000 +v -3.754131 1.050015 0.500000 +v -3.828221 0.996186 0.500000 +v -3.882050 0.922097 0.500000 +v -3.910350 0.834999 0.500000 +v -3.910350 0.743420 0.500000 +v -3.882050 0.656322 0.500000 +v -3.828222 0.582233 0.500000 +v -3.754132 0.528404 0.500000 +v -3.667035 0.500104 0.500000 +v -3.575455 0.500104 0.500000 +v -3.488358 0.528404 0.500000 +v -3.414269 0.582233 0.500000 +v -3.360440 0.656322 0.500000 +v -3.332140 0.743420 0.500000 +v -3.332140 0.834999 0.500000 +v -3.360440 0.922096 0.500000 +v -3.414269 0.996186 0.500000 +vn 0.707083 0.707083 0.000000 +vn 0.453963 0.890988 0.000000 +vn 0.156407 0.987671 0.000000 +vn -0.156407 0.987671 0.000000 +vn -0.453963 0.890988 0.000000 +vn -0.707083 0.707083 0.000000 +vn -0.890988 0.453993 0.000000 +vn -0.987671 0.156407 0.000000 +vn -0.987671 -0.156407 0.000000 +vn -0.890988 -0.453963 0.000000 +vn -0.707083 -0.707083 0.000000 +vn -0.453963 -0.890988 0.000000 +vn -0.156407 -0.987671 0.000000 +vn 0.156407 -0.987671 0.000000 +vn 0.453963 -0.890988 0.000000 +vn 0.707083 -0.707083 0.000000 +vn 0.890988 -0.453963 0.000000 +vn 0.987671 -0.156407 0.000000 +vn 0.987671 0.156407 0.000000 +vn 0.890988 0.453963 0.000000 +g Cube.001_Cube.002_Material.002 +usemtl Material.002 +s 1 +f 218//30 179//30 198//31 199//31 +f 197//32 200//32 199//31 198//31 +f 196//33 201//33 200//32 197//32 +f 195//34 202//34 201//33 196//33 +f 194//35 203//35 202//34 195//34 +f 193//36 204//36 203//35 194//35 +f 192//37 205//37 204//36 193//36 +f 191//38 206//38 205//37 192//37 +f 190//39 207//39 206//38 191//38 +f 189//40 208//40 207//39 190//39 +f 188//41 209//41 208//40 189//40 +f 187//42 210//42 209//41 188//41 +f 186//43 211//43 210//42 187//42 +f 185//44 212//44 211//43 186//43 +f 184//45 213//45 212//44 185//44 +f 183//46 214//46 213//45 184//45 +f 182//47 215//47 214//46 183//46 +f 181//48 216//48 215//47 182//47 +f 180//49 217//49 216//48 181//48 +f 179//30 218//30 217//49 180//49 +v -0.972935 0.581641 0.000000 +v -1.028585 0.087289 0.000000 +v -1.218865 -0.539980 0.000000 +v -1.527863 -1.118075 0.000000 +v -1.943704 -1.624779 0.000000 +v -2.450409 -2.040621 0.000000 +v -2.782750 -2.236504 0.000000 +v -2.782750 -2.236504 0.500000 +v -2.450409 -2.040621 0.500000 +v -1.943704 -1.624779 0.500000 +v -1.527863 -1.118075 0.500000 +v -1.218865 -0.539980 0.500000 +v -1.028585 0.087289 0.500000 +v -0.972935 0.581641 0.500000 +vn -0.507767 0.861476 0.000000 +vn -0.572771 0.819697 0.000000 +vn -0.831446 0.555559 0.000000 +vn -0.923856 0.382672 0.000000 +vn -0.979400 0.201910 0.000000 +vn -0.993713 0.111850 0.000000 +g Cube_Cube.001_Material.001 +usemtl Material.001 +s 1 +f 225//50 226//50 227//51 224//51 +f 224//51 227//51 228//35 223//35 +f 223//35 228//35 229//52 222//52 +f 222//52 229//52 230//53 221//53 +f 221//53 230//53 231//54 220//54 +f 232//55 219//55 220//54 231//54 +v -2.327900 2.981822 0.000000 +v -1.946631 2.517246 0.000000 +v -1.663324 1.987213 0.000000 +v -1.488863 1.412095 0.000000 +v -1.441604 0.586849 0.000000 +v -1.488863 0.215889 0.000000 +v -1.663323 -0.359229 0.000000 +v -1.946631 -0.889262 0.000000 +v -2.327900 -1.353839 0.000000 +v -2.792477 -1.735108 0.000000 +v -3.322510 -2.018416 0.000000 +v -3.897629 -2.192876 0.000000 +v -3.550383 3.725241 0.000000 +v -3.322504 3.646397 0.000000 +v -2.792472 3.363088 0.000000 +v -3.110140 1.315794 0.000000 +v -2.962800 1.105371 0.000000 +v -2.943537 1.031374 0.000000 +v -2.924606 0.589536 0.000000 +v -3.027266 0.368530 0.000000 +v -3.208907 0.186889 0.000000 +v -3.441719 0.078327 0.000000 +v -3.697621 0.055938 0.000000 +v -3.945747 0.122423 0.000000 +v -4.156171 0.269763 0.000000 +v -4.303511 0.480187 0.000000 +v -4.369996 0.728313 0.000000 +v -4.347608 0.984215 0.000000 +v -4.239046 1.217027 0.000000 +v -4.057405 1.398669 0.000000 +v -3.824593 1.507231 0.000000 +v -3.568691 1.529619 0.000000 +v -3.320565 1.463135 0.000000 +v -1.442270 1.039348 0.000000 +v -1.442270 1.039348 0.500000 +v -3.320565 1.463135 0.500000 +v -3.568691 1.529619 0.500000 +v -3.824593 1.507231 0.500000 +v -4.057405 1.398669 0.500000 +v -4.239046 1.217027 0.500000 +v -4.347608 0.984215 0.500000 +v -4.369996 0.728313 0.500000 +v -4.303511 0.480187 0.500000 +v -4.156171 0.269763 0.500000 +v -3.945747 0.122423 0.500000 +v -3.697621 0.055938 0.500000 +v -3.441719 0.078327 0.500000 +v -3.208907 0.186889 0.500000 +v -3.027266 0.368530 0.500000 +v -2.924606 0.589536 0.500000 +v -2.943537 1.031374 0.500000 +v -2.962800 1.105371 0.500000 +v -3.110140 1.315794 0.500000 +v -2.792472 3.363088 0.500000 +v -3.322504 3.646397 0.500000 +v -3.550383 3.725241 0.500000 +v -3.897629 -2.192876 0.500000 +v -3.322510 -2.018416 0.500000 +v -2.792477 -1.735108 0.500000 +v -2.327900 -1.353839 0.500000 +v -1.946631 -0.889262 0.500000 +v -1.663323 -0.359229 0.500000 +v -1.488863 0.215889 0.500000 +v -1.441604 0.586849 0.500000 +v -1.488863 1.412095 0.500000 +v -1.663324 1.987213 0.500000 +v -1.946631 2.517246 0.500000 +v -2.327900 2.981822 0.500000 +vn 0.978118 0.207892 0.000000 +vn 0.992248 0.124027 0.000000 +vn -0.422590 -0.906308 0.000000 +vn -0.087130 -0.996185 0.000000 +vn 0.258797 -0.965911 0.000000 +vn 0.573565 -0.819147 0.000000 +vn 0.819147 -0.573565 0.000000 +vn 0.965911 -0.258797 0.000000 +vn 0.996185 0.087130 0.000000 +vn 0.906278 0.422590 0.000000 +vn 0.422590 0.906278 0.000000 +vn 0.087130 0.996185 0.000000 +vn -0.258797 0.965911 0.000000 +vn -0.573565 0.819147 0.000000 +vn -0.819575 0.572954 0.000000 +vn -0.906919 0.421277 0.000000 +vn -0.967742 -0.251930 0.000000 +vn -0.907804 -0.419385 0.000000 +vn 0.555559 0.831446 0.000000 +vn 0.400403 0.916318 0.000000 +vn 0.326945 0.945006 0.000000 +vn 0.382672 -0.923856 0.000000 +vn 0.290262 -0.956938 0.000000 +vn 0.555559 -0.831446 0.000000 +vn 0.831446 -0.555559 0.000000 +vn 0.923856 -0.382672 0.000000 +vn 0.977874 -0.209052 0.000000 +vn 0.991974 -0.126347 0.000000 +vn 0.923856 0.382672 0.000000 +vn 0.831446 0.555559 0.000000 +g Cylinder_Cylinder_Material.001 +usemtl Material.001 +s 1 +f 236//56 297//56 267//57 266//57 +f 248//40 285//40 268//58 265//58 +f 265//58 268//58 269//59 264//59 +f 264//59 269//59 270//60 263//60 +f 263//60 270//60 271//61 262//61 +f 262//61 271//61 272//62 261//62 +f 261//62 272//62 273//63 260//63 +f 260//63 273//63 274//64 259//64 +f 259//64 274//64 275//65 258//65 +f 258//65 275//65 276//30 257//30 +f 257//30 276//30 277//66 256//66 +f 256//66 277//66 278//67 255//67 +f 255//67 278//67 279//68 254//68 +f 254//68 279//68 280//69 253//69 +f 253//69 280//69 281//70 252//70 +f 252//70 281//70 282//71 251//71 +f 250//72 283//72 284//73 249//73 +f 249//73 284//73 285//40 248//40 +f 300//30 233//30 247//74 286//74 +f 246//75 287//75 286//74 247//74 +f 245//76 288//76 287//75 246//75 +f 243//77 290//77 289//78 244//78 +f 242//79 291//79 290//77 243//77 +f 241//45 292//45 291//79 242//79 +f 240//80 293//80 292//45 241//45 +f 239//81 294//81 293//80 240//80 +f 238//82 295//82 294//81 239//81 +f 237//83 296//83 295//82 238//82 +f 235//84 298//84 297//56 236//56 +f 234//85 299//85 298//84 235//84 +f 233//30 300//30 299//85 234//85 diff --git a/make-win32.bat b/make-win32.bat new file mode 100755 index 0000000..2229c2c --- /dev/null +++ b/make-win32.bat @@ -0,0 +1 @@ +mingw32-make WIN32=1 %* diff --git a/ss/LogoBox.cpp b/ss/LogoBox.cpp new file mode 100644 index 0000000..05d9671 --- /dev/null +++ b/ss/LogoBox.cpp @@ -0,0 +1,112 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * This module can be used to create "LogoBox" objects + * which consist of a 3D DW logo and possibly have a + * mass and boundary associated with them if physics + * is being used + */ + +#include +#include +#include "../wfobj/WFObj.hh" +#include "LogoBox.h" +#include "../LoadFile/LoadFile.h" + +#include +using namespace std; + +static GLuint _logoList = 0; +static float _logoAABB[6]; +static GLuint _drawList; + +/* construct a LogoBox object + * The first time the constructor is called it loads the + * Alias Wavefront object model. Subsequent calls will + * reuse the same module. If physics are being used + * (world and space are not NULL), then the constructor + * initializes an ODE body and geometry for each + * constructed logo. + */ +LogoBox::LogoBox(dWorldID world, dSpaceID space) +{ + if (_logoList == 0) + { + WFObj obj; + if (obj.load("dwlogo.obj", NULL, &LoadFile)) + { + _logoList = obj.render(); + const float * aabb = obj.getAABB(); + memcpy(_logoAABB, aabb, sizeof(_logoAABB)); + + float c_x = (_logoAABB[0] + _logoAABB[3]) / 2.0f; + float c_y = (_logoAABB[1] + _logoAABB[4]) / 2.0f; + float c_z = (_logoAABB[2] + _logoAABB[5]) / 2.0f; + _drawList = glGenLists(1); + glNewList(_drawList, GL_COMPILE); + glPushMatrix(); + glTranslatef(-c_x, -c_y, -c_z); + glCallList(_logoList); + glPopMatrix(); + glEndList(); + } + } + + m_body = 0; + m_geom = 0; + + if (world != 0) + { + dMass mass; + + m_body = dBodyCreate(world); + dMassSetBox(&mass, 1.0, getWidth(), getDepth(), getHeight()); + dBodySetMass(m_body, &mass); + + if (space != 0) + { + m_geom = dCreateBox(space, getWidth(), getDepth(), getHeight()); + dGeomSetBody(m_geom, m_body); + } + } +} + +/* Clean up ODE resources */ +LogoBox::~LogoBox() +{ + if (m_geom) + { + dGeomDestroy(m_geom); + } + if (m_body) + { + dBodyDestroy(m_body); + } +} + +void LogoBox::draw() +{ + glCallList(_drawList); +} + +/* return a pointer to the axis-aligned bounding box for a DW logo */ +const float * const LogoBox::getAABB() const +{ + return _logoAABB; +} + +float LogoBox::getWidth() const +{ + return _logoAABB[3] - _logoAABB[0]; +} + +float LogoBox::getHeight() const +{ + return _logoAABB[5] - _logoAABB[2]; +} + +float LogoBox::getDepth() const +{ + return _logoAABB[4] - _logoAABB[1]; +} + diff --git a/ss/LogoBox.h b/ss/LogoBox.h new file mode 100644 index 0000000..07bc93f --- /dev/null +++ b/ss/LogoBox.h @@ -0,0 +1,31 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * The LogoBox class + */ + +#ifndef LOGOBOX_H +#define LOGOBOX_H + +#include +#include + +class LogoBox +{ +public: + LogoBox(dWorldID world = 0, dSpaceID space = 0); + ~LogoBox(); + void draw(); + const float * const getAABB() const; + float getWidth() const; + float getHeight() const; + float getDepth() const; + dBodyID getBody() { return m_body; } + dGeomID getGeom() { return m_geom; } + +protected: + dBodyID m_body; + dGeomID m_geom; +}; + +#endif diff --git a/ss/Makefile b/ss/Makefile new file mode 100644 index 0000000..156aa1b --- /dev/null +++ b/ss/Makefile @@ -0,0 +1,15 @@ + +OBJS := SSMain.o +OBJS += SSMode.o +OBJS += LogoBox.o +OBJS += PlainSpin.o +OBJS += TumblingLogos.o +OBJS += Towers.o +AROBJS := $(foreach o,$(OBJS),ss.a($(o))) + +all: ss.a + +ss.a: $(AROBJS) + +clean: + -$(RM) -f *~ *.o *.a diff --git a/ss/PlainSpin.cc b/ss/PlainSpin.cc new file mode 100644 index 0000000..759f91d --- /dev/null +++ b/ss/PlainSpin.cc @@ -0,0 +1,46 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * This module implements a very simple screensaver mode. + * It just creates a DW logo and then continuously spins around it. + */ + +#include +#include +#include "PlainSpin.h" + +#include +using namespace std; + +/* construct screensaver mode and do some basic OpenGL setup */ +PlainSpin::PlainSpin(SSMain * _SSMain) : SSMode(_SSMain) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60.0, + (double)m_SSMain->getWidth() + / (double)m_SSMain->getHeight() + / (double)m_SSMain->getNumMonitors(), + 0.01, + 1000.01); + glMatrixMode(GL_MODELVIEW); +} + +/* called every time this screensaver mode should redraw the screen */ +void PlainSpin::update() +{ + SSMode::update(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + int numMonitors = m_SSMain->getNumMonitors(); + int width = m_SSMain->getWidth(); + int height = m_SSMain->getHeight(); + for (int i = 0; i < numMonitors; i++) + { + glViewport(i*width/numMonitors, 0, width/numMonitors, height); + glLoadIdentity(); + gluLookAt(0, 0, 12, 0, 0, 0, 0, 1, 0); + glRotatef(m_elapsed/33.0f, 0, 1, 0); + m_logoBox.draw(); + } + SDL_GL_SwapBuffers(); +} diff --git a/ss/PlainSpin.h b/ss/PlainSpin.h new file mode 100644 index 0000000..07527bd --- /dev/null +++ b/ss/PlainSpin.h @@ -0,0 +1,23 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * A simple spinning logo screensaver mode object + */ + +#ifndef PLAINSPIN_H +#define PLAINSPIN_H + +#include "SSMain.h" +#include "SSMode.h" +#include "LogoBox.h" + +class PlainSpin : public SSMode +{ +public: + PlainSpin(SSMain * _SSMain); + void update(); +protected: + LogoBox m_logoBox; +}; + +#endif diff --git a/ss/SSMain.cc b/ss/SSMain.cc new file mode 100644 index 0000000..bfd4d37 --- /dev/null +++ b/ss/SSMain.cc @@ -0,0 +1,151 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * This module implements the main functionality of the DW screensaver. + * It is responsible for handling input and timer events, as well + * as instantiating and rotating through the various screensaver modes + * that have been implemented. + */ + +#include /* srand() */ +#include /* time() */ +#include +#include +#include +#include "SSMain.h" +#include "PlainSpin.h" +#include "TumblingLogos.h" +#include "Towers.h" + +/* switch to the next screensaver mode after this many milliseconds */ +#define SSMODE_TIMEOUT_MSEC (1000*45) + +static SDL_Event event; + +/* create the main screensaver object */ +SSMain::SSMain(int width, int height, int numMonitors, SDL_Surface * sdlSurface) + : m_width (width), m_height (height), + m_numMonitors (numMonitors), m_sdlSurface (sdlSurface), + m_mode(NULL), m_drawing(false) +{ + /* set up some common OpenGL defaults */ + glShadeModel(GL_SMOOTH); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_CULL_FACE); + GLfloat ambient[] = {0.0f, 0.0f, 0.0f, 1.0f}; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); + + /* setup redraw SDL event structure */ + event.type = SDL_USEREVENT; + event.user.code = 0; + + srand(time(NULL)); +} + +/* called by SDL when the update timer expires */ +Uint32 SSMain::updateCallback(Uint32 interval, void * param) +{ + SSMain * ssmain = (SSMain *) param; + return ssmain->updateCallback(interval); +} + +/* member update function to be called by our registered + * SDL callback non-member function */ +Uint32 SSMain::updateCallback(Uint32 interval) +{ + if (!m_drawing) + { + SDL_PushEvent(&event); + } + return interval; +} + +/* called to begin the screensaver, + * exits when the screensaver should terminate */ +void SSMain::run() +{ + Uint32 elapsed_msec; + Uint32 lastChanged_msec = 0; + SDL_Event event; + + /* initially set the screensaver mode */ + m_mode = startMode(); + + /* register a screen redrawing SDL event */ + SDL_AddTimer(12, &updateCallback, this); + + /* draw the screen */ + update(); + + /* main event loop */ + while (SDL_WaitEvent(&event)) + { + elapsed_msec = SDL_GetTicks(); /* get the time the event occurred at */ + if (elapsed_msec - lastChanged_msec > SSMODE_TIMEOUT_MSEC) + { + delete m_mode; + m_mode = startMode(); + lastChanged_msec = elapsed_msec; + } + + switch (event.type) + { +#ifndef WINDOW_MODE + case SDL_KEYDOWN: /* terminate screensaver upon */ +#endif + case SDL_QUIT: /* key press or quit event */ + goto RET; + case SDL_MOUSEBUTTONDOWN: + if (event.button.button == 1) + { + delete m_mode; + m_mode = startMode(); + lastChanged_msec = elapsed_msec; + } + break; + case SDL_USEREVENT: + if (event.user.code == 0) + { + update(); + } + break; + } + } +RET: + ; +} + +/* return a new screensaver mode object to start displaying */ +SSMode * SSMain::startMode() +{ + static int mode = 0; + const int numModes = 3; + + mode++; + if (mode >= numModes) + mode = 0; + + switch (mode) + { + case 0: + return new PlainSpin(this); + case 1: + return new Towers(this); + default: + return new TumblingLogos(this); + } +} + +/* call the update function to redraw the screen from the + * currently active screensaver mode. + * Uses a pseudo-semaphore to block new redraw requests + * from coming in while current ones are being serviced. + */ +void SSMain::update() +{ + m_drawing = true; + m_mode->update(); + m_drawing = false; +} diff --git a/ss/SSMain.h b/ss/SSMain.h new file mode 100644 index 0000000..6bff636 --- /dev/null +++ b/ss/SSMain.h @@ -0,0 +1,41 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * The main screensaver module + */ + +#ifndef SSMAIN_H +#define SSMAIN_H + +#include +#include + +class SSMode; + +#include "SSMode.h" + +class SSMain +{ +public: + SSMain(int width, int height, int numMonitors, SDL_Surface * sdlSurface); + void run(); + int getWidth() { return m_width; } + int getHeight() { return m_height; } + int getNumMonitors() { return m_numMonitors; } + SDL_Surface * getSurface() { return m_sdlSurface; } + Uint32 updateCallback(Uint32 interval); + +protected: + static Uint32 updateCallback(Uint32 interval, void * param); + void update(); + SSMode * startMode(); + + int m_width; + int m_height; + int m_numMonitors; + SDL_Surface * m_sdlSurface; + SSMode * m_mode; + bool m_drawing; +}; + +#endif diff --git a/ss/SSMode.cc b/ss/SSMode.cc new file mode 100644 index 0000000..8f85ada --- /dev/null +++ b/ss/SSMode.cc @@ -0,0 +1,51 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * SSMode is base class that provides common functionality for + * screensaver modes. It should be extended by a screensaver + * mode implementing class. + */ + +#include "SSMode.h" + +SSMode::SSMode(SSMain * _SSMain) + : m_SSMain(_SSMain) +{ + m_startTick = SDL_GetTicks(); +} + +SSMode::~SSMode() +{ +} + +/* update the number of elapsed milliseconds */ +void SSMode::update() +{ + Uint32 ticks = SDL_GetTicks(); + m_elapsed = ticks - m_startTick; +} + +/* push an OpenGL matrix onto the matrix stack for a given + * ODE body position and rotation */ +void SSMode::pushTransform(const float pos[3], const float R[12]) +{ + GLfloat matrix[16]; + matrix[0] = R[0]; + matrix[1] = R[4]; + matrix[2] = R[8]; + matrix[3] = 0; + matrix[4] = R[1]; + matrix[5] = R[5]; + matrix[6] = R[9]; + matrix[7] = 0; + matrix[8] = R[2]; + matrix[9] = R[6]; + matrix[10] = R[10]; + matrix[11] = 0; + matrix[12] = pos[0]; + matrix[13] = pos[1]; + matrix[14] = pos[2]; + matrix[15] = 1; + glPushMatrix(); + glMultMatrixf(matrix); +} diff --git a/ss/SSMode.h b/ss/SSMode.h new file mode 100644 index 0000000..c64b48f --- /dev/null +++ b/ss/SSMode.h @@ -0,0 +1,31 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * A base class for deriving concrete screensaver mode implementations + */ + +#ifndef SSMODE_H +#define SSMODE_H + +#include +#include + +class SSMain; + +#include "SSMain.h" + +class SSMode +{ +public: + SSMode(SSMain * _SSMain); + virtual ~SSMode(); + virtual void update(); + void pushTransform(const float pos[3], const float R[12]); + +protected: + SSMain * m_SSMain; + Uint32 m_startTick; + Uint32 m_elapsed; +}; + +#endif diff --git a/ss/Towers.cc b/ss/Towers.cc new file mode 100644 index 0000000..892f53b --- /dev/null +++ b/ss/Towers.cc @@ -0,0 +1,302 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * A slightly more interesting screensaver mode involving + * towers of DW logos with randomly disappearing ones + */ + +#include /* fmax(), sin() */ +#include /* rand() */ +#include +#include +#include "Towers.h" +#include + +#include +using namespace std; + +#define WORLD_STEP 0.001 +/* time at beginning of mode to not remove any logos (msec) */ +#define WAIT_TIME 6000 +/* amount of time to wait before removing another logo (msec) */ +#define REMOVE_TIME 3000 +#define GROUND_ALPHA 0.5 + +Towers::Towers(SSMain * _SSMain) : SSMode(_SSMain) +{ + /* set up our specific openGL settings */ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60.0, + (double)m_SSMain->getWidth() + / (double)m_SSMain->getHeight() + / (double)m_SSMain->getNumMonitors(), + 0.01, + 1000.01); + glMatrixMode(GL_MODELVIEW); + + /* create the ground texture */ + float tex[] = {1.0, 0.0, 0.0, GROUND_ALPHA, + 1.0, 1.0, 1.0, GROUND_ALPHA, + 1.0, 1.0, 1.0, GROUND_ALPHA, + 1.0, 0.0, 0.0, GROUND_ALPHA}; + glGenTextures(1, &m_groundTexture); + glBindTexture(GL_TEXTURE_2D, m_groundTexture); + glTexImage2D(GL_TEXTURE_2D, 0, 4, 2, 2, 0, GL_RGBA, GL_FLOAT, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* create the ODE world */ + createWorld(); + + m_lastElapsed = 0; + m_logoToRemove = -1; + m_logosList = glGenLists(1); +} + +Towers::~Towers() +{ + int sz = m_logos.size(); + for (int i = 0; i < sz; i++) + { + delete m_logos[i]; + } + dJointGroupDestroy(m_contactJointGroup); + dWorldDestroy(m_world); + dSpaceDestroy(m_space); + glDeleteTextures(1, &m_groundTexture); + glDeleteLists(m_logosList, 1); +} + +void Towers::createWorld() +{ + /* set up the ODE world and collision space */ + m_world = dWorldCreate(); +// dWorldSetCFM(m_world, 1e-5); + dWorldSetGravity(m_world, 0, -9.81, 0); + dWorldSetERP(m_world, 0.8); + m_space = dHashSpaceCreate(0); +// dVector3 center = {0, 10, 0}; +// dVector3 extents = {100, 40, 100}; +// m_space = dQuadTreeSpaceCreate(0, center, extents, 5); + m_contactJointGroup = dJointGroupCreate(0); + + double lHeight = 0.0; + const double towerBaseOffset = 0.0; + const int towerHeight = 6; + const double towerSpacing = 10.0; + const double logoTierSpacing = 3.75; + bool turn = false; + for (double xoffset = -towerSpacing; + xoffset < towerSpacing*1.1; + xoffset += towerSpacing*2.0) + { + for (double zoffset = -towerSpacing; + zoffset < towerSpacing*1.1; + zoffset += towerSpacing*2.0) + { + double towerOffset = 0.0; + for (int i = 0; + i < towerHeight; + i++, towerOffset += lHeight, turn = !turn) + { + double localXOffset = 0.0; + double localZOffset = 0.0; + if (turn) + localXOffset = -logoTierSpacing; + else + localZOffset = -logoTierSpacing; + for (int i = 0; i < 2; i++) + { + LogoBox * lb = new LogoBox(m_world, m_space); + if (lHeight == 0.0) + lHeight = lb->getDepth(); + dMatrix3 rotation; + dRFromEulerAngles(rotation, + 0, + turn ? M_PI_2 : 0, + 0); + dBodySetRotation(lb->getBody(), rotation); + dBodySetPosition(lb->getBody(), + xoffset + localXOffset, + towerBaseOffset + towerOffset + lb->getDepth()/2.0, + zoffset + localZOffset); + m_logos.push_back(lb); + + if (turn) + localXOffset += logoTierSpacing * 2; + else + localZOffset += logoTierSpacing * 2; + } + } + turn = !turn; + } + } + + /* create ground plane */ + dCreatePlane(m_space, 0, 1, 0, 0); +} + +void Towers::update() +{ + SSMode::update(); + + if (m_lastElapsed == 0) + m_lastElapsed = m_elapsed; + + if (m_logoToRemove == -1 && m_elapsed > WAIT_TIME) + { + if (m_logos.size() > 0) + { + m_lastRemoveTime = m_elapsed; + m_logoToRemove = rand() % m_logos.size(); + } + } + else if (m_elapsed - m_lastRemoveTime > REMOVE_TIME) + { + if (m_logoToRemove >= 0) + { + std::vector::iterator it = m_logos.begin(); + for (int i = 0; i < m_logoToRemove; i++) + it++; + delete *it; + m_logos.erase(it); + m_logoToRemove = (m_logos.size() > 0) ? rand() % m_logos.size() : -1; + m_lastRemoveTime = m_elapsed; + } + } + + Uint32 delta = m_elapsed - m_lastElapsed; + for (unsigned int i = 0; i < delta / 2; i++) + worldStep(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + int numMonitors = m_SSMain->getNumMonitors(); + int width = m_SSMain->getWidth(); + int height = m_SSMain->getHeight(); + bool drewLogos = false; + for (int i = 0; i < numMonitors; i++) + { + glViewport(i*width/numMonitors, 0, width/numMonitors, height); + glLoadIdentity(); + gluLookAt(0, 30, 55, /* eye position */ + 0, 12, 0, /* focal point */ + 0, 1, 0); /* up vector */ + glRotatef(m_elapsed/60.0f, 0, 1, 0); /* rotate view over time */ + + /* draw logo reflection */ + glPushAttrib(GL_POLYGON_BIT); + glFrontFace(GL_CW); /* polygon vertex order reversed when + reflecting across a plane */ + glPushMatrix(); + glScalef(1.0, -1.0, 1.0); + if (drewLogos) + glCallList(m_logosList); + else + { + glNewList(m_logosList, GL_COMPILE_AND_EXECUTE); + drawLogos(); + glEndList(); + drewLogos = true; + } + glPopMatrix(); + glPopAttrib(); + + drawGround(); + + /* draw logos normally */ +// drawLogos(); + glCallList(m_logosList); + } + SDL_GL_SwapBuffers(); + + m_lastElapsed = m_elapsed; +} + +void Towers::drawLogos() +{ + /* draw the logos */ + int sz = m_logos.size(); + for (int i = 0; i < sz; i++) + { + dBodyID body = m_logos[i]->getBody(); + pushTransform(dBodyGetPosition(body), + dBodyGetRotation(body)); + if (i == m_logoToRemove) + { + float amt = fabsf(sin(1.9*M_PI* + (m_elapsed-m_lastRemoveTime)/REMOVE_TIME)); + GLfloat emission[] = {0.0, 0.0, amt, 1.0}; + glPushAttrib(GL_LIGHTING_BIT); + glMaterialfv(GL_FRONT, GL_EMISSION, emission); + } + m_logos[i]->draw(); + if (i == m_logoToRemove) + { + glPopAttrib(); + } + glPopMatrix(); + } +} + +void Towers::drawGround() +{ + const float gs = 120; /* ground size */ + const float tt = 20; /* texture tiles */ + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBegin(GL_QUADS); + glNormal3f(0, 1, 0); + glTexCoord2f(0, 0); + glVertex3f(-gs, 0, gs); + glTexCoord2f(tt, 0); + glVertex3f(gs, 0, gs); + glTexCoord2f(tt, tt); + glVertex3f(gs, 0, -gs); + glTexCoord2f(0, tt); + glVertex3f(-gs, 0, -gs); + glEnd(); + glPopAttrib(); +} + +/* used by ODE to perform collision detection */ +void Towers_collide_callback(void * data, dGeomID o1, dGeomID o2) +{ + const int maxNumContacts = 2; + Towers * t = (Towers *) data; + static dContact contact[maxNumContacts]; + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnected(b1, b2)) + return; + int num = dCollide(o1, o2, maxNumContacts, + &contact[0].geom, sizeof(dContact)); + int i; + for (i = 0; i < num; i++) + { + contact[i].surface.mode = + dContactSlip1 | dContactSlip2 | dContactBounce | + dContactSoftERP | dContactSoftCFM | dContactApprox1; + contact[i].surface.mu = 0.5; + contact[i].surface.slip1 = 0.0; + contact[i].surface.slip2 = 0.0; + contact[i].surface.soft_erp = 0.8; + contact[i].surface.soft_cfm = 0.01; + contact[i].surface.bounce = 0.0; + dJointID joint = dJointCreateContact(t->m_world, + t->m_contactJointGroup, contact + i); + dJointAttach(joint, b1, b2); + } +} + +/* invokes ODE to do physics on our world */ +void Towers::worldStep() +{ + dSpaceCollide(m_space, this, Towers_collide_callback); + dWorldQuickStep(m_world, WORLD_STEP); + dJointGroupEmpty(m_contactJointGroup); +} + diff --git a/ss/Towers.h b/ss/Towers.h new file mode 100644 index 0000000..72f6018 --- /dev/null +++ b/ss/Towers.h @@ -0,0 +1,42 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * A slightly more interesting screensaver mode involving + * towers of DW logos with randomly disappearing ones + */ + +#ifndef TOWERS_H +#define TOWERS_H + +#include "SSMain.h" +#include "SSMode.h" +#include "LogoBox.h" +#include +#include + +class Towers : public SSMode +{ +public: + Towers(SSMain * _SSMain); + ~Towers(); + void update(); + +protected: + void createWorld(); + void worldStep(); + void drawLogos(); + void drawGround(); + friend void Towers_collide_callback(void * data, dGeomID o1, dGeomID o2); + + dWorldID m_world; + dSpaceID m_space; + dJointGroupID m_contactJointGroup; + std::vector m_logos; + Uint32 m_lastElapsed; + GLuint m_groundTexture; + int m_logoToRemove; + Uint32 m_lastRemoveTime; + GLuint m_logosList; +}; + +#endif diff --git a/ss/TumblingLogos.cc b/ss/TumblingLogos.cc new file mode 100644 index 0000000..11575c6 --- /dev/null +++ b/ss/TumblingLogos.cc @@ -0,0 +1,244 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * This module implements a slightly more interesting screensaver + * mode that contains many DornerWorks logos that "tumble" around + * inside a cube-shaped container as gravity randomly shifts every + * few seconds + */ + +#include /* fmax() */ +#include /* rand() */ +#include +#include +#include "TumblingLogos.h" +#include + +#include +using namespace std; + +#define WORLD_STEP 0.001 +/* number of milliseconds after which to switch gravity */ +#define GRAVITY_CHANGE_MSEC 4000 + +static double randDbl() +{ + return ((double) rand() / (double) RAND_MAX); +} + +TumblingLogos::TumblingLogos(SSMain * _SSMain) : SSMode(_SSMain) +{ + /* set up our specific openGL settings */ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60.0, + (double)m_SSMain->getWidth() + / (double)m_SSMain->getHeight() + / (double)m_SSMain->getNumMonitors(), + 0.01, + 1000.01); + glMatrixMode(GL_MODELVIEW); + + /* create the ODE world */ + createWorld(); + + m_lastGravityChangeTime = m_lastElapsed = 0; +} + +TumblingLogos::~TumblingLogos() +{ + int sz = m_logos.size(); + for (int i = 0; i < sz; i++) + { + delete m_logos[i]; + } + dJointGroupDestroy(m_contactJointGroup); + dWorldDestroy(m_world); + dSpaceDestroy(m_space); +} + +void TumblingLogos::createWorld() +{ + /* set up the ODE world and collision space */ + m_world = dWorldCreate(); +// dWorldSetCFM(m_world, 1e-5); + dWorldSetGravity(m_world, 0, -9.81, 0); + dWorldSetERP(m_world, 0.8); + m_space = dHashSpaceCreate(0); + m_contactJointGroup = dJointGroupCreate(0); + + m_logoSize = 0.0; + m_boxSize = 0.0; + double logo_offset = 0.0; + double logoCellSize = 0.0; + + /* create the logos to fill the tumble box */ + for (int x = 0; x < TUMBLE_BOX_CELLS; x++) + { + for (int y = 0; y < TUMBLE_BOX_CELLS; y++) + { + for (int z = 0; z < TUMBLE_BOX_CELLS; z++) + { + LogoBox * lb = new LogoBox(m_world, m_space); + + if (m_boxSize == 0.0) + { + m_logoSize = fmax(fmax(lb->getWidth(), lb->getDepth()), + lb->getHeight()); + logoCellSize = m_logoSize * 1.3; + m_boxSize = logoCellSize * TUMBLE_BOX_CELLS; + logo_offset = logoCellSize/2.0 - m_boxSize/2.0; + } + + dMatrix3 rotation; + dRFromEulerAngles(rotation, + randDbl() * M_PI * 2.0, + randDbl() * M_PI * 2.0, + randDbl() * M_PI * 2.0); + dBodySetPosition(lb->getBody(), + x * logoCellSize + logo_offset + + (randDbl() - 0.5) * 0.1 * m_logoSize, + y * logoCellSize + logo_offset + + (randDbl() - 0.5) * 0.1 * m_logoSize, + z * logoCellSize + logo_offset + + (randDbl() - 0.5) * 0.1 * m_logoSize); + dBodySetRotation(lb->getBody(), rotation); + m_logos.push_back(lb); + } + } + } + + /* tumble box will be size m_boxSize X m_boxSize X m_boxSize + * centered at the origin */ + /* create planes surrounding the tumble box */ + dCreatePlane(m_space, -1, 0, 0, -m_boxSize/2.0); + dCreatePlane(m_space, 1, 0, 0, -m_boxSize/2.0); + dCreatePlane(m_space, 0, -1, 0, -m_boxSize/2.0); + dCreatePlane(m_space, 0, 1, 0, -m_boxSize/2.0); + dCreatePlane(m_space, 0, 0, -1, -m_boxSize/2.0); + dCreatePlane(m_space, 0, 0, 1, -m_boxSize/2.0); +} + +void TumblingLogos::update() +{ + SSMode::update(); + + if (m_lastElapsed == 0) + m_lastGravityChangeTime = m_lastElapsed = m_elapsed; + + /* change gravity if it has been GRAVITY_CHANGE_MSEC milliseconds */ + if (m_elapsed - m_lastGravityChangeTime > GRAVITY_CHANGE_MSEC) + { + changeGravity(); + m_lastGravityChangeTime = m_elapsed; + } + + Uint32 delta = m_elapsed - m_lastElapsed; + for (unsigned int i = 0; i < delta / 2; i++) + worldStep(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + int numMonitors = m_SSMain->getNumMonitors(); + int width = m_SSMain->getWidth(); + int height = m_SSMain->getHeight(); + for (int i = 0; i < numMonitors; i++) + { + glViewport(i*width/numMonitors, 0, width/numMonitors, height); + glLoadIdentity(); + gluLookAt(0, 0, m_boxSize * 1.25, 0, 0, 0, 0, 1, 0); + glRotatef(m_elapsed/60.0f, 0, 1, 0); + + /* draw the logos */ + int sz = m_logos.size(); + for (int i = 0; i < sz; i++) + { + dBodyID body = m_logos[i]->getBody(); + pushTransform(dBodyGetPosition(body), + dBodyGetRotation(body)); + m_logos[i]->draw(); + glPopMatrix(); + } + + /* draw the wireframe bounding box */ + glColor3f(1, 1, 1); + double halfBox = m_boxSize/2.0; + glPushAttrib(GL_LIGHTING_BIT); + glDisable(GL_LIGHTING); + for (int i = -1; i <= 1; i += 2) + { + glBegin(GL_LINE_LOOP); + glVertex3f(-halfBox, -halfBox, m_boxSize*i/2.0); + glVertex3f(-halfBox, halfBox, m_boxSize*i/2.0); + glVertex3f(halfBox, halfBox, m_boxSize*i/2.0); + glVertex3f(halfBox, -halfBox, m_boxSize*i/2.0); + glEnd(); + glBegin(GL_LINES); + glVertex3f(m_boxSize*i/2.0, -halfBox, -halfBox); + glVertex3f(m_boxSize*i/2.0, -halfBox, halfBox); + glVertex3f(m_boxSize*i/2.0, halfBox, -halfBox); + glVertex3f(m_boxSize*i/2.0, halfBox, halfBox); + glEnd(); + } + glPopAttrib(); + } + SDL_GL_SwapBuffers(); + + m_lastElapsed = m_elapsed; +} + +/* used by ODE to perform collision detection */ +void TumblingLogos_collide_callback(void * data, dGeomID o1, dGeomID o2) +{ + TumblingLogos * tl = (TumblingLogos *) data; + static dContact contact[4]; + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnected(b1, b2)) + return; + int num = dCollide(o1, o2, 4, &contact[0].geom, sizeof(dContact)); + int i; + for (i = 0; i < num; i++) + { + contact[i].surface.mode = + dContactSlip1 | dContactSlip2 | dContactBounce | + dContactSoftERP | dContactSoftCFM | dContactApprox1; + contact[i].surface.mu = 0.5; + contact[i].surface.slip1 = 0.0; + contact[i].surface.slip2 = 0.0; + contact[i].surface.soft_erp = 0.8; + contact[i].surface.soft_cfm = 0.01; + contact[i].surface.bounce = 0.4; + dJointID joint = dJointCreateContact(tl->m_world, + tl->m_contactJointGroup, contact + i); + dJointAttach(joint, b1, b2); + } +} + +/* invokes ODE to do physics on our world */ +void TumblingLogos::worldStep() +{ + dSpaceCollide(m_space, this, TumblingLogos_collide_callback); + dWorldQuickStep(m_world, WORLD_STEP); + dJointGroupEmpty(m_contactJointGroup); +} + +/* randomly chooses a gravity direction */ +void TumblingLogos::changeGravity() +{ + double g[3] = {0.0, 0.0, 0.0}; + double r = randDbl(); + if (r < 0.7) + { + /* gravitate towards one of the six sides */ + g[(int) (randDbl() * 3)] = ((randDbl() < 0.5) ? -1 : 1) * 9.81; + } + else + { + /* gravitate towards one of the eight corners */ + for (int i = 0; i < 3; i++) + { + g[i] = ((randDbl() < 0.5) ? -1 : 1) * 9.81; + } + } + dWorldSetGravity(m_world, g[0], g[1], g[2]); +} diff --git a/ss/TumblingLogos.h b/ss/TumblingLogos.h new file mode 100644 index 0000000..f316f8a --- /dev/null +++ b/ss/TumblingLogos.h @@ -0,0 +1,43 @@ + +/* Author: Josh Holtrop + * DornerWorks screensaver + * A slightly more interesting screensaver mode involving + * colliding DW logos contained in a cube that spins while + * gravity randomly changes direction + */ + +#ifndef TUMBLING_LOGOS_H +#define TUMBLING_LOGOS_H + +#include "SSMain.h" +#include "SSMode.h" +#include "LogoBox.h" +#include +#include + +#define TUMBLE_BOX_CELLS 4 + +class TumblingLogos : public SSMode +{ +public: + TumblingLogos(SSMain * _SSMain); + ~TumblingLogos(); + void update(); + +protected: + void createWorld(); + void worldStep(); + void changeGravity(); + friend void TumblingLogos_collide_callback(void * data, dGeomID o1, dGeomID o2); + + dWorldID m_world; + dSpaceID m_space; + double m_logoSize; + double m_boxSize; + dJointGroupID m_contactJointGroup; + std::vector m_logos; + Uint32 m_lastGravityChangeTime; + Uint32 m_lastElapsed; +}; + +#endif