Gopher works a bit like a conventional music tracker: it "plays" a file row by row except instead of mixing samples it calls functions with interpolated parameters corresponding to the current row.
You can write your effects once and then [re-]use them in different parts of your demo, in different demos or share them with other sceners.
Another advantage is that you'll never have to write synchronization code again. I simply got sick of breaking up my demo in a whole bunch of small "while" loops and hardcoding the sequence and parameters of my effects.
Portability: the player is written using SDL so your demo should be portable everywhere SDL is supported. Portability also means if a member of your demogroup only uses Linux and your other coders use Windows, she'll still be able to contribute.
Lastly: you'll never have to recompile anything to change how your demo behaves, what texture it should use, when some effect starts and eventually, a demo editor that looks and acts somewhat like Adobe Premiere can be written to make demo editing a visual process with realtime previews and a tracker-like interface.
This assumes your development computer is configured to use SDL and that you already know how to create DLLs and to operate a C or C++ compiler.
Gopher_Init Gopher_BeginNewFrame Gopher_Quit...to the .DEF file you created in section 4.1)
Plasma...to the .DEF file you created in section 4.1.
For example:
BOOL Plasma ( signed long wave1, signed long wave2, signed long wave3, signed long outputSurfaceIndex );...this function takes three parameters that determine what the effect looks like (wave1-3) and one parameter that determines where the effect is rendered (outputSurfaceIndex)
I use the convention that if one of my effects receives '-1' as outputSurfaceIndex, the effect is rendered to one of the outputSurfaces (specifically, outputSurfaces[currentOutputSurface]) otherwise, the effect is rendered in inputSurfaces[outputSurfaceIndex]
This convention isn't necessary if you would rather have one of your functions take an index into inputSurfaces as a parameter and blit it to the current output surface, but this is an extra operation...
Gopher is designed so that when it starts calling effect functions in your library, they execute very quickly and do exactly what they are told.
It may happen that a particular frame took longer to render than an another one, in this case that frame might have been the last one to call a specific effect function. This means you have no guarantee a given set of functions at a given millisecond in your track will ever get executed, because it is possible an execution prior to that took longer than expected (the system has to keep up: it was deemed more important to have it keep up and be synchronized than to produce perfect, fixed framerate output. This type of output could be achieved by an offline version of Gopher that outputs a video file or something..)
Effect functions are therefore not recommended to keep ANY state for future calls nor to expect to be called at all during a demo, in case some user cranks up the detail level on their computer and the demo runs at 1 frame per second.
Effect functions should have all their data initialized in Gopher_Init, which would preferentially read from an external file what special parameters should be set, so that changing what texture is loaded or what image maps are used is something that can be done without recompiling the library, hence extending its re-use and making it simpler for the person putting the demo together (especially if an editor is used in the future).
Hopefully the suggestions in Section 4.4 were followed and your DLL is now externally configurable. Create some sample data so that you can test your code.
Start a text file (ie: demo.xml) and insert the following text:
<?xml version="1.0" encoding="windows-1252"?> <!DOCTYPE GopherDemoFile SYSTEM "http://gopher.sourceforge.net/Gopher1.5.dtd"> <GopherDemoFile> </GopherDemoFile>
<Libraries> </Libraries>
<Library FileName = "blarg.dll"> </Library>
<Function Name = "Plasma"> <Parameter> wave1 </Parameter> <Parameter> wave2 </Parameter> <Parameter> wave3 </Parameter> <Parameter> destSurface </Parameter> </Function>
<?xml version="1.0" encoding="windows-1252"?> <!DOCTYPE GopherDemoFile SYSTEM "http://gopher.sourceforge.net/Gopher1.5.dtd"> <GopherDemoFile> <Libraries> <Library FileName = "blarg.dll"> <Function Name = "Plasma"> <Parameter> wave1 </Parameter> <Parameter> wave2 </Parameter> <Parameter> wave3 </Parameter> <Parameter> destSurface </Parameter> </Function> <Function Name = "Blend"> <Parameter> sourceSurface </Parameter> <Parameter> blendAmount </Parameter> <Parameter> destSurface </Parameter> </Function> </Library> <Library FileName = "mystic.dll" /> </Libraries> </GopherDemoFile>
Insert the following lines after the "Libraries" element:
<Demo> </Demo>
<Summary> </Summary>
<Author> Purple Motion </Author>
<Group> Future Crew </Group>
<ExtraInfo> 1st place at Assembly 1993 </ExtraInfo>
<Demo> <Summary> <Author> Purple Motion </Author> <Author> Psi </Author> <Group> Future Crew </Group> <ExtraInfo> 1st place at Assembly 1993 </ExtraInfo> </Summary> </Demo>
Only one of these are necessary (and allowed) and it goes after "Summary", but before the end of "Demo":
<Settings RunningTime = "56500" UseOpenGL = "false" NumberOfInputSurfaces = "32" InputMusicFile = "data\omf-ar0.s3m" />
In the future, the player could determine how much RAM is required and give the option to the user to reduce their resolution if they don't have enough physical RAM [available].
For each Track you'll want to use (you need at least one) insert "Track" elements after the "Settings" element:
<Track> </Track>
An important thing to know: for every frame, tracks are executed from last to first. This makes it easier to supply a "blit" function throughtout the demo as you only need to stick it in the first track and have one event execute throughout the demo as opposed to trying to use whatever track is free for a given event. (trust me on this one, I tried it the other way and it made the Gopher demo file very ugly and hard to use)
Just think of it like a music tracker and at the low level, channels are being played in reverse order, from right to left (ie: 4, 3, 2, 1).
A track is composed of one or more "Events". An Event is composed of fixed start and end time points, a function name, the library in which said function name is found, and a list of what the parameters should be equal to at the start of and the end of the event.
StartTime and EndTime are in milliseconds. Be careful not to overlap times for events within the same track. (times can overlap for events in different tracks)
<Event LibraryFileName = "blarg.dll" FunctionName = "Plasma" StartTime = "0" EndTime = "14000"> </Event>
Supply as many "param" elements, in the "Event" element, as the function needs parameters and give values to the "start" and "end" attributes:
<param start="0" end="1000" /> <param start="10000" end="0" /> <param start="250" end="750" /> <param start="31" end="31" />
In this example, the "Plasma" function in the library "blarg.dll" will be executed for every frame between 0 and 14000 miliseconds (14 seconds), with the first parameter going from 0 to 1000, the second from 1000 to 0, the third from 250 to 750 and the last staying put at 31 for the entire time.
Your completed demo file should look like this:
<?xml version="1.0" encoding="windows-1252"?> <!DOCTYPE GopherDemoFile SYSTEM "http://gopher.sourceforge.net/Gopher1.5.dtd"> <GopherDemoFile> <Libraries> <Library FileName = "blarg.dll"> <Function Name = "Plasma"> <Parameter> wave1 </Parameter> <Parameter> wave2 </Parameter> <Parameter> wave3 </Parameter> <Parameter> destSurface </Parameter> </Function> <Function Name = "Blend"> <Parameter> sourceSurface </Parameter> <Parameter> blendAmount </Parameter> <Parameter> destSurface </Parameter> </Function> </Library> <Library FileName = "mystic.dll" /> </Libraries> <Demo> <Summary> <Author> Purple Motion </Author> <Author> Psi </Author> <Group> Future Crew </Group> <ExtraInfo> 1st place at Assembly 1993 </ExtraInfo> </Summary> <Settings RunningTime = "56500" UseOpenGL = "false" NumberOfInputSurfaces = "32" InputMusicFile = "data\omf-ar0.s3m" /> <Track> <Event LibraryFileName = "blarg.dll" FunctionName = "Plasma" StartTime = "0" EndTime = "14000"> <param start="0" end="1000" /> <param start="10000" end="0" /> <param start="250" end="750" /> <param start="31" end="31" /> </Event> </Track> </Demo> </GopherDemoFile>
That's it! Look at the supplied example (trix.xml) for what a simple, complete demo looks like and how files are typically organized for a demo.