The DOOM Classic for iPhone 1.0 source as released on November 3, 2009

This commit is contained in:
Travis Bradshaw
2012-01-31 16:35:15 -06:00
commit 0cdbbdf96e
316 changed files with 127631 additions and 0 deletions
BIN
View File
Binary file not shown.
+127
View File
@@ -0,0 +1,127 @@
/*
* doomiphone.h
* doom
*
* Created by John Carmack on 3/13/09.
* Copyright 2009 idSoftware. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <setjmp.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
#include <dlfcn.h>
#include <dlfcn.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <dirent.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <ifaddrs.h>
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#include "iphone/gles_glue.h"
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#include <OpenAL/oalStaticBufferExtension.h>
#undef ALCAPI
#define ALCAPI
#undef false
#undef true
#include "prboom/SDL_opengl.h"
// prBoom code
#include "prboom/m_fixed.h"
#include "prboom/doomdef.h"
#include "prboom/doomtype.h"
#include "prboom/doomstat.h"
#include "prboom/d_net.h"
#include "prboom/dstrings.h"
#include "prboom/sounds.h"
#include "prboom/z_zone.h"
#include "prboom/w_wad.h"
#include "prboom/s_sound.h"
#include "prboom/v_video.h"
#include "prboom/f_finale.h"
#include "prboom/f_wipe.h"
#include "prboom/m_argv.h"
#include "prboom/m_misc.h"
#include "prboom/m_menu.h"
#include "prboom/p_checksum.h"
#include "prboom/i_main.h"
#include "prboom/i_system.h"
#include "prboom/i_sound.h"
#include "prboom/i_video.h"
#include "prboom/g_game.h"
#include "prboom/hu_stuff.h"
#include "prboom/wi_stuff.h"
#include "prboom/st_stuff.h"
#include "prboom/am_map.h"
#include "prboom/p_setup.h"
#include "prboom/r_draw.h"
#include "prboom/r_main.h"
#include "prboom/r_fps.h"
#include "prboom/d_main.h"
#include "prboom/d_deh.h"
#include "prboom/lprintf.h"
#include "prboom/am_map.h"
#include "prboom/gl_intern.h"
#include "prboom/p_mobj.h"
#include "prboom/p_maputl.h"
#include "prboom/p_map.h"
// open / close name collision problem... #include "prboom/p_spec.h"
#include "prboom/p_inter.h"
#include "prboom/m_random.h"
#include "prboom/m_bbox.h"
#include "prboom/m_cheat.h"
// we will now define landscapeViewport / landscapeScissor to rotate the coords
#undef glViewport
#undef glScissor
// our vestigial system environment
#include "iphone/misc.h"
#include "iphone/cvar.h"
// new iphone code
#include "iphone/ipak.h"
#include "iphone/iphone_doom.h"
#include "iphone/iphone_email.h" //gsh, adds support for emailing the console to id
BIN
View File
Binary file not shown.
+539
View File
@@ -0,0 +1,539 @@
/*
* BackgroundMusic.cpp
* doom
*
* Created by John Carmack on 5/15/09.
* Copyright 2009 Id Software. All rights reserved.
*
*/
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//==================================================================================================
// Includes
//==================================================================================================
// System Includes
#include <AudioToolbox/AudioToolbox.h>
#include <CoreFoundation/CFURL.h>
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#include <map>
#include <vector>
#include <pthread.h>
#include <mach/mach.h>
#include <string>
extern "C" {
#include "doomiphone.h"
}
enum {
kSoundEngineErrUnitialized = 1,
kSoundEngineErrInvalidID = 2,
kSoundEngineErrFileNotFound = 3,
kSoundEngineErrInvalidFileFormat = 4,
kSoundEngineErrDeviceNotFound = 5
};
#define AssertNoError(inMessage, inHandler) \
if(result != noErr) \
{ \
printf("%s: %d\n", inMessage, (int)result); \
goto inHandler; \
}
#define kNumberBuffers 3
static Float32 gMasterVolumeGain = 0.5f;
//==================================================================================================
// Helper functions
//==================================================================================================
OSStatus LoadFileDataInfo(const char *inFilePath, AudioFileID &outAFID, AudioStreamBasicDescription &outFormat, UInt64 &outDataSize)
{
UInt32 thePropSize;
CFURLRef theURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8*)inFilePath, strlen(inFilePath), false);
if (theURL == NULL)
return kSoundEngineErrFileNotFound;
OSStatus result = AudioFileOpenURL(theURL, kAudioFileReadPermission, 0, &outAFID);
CFRelease(theURL);
AssertNoError("Error opening file", end);
thePropSize = sizeof(outFormat);
result = AudioFileGetProperty(outAFID, kAudioFilePropertyDataFormat, &thePropSize, &outFormat);
AssertNoError("Error getting file format", end);
thePropSize = sizeof(UInt64);
result = AudioFileGetProperty(outAFID, kAudioFilePropertyAudioDataByteCount, &thePropSize, &outDataSize);
AssertNoError("Error getting file data size", end);
end:
return result;
}
void CalculateBytesForTime (AudioStreamBasicDescription & inDesc, UInt32 inMaxPacketSize, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets)
{
static const UInt32 maxBufferSize = 0x10000; // limit size to 64K
static const UInt32 minBufferSize = 0x4000; // limit size to 16K
if (inDesc.mFramesPerPacket) {
Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;
*outBufferSize = (long unsigned int)numPacketsForTime * inMaxPacketSize;
} else {
// if frames per packet is zero, then the codec has no predictable packet == time
// so we can't tailor this (we don't know how many Packets represent a time period
// we'll just return a default buffer size
*outBufferSize = maxBufferSize > inMaxPacketSize ? maxBufferSize : inMaxPacketSize;
}
// we're going to limit our size to our default
if (*outBufferSize > maxBufferSize && *outBufferSize > inMaxPacketSize)
*outBufferSize = maxBufferSize;
else {
// also make sure we're not too small - we don't want to go the disk for too small chunks
if (*outBufferSize < minBufferSize)
*outBufferSize = minBufferSize;
}
*outNumPackets = *outBufferSize / inMaxPacketSize;
}
static Boolean MatchFormatFlags(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
{
UInt32 xFlags = x.mFormatFlags;
UInt32 yFlags = y.mFormatFlags;
// match wildcards
if (x.mFormatID == 0 || y.mFormatID == 0 || xFlags == 0 || yFlags == 0)
return true;
if (x.mFormatID == kAudioFormatLinearPCM)
{
// knock off the all clear flag
xFlags = xFlags & ~kAudioFormatFlagsAreAllClear;
yFlags = yFlags & ~kAudioFormatFlagsAreAllClear;
// if both kAudioFormatFlagIsPacked bits are set, then we don't care about the kAudioFormatFlagIsAlignedHigh bit.
if (xFlags & yFlags & kAudioFormatFlagIsPacked) {
xFlags = xFlags & ~kAudioFormatFlagIsAlignedHigh;
yFlags = yFlags & ~kAudioFormatFlagIsAlignedHigh;
}
// if both kAudioFormatFlagIsFloat bits are set, then we don't care about the kAudioFormatFlagIsSignedInteger bit.
if (xFlags & yFlags & kAudioFormatFlagIsFloat) {
xFlags = xFlags & ~kAudioFormatFlagIsSignedInteger;
yFlags = yFlags & ~kAudioFormatFlagIsSignedInteger;
}
// if the bit depth is 8 bits or less and the format is packed, we don't care about endianness
if((x.mBitsPerChannel <= 8) && ((xFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
{
xFlags = xFlags & ~kAudioFormatFlagIsBigEndian;
}
if((y.mBitsPerChannel <= 8) && ((yFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
{
yFlags = yFlags & ~kAudioFormatFlagIsBigEndian;
}
// if the number of channels is 0 or 1, we don't care about non-interleavedness
if (x.mChannelsPerFrame <= 1 && y.mChannelsPerFrame <= 1) {
xFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
yFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
}
}
return xFlags == yFlags;
}
Boolean FormatIsEqual(AudioStreamBasicDescription x, AudioStreamBasicDescription y)
{
// the semantics for equality are:
// 1) Values must match exactly
// 2) wildcard's are ignored in the comparison
#define MATCH(name) ((x.name) == 0 || (y.name) == 0 || (x.name) == (y.name))
return
((x.mSampleRate==0.) || (y.mSampleRate==0.) || (x.mSampleRate==y.mSampleRate))
&& MATCH(mFormatID)
&& MatchFormatFlags(x, y)
&& MATCH(mBytesPerPacket)
&& MATCH(mFramesPerPacket)
&& MATCH(mBytesPerFrame)
&& MATCH(mChannelsPerFrame)
&& MATCH(mBitsPerChannel) ;
}
#pragma mark ***** BackgroundTrackMgr *****
//==================================================================================================
// BackgroundTrackMgr class
//==================================================================================================
typedef struct BG_FileInfo {
std::string mFilePath;
AudioFileID mAFID;
AudioStreamBasicDescription mFileFormat;
UInt64 mFileDataSize;
//UInt64 mFileNumPackets; // this is only used if loading file to memory
Boolean mLoadAtOnce;
Boolean mFileDataInQueue;
} BackgroundMusicFileInfo;
class BackgroundTrackMgr
{
public:
BackgroundTrackMgr();
~BackgroundTrackMgr();
void Teardown();
static void QueueCallback( void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer);
OSStatus SetupQueue(BG_FileInfo *inFileInfo);
OSStatus SetupBuffers(BG_FileInfo *inFileInfo);
OSStatus LoadTrack(const char* inFilePath, Boolean inAddToQueue, Boolean inLoadAtOnce);
OSStatus SetVolume(Float32 inVolume);
Float32 GetVolume() const;
OSStatus Start();
OSStatus Stop(Boolean inStopAtEnd);
AudioQueueRef mQueue;
AudioQueueBufferRef mBuffers[kNumberBuffers];
UInt32 mBufferByteSize;
SInt64 mCurrentPacket;
UInt32 mNumPacketsToRead;
Float32 mVolume;
AudioStreamPacketDescription * mPacketDescs;
static BG_FileInfo * CurFileInfo;
Boolean mStopAtEnd;
};
BG_FileInfo *BackgroundTrackMgr::CurFileInfo;
BackgroundTrackMgr::BackgroundTrackMgr()
: mQueue(0),
mBufferByteSize(0),
mCurrentPacket(0),
mNumPacketsToRead(0),
mVolume(1.0f),
mPacketDescs(NULL),
mStopAtEnd(false)
{ }
BackgroundTrackMgr::~BackgroundTrackMgr() {
Teardown();
}
void BackgroundTrackMgr::Teardown() {
if (mQueue) {
AudioQueueDispose(mQueue, true);
mQueue = NULL;
}
if ( CurFileInfo ) {
AudioFileClose( CurFileInfo->mAFID);
delete CurFileInfo;
CurFileInfo = NULL;
}
if (mPacketDescs) {
delete[] mPacketDescs;
mPacketDescs = NULL;
}
}
void BackgroundTrackMgr::QueueCallback( void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer ) {
// dispose of the buffer if no longer in use
OSStatus result = noErr;
BackgroundTrackMgr *THIS = (BackgroundTrackMgr*)inUserData;
UInt32 nPackets = 0;
// loop the current buffer if the following:
// 1. file was loaded into the buffer previously
// 2. only one file in the queue
// 3. we have not been told to stop at playlist completion
if ((CurFileInfo->mFileDataInQueue) && (!THIS->mStopAtEnd)) {
nPackets = THIS->mNumPacketsToRead;
} else {
UInt32 numBytes;
while (nPackets == 0) {
// if loadAtOnce, get all packets in the file, otherwise ~.5 seconds of data
nPackets = THIS->mNumPacketsToRead;
result = AudioFileReadPackets(CurFileInfo->mAFID, false, &numBytes, THIS->mPacketDescs, THIS->mCurrentPacket, &nPackets,
inCompleteAQBuffer->mAudioData);
AssertNoError("Error reading file data", end);
inCompleteAQBuffer->mAudioDataByteSize = numBytes;
if (nPackets == 0) { // no packets were read, this file has ended.
if (CurFileInfo->mLoadAtOnce) {
CurFileInfo->mFileDataInQueue = true;
}
THIS->mCurrentPacket = 0;
// we have gone through the playlist. if mStopAtEnd, stop the queue here
if ( THIS->mStopAtEnd ) {
result = AudioQueueStop(inAQ, false);
AssertNoError("Error stopping queue", end);
return;
}
}
}
}
result = AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, (THIS->mPacketDescs ? nPackets : 0), THIS->mPacketDescs);
if(result != noErr) {
result = AudioQueueFreeBuffer(inAQ, inCompleteAQBuffer);
AssertNoError("Error freeing buffers that didn't enqueue", end);
}
AssertNoError("Error enqueuing new buffer", end);
if (CurFileInfo->mLoadAtOnce) {
CurFileInfo->mFileDataInQueue = true;
}
THIS->mCurrentPacket += nPackets;
end:
return;
}
OSStatus BackgroundTrackMgr::SetupQueue(BG_FileInfo *inFileInfo) {
UInt32 size = 0;
OSStatus result = AudioQueueNewOutput(&inFileInfo->mFileFormat, QueueCallback, this,
CFRunLoopGetMain() /* CFRunLoopGetCurrent() */, kCFRunLoopCommonModes, 0, &mQueue);
AssertNoError("Error creating queue", end);
#if 0
// (2) If the file has a cookie, we should get it and set it on the AQ
size = sizeof(UInt32);
result = AudioFileGetPropertyInfo (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, NULL);
if (!result && size) {
char* cookie = new char [size];
result = AudioFileGetProperty (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, cookie);
AssertNoError("Error getting magic cookie", end);
result = AudioQueueSetProperty(mQueue, kAudioQueueProperty_MagicCookie, cookie, size);
delete [] cookie;
AssertNoError("Error setting magic cookie", end);
}
#endif
// channel layout
OSStatus err = AudioFileGetPropertyInfo(inFileInfo->mAFID, kAudioFilePropertyChannelLayout, &size, NULL);
if (err == noErr && size > 0) {
AudioChannelLayout *acl = (AudioChannelLayout *)malloc(size);
result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyChannelLayout, &size, acl);
AssertNoError("Error getting channel layout from file", end);
result = AudioQueueSetProperty(mQueue, kAudioQueueProperty_ChannelLayout, acl, size);
free(acl);
AssertNoError("Error setting channel layout on queue", end);
}
// volume
result = SetVolume(mVolume);
end:
return result;
}
OSStatus BackgroundTrackMgr::SetupBuffers(BG_FileInfo *inFileInfo) {
int numBuffersToQueue = kNumberBuffers;
UInt32 maxPacketSize;
UInt32 size = sizeof(maxPacketSize);
// we need to calculate how many packets we read at a time, and how big a buffer we need
// we base this on the size of the packets in the file and an approximate duration for each buffer
// first check to see what the max size of a packet is - if it is bigger
// than our allocation default size, that needs to become larger
OSStatus result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
AssertNoError("Error getting packet upper bound size", end);
bool isFormatVBR = (inFileInfo->mFileFormat.mBytesPerPacket == 0 || inFileInfo->mFileFormat.mFramesPerPacket == 0);
CalculateBytesForTime(inFileInfo->mFileFormat, maxPacketSize, 0.5/*seconds*/, &mBufferByteSize, &mNumPacketsToRead);
// if the file is smaller than the capacity of all the buffer queues, always load it at once
if ((mBufferByteSize * numBuffersToQueue) > inFileInfo->mFileDataSize) {
inFileInfo->mLoadAtOnce = true;
}
if (inFileInfo->mLoadAtOnce) {
UInt64 theFileNumPackets;
size = sizeof(UInt64);
result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyAudioDataPacketCount, &size, &theFileNumPackets);
AssertNoError("Error getting packet count for file", end);
mNumPacketsToRead = (UInt32)theFileNumPackets;
mBufferByteSize = (UInt32)inFileInfo->mFileDataSize;
numBuffersToQueue = 1;
} else {
mNumPacketsToRead = mBufferByteSize / maxPacketSize;
}
if (isFormatVBR) {
mPacketDescs = new AudioStreamPacketDescription [mNumPacketsToRead];
} else {
mPacketDescs = NULL; // we don't provide packet descriptions for constant bit rate formats (like linear PCM)
}
// allocate the queue's buffers
for (int i = 0; i < numBuffersToQueue; ++i) {
result = AudioQueueAllocateBuffer(mQueue, mBufferByteSize, &mBuffers[i]);
AssertNoError("Error allocating buffer for queue", end);
QueueCallback (this, mQueue, mBuffers[i]);
if (inFileInfo->mLoadAtOnce) {
inFileInfo->mFileDataInQueue = true;
}
}
end:
return result;
}
OSStatus BackgroundTrackMgr::LoadTrack(const char* inFilePath, Boolean inAddToQueue, Boolean inLoadAtOnce) {
// OSStatus result = LoadFileDataInfo(CurFileInfo->mFilePath.c_str(), CurFileInfo->mAFID, CurFileInfo->mFileFormat, CurFileInfo->mFileDataSize);
// AssertNoError("Error getting file data info", fail);
OSStatus result;
UInt32 thePropSize;
CFURLRef theURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8*)inFilePath, strlen(inFilePath), false);
if (theURL == NULL)
result = kSoundEngineErrFileNotFound;
else
result = 0;
AssertNoError("Error opening URL", fail);
CurFileInfo = new BG_FileInfo;
CurFileInfo->mFilePath = inFilePath;
result = AudioFileOpenURL(theURL, kAudioFileReadPermission, 0, &CurFileInfo->mAFID);
CFRelease(theURL);
AssertNoError("Error opening file", fail);
thePropSize = sizeof(CurFileInfo->mFileFormat);
result = AudioFileGetProperty(CurFileInfo->mAFID, kAudioFilePropertyDataFormat, &thePropSize, &CurFileInfo->mFileFormat);
AssertNoError("Error getting file format", fail);
thePropSize = sizeof(UInt64);
result = AudioFileGetProperty(CurFileInfo->mAFID, kAudioFilePropertyAudioDataByteCount, &thePropSize, &CurFileInfo->mFileDataSize);
AssertNoError("Error getting file data size", fail);
CurFileInfo->mLoadAtOnce = inLoadAtOnce;
CurFileInfo->mFileDataInQueue = false;
result = SetupQueue(CurFileInfo);
AssertNoError("Error setting up queue", fail);
result = SetupBuffers(CurFileInfo);
AssertNoError("Error setting up queue buffers", fail);
return result;
fail:
if (CurFileInfo) {
delete CurFileInfo;
CurFileInfo = NULL;
}
return result;
}
OSStatus BackgroundTrackMgr::SetVolume(Float32 inVolume) {
mVolume = inVolume;
return AudioQueueSetParameter(mQueue, kAudioQueueParam_Volume, mVolume * gMasterVolumeGain);
}
Float32 BackgroundTrackMgr::GetVolume() const {
return mVolume;
}
OSStatus BackgroundTrackMgr::Start() {
OSStatus result = AudioQueuePrime(mQueue, 1, NULL);
if (result) {
printf("BackgroundTrackMgr: Error priming queue: %d\n", (int)result);
return result;
}
return AudioQueueStart(mQueue, NULL);
}
OSStatus BackgroundTrackMgr::Stop(Boolean inStopAtEnd) {
if (inStopAtEnd) {
mStopAtEnd = true;
return noErr;
} else {
return AudioQueueStop(mQueue, true);
}
}
static BackgroundTrackMgr sBackgroundTrackMgr;
static char currentMusicName[1024];
void iphonePauseMusic() {
if ( music->value == 0 ) {
// music is disabled
return;
}
AudioQueuePause(sBackgroundTrackMgr.mQueue);
}
void iphoneResumeMusic() {
if ( music->value == 0 ) {
// music is disabled
return;
}
AudioQueueStart(sBackgroundTrackMgr.mQueue,NULL);
}
void iphoneStopMusic() {
sBackgroundTrackMgr.Teardown();
}
void iphoneStartMusic() {
if ( music->value == 0 ) {
// music is disabled
return;
}
char fullName[1024];
sprintf( fullName, "%s/base/music/d_%s.mp3", SysIphoneGetAppDir(), currentMusicName );
printf( "Starting music '%s'\n", fullName );
iphoneStopMusic();
sBackgroundTrackMgr.LoadTrack( fullName, false, true);
sBackgroundTrackMgr.Start();
if ( !strcmp( currentMusicName, "intro" ) ) {
// stop the intro music at end, don't loop
sBackgroundTrackMgr.mStopAtEnd = true;
} else {
sBackgroundTrackMgr.mStopAtEnd = false;
}
}
void iphonePlayMusic( const char *name ) {
strcpy( currentMusicName, name );
iphoneStartMusic();
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,574 @@
// !$*UTF8*$!
{
1D6058900D05DD3D006BFB54 /* Doom */ = {
activeExec = 0;
executables = (
ED9BAB90108380AC00166CDA /* Doom */,
);
};
29B97313FDCFA39411CA2CEA /* Project object */ = {
activeBuildConfigurationName = Debug;
activeExecutable = ED9BAB90108380AC00166CDA /* Doom */;
activeSDKPreference = iphonesimulator2.0;
activeTarget = 1D6058900D05DD3D006BFB54 /* Doom */;
addToTargets = (
1D6058900D05DD3D006BFB54 /* Doom */,
);
codeSenseManager = ED9BABB0108380C600166CDA /* Code sense */;
executables = (
ED9BAB90108380AC00166CDA /* Doom */,
);
perUserDictionary = {
PBXConfiguration.PBXFileTableDataSource3.PBXBookmarksDataSource = {
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
PBXFileTableDataSourceColumnSortingKey = PBXBookmarksDataSource_NameID;
PBXFileTableDataSourceColumnWidthsKey = (
200,
200,
293.58349609375,
);
PBXFileTableDataSourceColumnsKey = (
PBXBookmarksDataSource_LocationID,
PBXBookmarksDataSource_NameID,
PBXBookmarksDataSource_CommentsID,
);
};
PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
PBXFileTableDataSourceColumnWidthsKey = (
20,
484,
20,
48,
43,
43,
20,
);
PBXFileTableDataSourceColumnsKey = (
PBXFileDataSource_FiletypeID,
PBXFileDataSource_Filename_ColumnID,
PBXFileDataSource_Built_ColumnID,
PBXFileDataSource_ObjectSize_ColumnID,
PBXFileDataSource_Errors_ColumnID,
PBXFileDataSource_Warnings_ColumnID,
PBXFileDataSource_Target_ColumnID,
);
};
PBXConfiguration.PBXFileTableDataSource3.PBXFindDataSource = {
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
PBXFileTableDataSourceColumnSortingKey = PBXFindDataSource_LocationID;
PBXFileTableDataSourceColumnWidthsKey = (
200,
498,
);
PBXFileTableDataSourceColumnsKey = (
PBXFindDataSource_MessageID,
PBXFindDataSource_LocationID,
);
};
PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = {
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
PBXFileTableDataSourceColumnWidthsKey = (
20,
444,
60,
20,
48,
43,
43,
);
PBXFileTableDataSourceColumnsKey = (
PBXFileDataSource_FiletypeID,
PBXFileDataSource_Filename_ColumnID,
PBXTargetDataSource_PrimaryAttribute,
PBXFileDataSource_Built_ColumnID,
PBXFileDataSource_ObjectSize_ColumnID,
PBXFileDataSource_Errors_ColumnID,
PBXFileDataSource_Warnings_ColumnID,
);
};
PBXPerProjectTemplateStateSaveDate = 279053176;
PBXWorkspaceStateSaveDate = 279053176;
};
perUserProjectItems = {
ED2D127F10838A65003A9380 /* PBXBookmark */ = ED2D127F10838A65003A9380 /* PBXBookmark */;
ED2D128210838A65003A9380 /* PBXBookmark */ = ED2D128210838A65003A9380 /* PBXBookmark */;
ED9AB3E410967730000B5852 /* PlistBookmark */ = ED9AB3E410967730000B5852 /* PlistBookmark */;
ED9AB3E510967730000B5852 /* PBXTextBookmark */ = ED9AB3E510967730000B5852 /* PBXTextBookmark */;
ED9AB3E610967730000B5852 /* PBXTextBookmark */ = ED9AB3E610967730000B5852 /* PBXTextBookmark */;
ED9AB3E710967730000B5852 /* PBXTextBookmark */ = ED9AB3E710967730000B5852 /* PBXTextBookmark */;
ED9AB3E910967730000B5852 /* PBXTextBookmark */ = ED9AB3E910967730000B5852 /* PBXTextBookmark */;
ED9AB3EC10967730000B5852 /* PBXTextBookmark */ = ED9AB3EC10967730000B5852 /* PBXTextBookmark */;
ED9AB3ED10967730000B5852 /* PBXTextBookmark */ = ED9AB3ED10967730000B5852 /* PBXTextBookmark */;
ED9AB3EF10967730000B5852 /* PBXTextBookmark */ = ED9AB3EF10967730000B5852 /* PBXTextBookmark */;
ED9AB3F210967730000B5852 /* PBXTextBookmark */ = ED9AB3F210967730000B5852 /* PBXTextBookmark */;
ED9AB3F310967730000B5852 /* PBXTextBookmark */ = ED9AB3F310967730000B5852 /* PBXTextBookmark */;
ED9BABDF1083850C00166CDA /* PlistBookmark */ = ED9BABDF1083850C00166CDA /* PlistBookmark */;
ED9BABE11083850C00166CDA /* PlistBookmark */ = ED9BABE11083850C00166CDA /* PlistBookmark */;
ED9BABE21083850C00166CDA /* PlistBookmark */ = ED9BABE21083850C00166CDA /* PlistBookmark */;
EDAFC7F4109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F4109A575F002C3487 /* PBXTextBookmark */;
EDAFC7F5109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F5109A575F002C3487 /* PBXTextBookmark */;
EDAFC7F7109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F7109A575F002C3487 /* PBXTextBookmark */;
EDAFC7F8109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F8109A575F002C3487 /* PBXTextBookmark */;
EDAFC7F9109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F9109A575F002C3487 /* PBXTextBookmark */;
EDAFC818109A5B94002C3487 /* PBXTextBookmark */ = EDAFC818109A5B94002C3487 /* PBXTextBookmark */;
EDAFC819109A5B94002C3487 /* PBXTextBookmark */ = EDAFC819109A5B94002C3487 /* PBXTextBookmark */;
EDAFC81B109A5B94002C3487 /* PBXTextBookmark */ = EDAFC81B109A5B94002C3487 /* PBXTextBookmark */;
EDFDF9B310A2054E0071CB9B /* PBXTextBookmark */ = EDFDF9B310A2054E0071CB9B /* PBXTextBookmark */;
EDFDF9B410A2054E0071CB9B /* PBXTextBookmark */ = EDFDF9B410A2054E0071CB9B /* PBXTextBookmark */;
EDFDF9B610A2054E0071CB9B /* PBXTextBookmark */ = EDFDF9B610A2054E0071CB9B /* PBXTextBookmark */;
EDFDF9B710A2054E0071CB9B /* PBXTextBookmark */ = EDFDF9B710A2054E0071CB9B /* PBXTextBookmark */;
EDFDF9C110A205F50071CB9B /* PBXTextBookmark */ = EDFDF9C110A205F50071CB9B /* PBXTextBookmark */;
EDFDF9C310A205F50071CB9B /* PBXTextBookmark */ = EDFDF9C310A205F50071CB9B /* PBXTextBookmark */;
EDFDF9C410A205F50071CB9B /* PBXTextBookmark */ = EDFDF9C410A205F50071CB9B /* PBXTextBookmark */;
EDFDF9C810A2114E0071CB9B /* PBXTextBookmark */ = EDFDF9C810A2114E0071CB9B /* PBXTextBookmark */;
};
sourceControlManager = ED9BABAF108380C600166CDA /* Source Control */;
userBuildSettings = {
};
};
434669950F8D058400EA7D6D /* Doom_icon.png */ = {
uiCtxt = {
sepNavWindowFrame = "{{15, 250}, {800, 773}}";
};
};
43CF03090F56D5C200E4A23D /* iphone_loop.c */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {732, 24640}}";
sepNavSelRange = "{6159, 14}";
sepNavVisRange = "{5677, 1070}";
};
};
43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {720, 7154}}";
sepNavSelRange = "{8344, 0}";
sepNavVisRange = "{8241, 409}";
};
};
7229CC8E0F6B3363004123C5 /* doomiphone.h */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {662, 1512}}";
sepNavSelRange = "{2492, 0}";
sepNavVisRange = "{1199, 1295}";
};
};
72A55EEE1003A94300F788A5 /* iphone_start.c */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {662, 3066}}";
sepNavSelRange = "{0, 0}";
sepNavVisRange = "{0, 757}";
};
};
72A7E8F60F5F2063005B83C0 /* iphone_menus.c */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {662, 13314}}";
sepNavSelRange = "{5284, 13}";
sepNavVisRange = "{4701, 1236}";
};
};
72D50DBA0F8ED98000BB49E6 /* ipak.h */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {662, 2478}}";
sepNavSelRange = "{0, 0}";
sepNavVisRange = "{287, 1502}";
};
};
72D50DBB0F8ED98000BB49E6 /* ipak.c */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {864, 5586}}";
sepNavSelRange = "{578, 8}";
sepNavVisRange = "{1113, 1914}";
};
};
72E731EA0F97E68100E702CD /* iphone_sound.c */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {864, 4214}}";
sepNavSelRange = "{4675, 0}";
sepNavVisRange = "{3563, 1771}";
};
};
72E847CA0F94096C00AB3C99 /* SDL_opengl.h */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {870, 3024}}";
sepNavSelRange = "{0, 0}";
sepNavVisRange = "{0, 1327}";
};
};
72E8495E0F942B9300AB3C99 /* misc.c */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {742, 1218}}";
sepNavSelRange = "{1221, 0}";
sepNavVisRange = "{1132, 433}";
};
};
ED2D127F10838A65003A9380 /* PBXBookmark */ = {
isa = PBXBookmark;
fRef = 434669950F8D058400EA7D6D /* Doom_icon.png */;
};
ED2D128210838A65003A9380 /* PBXBookmark */ = {
isa = PBXBookmark;
fRef = 434669950F8D058400EA7D6D /* Doom_icon.png */;
};
ED9AB3CF10966E85000B5852 /* iphone_email.h */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {662, 621}}";
sepNavSelRange = "{319, 0}";
sepNavVisRange = "{0, 319}";
};
};
ED9AB3D010966E85000B5852 /* iphone_email.m */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {750, 3024}}";
sepNavSelRange = "{0, 0}";
sepNavVisRange = "{0, 1042}";
};
};
ED9AB3E410967730000B5852 /* PlistBookmark */ = {
isa = PlistBookmark;
fRef = 8D1107310486CEB800E47090 /* Info.plist */;
fallbackIsa = PBXBookmark;
isK = 0;
kPath = (
UIPrerenderedIcon,
);
name = /Users/greghodges/doom/code/iphone/Info.plist;
rLen = 0;
rLoc = 2147483647;
};
ED9AB3E510967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72A55EEE1003A94300F788A5 /* iphone_start.c */;
name = "iphone_start.c: 1";
rLen = 0;
rLoc = 0;
rType = 0;
vrLen = 757;
vrLoc = 0;
};
ED9AB3E610967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = ED9AB3CF10966E85000B5852 /* iphone_email.h */;
name = "iphone_email.h: 15";
rLen = 0;
rLoc = 319;
rType = 0;
vrLen = 319;
vrLoc = 0;
};
ED9AB3E710967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = ED9AB3D010966E85000B5852 /* iphone_email.m */;
name = "iphone_email.m: 1";
rLen = 0;
rLoc = 0;
rType = 0;
vrLen = 1042;
vrLoc = 0;
};
ED9AB3E910967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 7229CC8E0F6B3363004123C5 /* doomiphone.h */;
name = "doomiphone.h: 106";
rLen = 0;
rLoc = 2492;
rType = 0;
vrLen = 1295;
vrLoc = 1199;
};
ED9AB3EC10967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = ED9AB3CF10966E85000B5852 /* iphone_email.h */;
name = "iphone_email.h: 15";
rLen = 0;
rLoc = 319;
rType = 0;
vrLen = 319;
vrLoc = 0;
};
ED9AB3ED10967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72A55EEE1003A94300F788A5 /* iphone_start.c */;
name = "iphone_start.c: 1";
rLen = 0;
rLoc = 0;
rType = 0;
vrLen = 757;
vrLoc = 0;
};
ED9AB3EF10967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = ED9AB3D010966E85000B5852 /* iphone_email.m */;
name = "iphone_email.m: 1";
rLen = 0;
rLoc = 0;
rType = 0;
vrLen = 1042;
vrLoc = 0;
};
ED9AB3F210967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */;
name = "iphone_main.c: 334";
rLen = 0;
rLoc = 8790;
rType = 0;
vrLen = 1755;
vrLoc = 7979;
};
ED9AB3F310967730000B5852 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 7229CC8E0F6B3363004123C5 /* doomiphone.h */;
name = "doomiphone.h: 106";
rLen = 0;
rLoc = 2492;
rType = 0;
vrLen = 1295;
vrLoc = 1199;
};
ED9BAB90108380AC00166CDA /* Doom */ = {
isa = PBXExecutable;
activeArgIndices = (
);
argumentStrings = (
);
autoAttachOnCrash = 1;
breakpointsEnabled = 0;
configStateDict = {
};
customDataFormattersEnabled = 1;
debuggerPlugin = GDBDebugging;
disassemblyDisplayState = 0;
dylibVariantSuffix = "";
enableDebugStr = 1;
environmentEntries = (
);
executableSystemSymbolLevel = 0;
executableUserSymbolLevel = 0;
libgmallocEnabled = 0;
name = Doom;
savedGlobals = {
};
sourceDirectories = (
);
};
ED9BABAF108380C600166CDA /* Source Control */ = {
isa = PBXSourceControlManager;
fallbackIsa = XCSourceControlManager;
isSCMEnabled = 0;
scmConfiguration = {
};
};
ED9BABB0108380C600166CDA /* Code sense */ = {
isa = PBXCodeSenseManager;
indexTemplatePath = "";
};
ED9BABDF1083850C00166CDA /* PlistBookmark */ = {
isa = PlistBookmark;
fRef = 4364BF3E0F5CB25900F29317 /* dist.plist */;
fallbackIsa = PBXBookmark;
isK = 0;
kPath = (
);
name = /Users/greghodges/doom/code/iphone/dist.plist;
rLen = 0;
rLoc = 2147483647;
};
ED9BABE11083850C00166CDA /* PlistBookmark */ = {
isa = PlistBookmark;
fRef = 8D1107310486CEB800E47090 /* Info.plist */;
fallbackIsa = PBXBookmark;
isK = 0;
kPath = (
CFBundleIconFile,
);
name = /Users/greghodges/doom/code/iphone/Info.plist;
rLen = 0;
rLoc = 2147483647;
};
ED9BABE21083850C00166CDA /* PlistBookmark */ = {
isa = PlistBookmark;
fRef = 4364BF3E0F5CB25900F29317 /* dist.plist */;
fallbackIsa = PBXBookmark;
isK = 0;
kPath = (
);
name = /Users/greghodges/doom/code/iphone/dist.plist;
rLen = 0;
rLoc = 2147483647;
};
EDAFC7F4109A575F002C3487 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */;
name = "iphone_menus.c: 201";
rLen = 13;
rLoc = 5284;
rType = 0;
vrLen = 1236;
vrLoc = 4701;
};
EDAFC7F5109A575F002C3487 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 43CF03090F56D5C200E4A23D /* iphone_loop.c */;
name = "iphone_loop.c: 212";
rLen = 14;
rLoc = 6159;
rType = 0;
vrLen = 1070;
vrLoc = 5677;
};
EDAFC7F7109A575F002C3487 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72E8495E0F942B9300AB3C99 /* misc.c */;
name = "misc.c: 40";
rLen = 0;
rLoc = 768;
rType = 0;
vrLen = 807;
vrLoc = 0;
};
EDAFC7F8109A575F002C3487 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */;
name = "iphone_menus.c: 201";
rLen = 13;
rLoc = 5284;
rType = 0;
vrLen = 1236;
vrLoc = 4701;
};
EDAFC7F9109A575F002C3487 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 43CF03090F56D5C200E4A23D /* iphone_loop.c */;
name = "iphone_loop.c: 212";
rLen = 14;
rLoc = 6159;
rType = 0;
vrLen = 1070;
vrLoc = 5677;
};
EDAFC818109A5B94002C3487 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72D50DBB0F8ED98000BB49E6 /* ipak.c */;
name = "ipak.c: 28";
rLen = 7;
rLoc = 467;
rType = 0;
vrLen = 1190;
vrLoc = 359;
};
EDAFC819109A5B94002C3487 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */;
name = "iphone_main.c: 325";
rLen = 7;
rLoc = 8411;
rType = 0;
vrLen = 1755;
vrLoc = 7979;
};
EDAFC81B109A5B94002C3487 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72D50DBB0F8ED98000BB49E6 /* ipak.c */;
name = "ipak.c: 28";
rLen = 7;
rLoc = 467;
rType = 0;
vrLen = 1190;
vrLoc = 359;
};
EDFDF9B310A2054E0071CB9B /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72D50DBA0F8ED98000BB49E6 /* ipak.h */;
name = "ipak.h: 1";
rLen = 0;
rLoc = 0;
rType = 0;
vrLen = 1502;
vrLoc = 287;
};
EDFDF9B410A2054E0071CB9B /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72E8495E0F942B9300AB3C99 /* misc.c */;
name = "misc.c: 60";
rLen = 0;
rLoc = 1496;
rType = 0;
vrLen = 964;
vrLoc = 869;
};
EDFDF9B610A2054E0071CB9B /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72D50DBA0F8ED98000BB49E6 /* ipak.h */;
name = "ipak.h: 1";
rLen = 0;
rLoc = 0;
rType = 0;
vrLen = 1502;
vrLoc = 287;
};
EDFDF9B710A2054E0071CB9B /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72E8495E0F942B9300AB3C99 /* misc.c */;
name = "misc.c: 60";
rLen = 0;
rLoc = 1496;
rType = 0;
vrLen = 964;
vrLoc = 869;
};
EDFDF9C110A205F50071CB9B /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = EDFDF9C210A205F50071CB9B /* alc.h */;
name = "alc.h: 186";
rLen = 0;
rLoc = 3852;
rType = 0;
vrLen = 1365;
vrLoc = 3211;
};
EDFDF9C210A205F50071CB9B /* alc.h */ = {
isa = PBXFileReference;
name = alc.h;
path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/OpenAL.framework/Headers/alc.h;
sourceTree = "<absolute>";
};
EDFDF9C310A205F50071CB9B /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72E731EA0F97E68100E702CD /* iphone_sound.c */;
name = "iphone_sound.c: 160";
rLen = 16;
rLoc = 5037;
rType = 0;
vrLen = 1820;
vrLoc = 3514;
};
EDFDF9C410A205F50071CB9B /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = EDFDF9C510A205F50071CB9B /* alc.h */;
name = "alc.h: 186";
rLen = 0;
rLoc = 3852;
rType = 0;
vrLen = 1365;
vrLoc = 3211;
};
EDFDF9C510A205F50071CB9B /* alc.h */ = {
isa = PBXFileReference;
name = alc.h;
path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/OpenAL.framework/Headers/alc.h;
sourceTree = "<absolute>";
};
EDFDF9C810A2114E0071CB9B /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 72E731EA0F97E68100E702CD /* iphone_sound.c */;
name = "iphone_sound.c: 148";
rLen = 0;
rLoc = 4675;
rType = 0;
vrLen = 1771;
vrLoc = 3563;
};
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

+48
View File
@@ -0,0 +1,48 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
/*
This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
The view content is basically an EAGL surface you render your OpenGL scene into.
Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
*/
@interface EAGLView : UIView <UITextFieldDelegate> {
@public
UITextField *textField;
@private
/* The pixel dimensions of the backbuffer */
GLint backingWidth;
GLint backingHeight;
/* OpenGL names for the renderbuffer and framebuffers used to render to this view */
GLuint viewRenderbuffer, viewFramebuffer;
/* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */
GLuint depthRenderbuffer;
}
@end
+354
View File
@@ -0,0 +1,354 @@
//
// EAGLView.m
// Doom
//
// Created by Cass Everitt on 2/20/09.
// Copyright Id Software 2009. All rights reserved.
//
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import "EAGLView.h"
#import "doomAppDelegate.h"
#include "doomiphone.h"
#include <pthread.h>
EAGLView *eaglview;
EAGLContext *context;
@implementation EAGLView
// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}
CAEAGLLayer *eaglLayer;
//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {
self = [super initWithCoder:coder];
eaglview = self;
// allow multiple touch events
self.multipleTouchEnabled = true;
// Get the layer
eaglLayer = (CAEAGLLayer *)self.layer;
// set opaque so UIKit doesn't try to blend it over other layers
eaglLayer.opaque = YES;
// set it to no-backing-retained so it can do ast pageflips instead
// of update copies, and go to 565 bit depth for higher performance.
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO],
kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGB565,
/* kEAGLColorFormatRGBA8, */
kEAGLDrawablePropertyColorFormat,
nil];
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
assert( context );
if ( ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
}
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
return self;
}
- (void) handleTouches:(UIEvent*)event {
int touchCount = 0;
static int previousTouchCount;
static int touchRover;
int touchThisSequence[MAX_TOUCHES];
memset( touchThisSequence, 0, sizeof( touchThisSequence ) );
NSSet *touches = [event allTouches];
// printf( "count: %i\n", [touches count] );
// lock the game out temporarily
pthread_mutex_lock( &eventMutex );
for (UITouch *myTouch in touches)
{
CGPoint touchLocation = [myTouch locationInView:nil];
// handle landscape mode and flipping
int x, y;
if ( revLand->value ) {
x = touchLocation.y;
y = 319 - touchLocation.x;
} else {
x = 479 - touchLocation.y;
y = touchLocation.x;
}
// printf( "%i, %i\n", x, y );
touchCount++;
touch_t *t2;
// find which one it is closest to
int minDist = 64 * 64; // allow up to 64 unit moves to be drags
int minIndex = -1;
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
t2 = &sysTouches[i];
if ( !t2->down ) {
continue;
}
int dist = ( t2->x - x ) * ( t2->x - x ) + ( t2->y - y ) * ( t2->y - y );
if ( dist < minDist ) {
minDist = dist;
minIndex = i;
}
}
if ( minIndex != -1 ) {
// reuse a touch
sysTouches[minIndex].x = x;
sysTouches[minIndex].y = y;
if (myTouch.phase == UITouchPhaseEnded) {
// if this was released before the game got to see it,
// make it a special case
if ( sysTouches[minIndex].stateCount == 1 ) {
// leave it in the down state with a special count
sysTouches[minIndex].stateCount = -1;
// printf( "Tap release touch on a reuse\n" );
} else {
sysTouches[minIndex].down = false;
sysTouches[minIndex].stateCount = 1;
// printf( "Release touch on a reuse\n" );
}
} else {
if (myTouch.phase == UITouchPhaseBegan) {
sysTouches[minIndex].stateCount = 1;
sysTouches[minIndex].controlOwner = NULL;
// printf( "Begin touch on a reuse\n" );
} else {
// printf( "Drag touch on a reuse\n" );
}
sysTouches[minIndex].down = true;
}
touchThisSequence[minIndex] = true;
} else {
if ( myTouch.phase != UITouchPhaseBegan ) {
printf( "Non-local touch wasn't a begin\n" );
} else {
// allocate a new one
// grab the next rover spot
// don't just use first-not-down, because that might
// cause the release to be missed by the game code.
int i, j;
for ( j = 0 ; j < MAX_TOUCHES ; j++ ) {
i = touchRover;
t2 = &sysTouches[i];
touchRover = ( touchRover + 1 ) % MAX_TOUCHES;
if ( !t2->down ) {
break;
}
}
if ( j == MAX_TOUCHES ) {
printf( "MAX_TOUCHES, clearing everything!\n" );
memset( sysTouches, 0, sizeof( sysTouches ) );
continue;
}
// printf( "new touch down\n" );
t2->x = x;
t2->y = y;
t2->down = true;
t2->controlOwner = NULL;
t2->stateCount = 1;
touchThisSequence[i] = true;
}
}
}
// Change any active touches to released if they weren't
// in the touch set. This will happen if we forced a break because
// a "moved" event was so large that it was very likely a release and
// press of a different finger that happened to be in the same frame.
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
if ( sysTouches[i].down && !touchThisSequence[i] ) {
printf( "clearing touch %i\n", i );
sysTouches[i].down = false;
sysTouches[i].stateCount = 0;
touchCount--;
}
}
// toggle the console with four touches
if ( touchCount == 4 && previousTouchCount != 4 ) {
touchCount = 0; // won't get the releases, because the text field will eat them
if ( textField == nil ) {
// do this before starting the textField, which
// takes a long time
// iphoneActivateConsole();
textField = [UITextField alloc];
[textField initWithFrame:CGRectMake( 0, 0, 20, 20 ) ];
[self addSubview:textField];
[textField release];
textField.hidden = true;
textField.delegate = self;
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
[textField becomeFirstResponder];
} else {
}
}
// the game is free to copy the touches now
pthread_mutex_unlock( &eventMutex );
previousTouchCount = touchCount;
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
// printf( "touchesBegan\n" );
[self handleTouches:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// printf( "touchesMoved\n" );
[self handleTouches:event];
}
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
// printf( "touchesEnded\n" );
[self handleTouches:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
// printf( "touchesCancelled\n" );
[self handleTouches:event];
}
@end
@implementation EAGLView (UITextFieldDelegate)
char consoleCommand[1024];
- (BOOL)textFieldShouldReturn:(UITextField *)_textField
{
if ( eaglview->textField == nil ) {
return YES;
}
// we can't just execute this, because we are running in another
// thread, so fetch the line and the game will catch it next time
// around
// lock the game out temporarily
pthread_mutex_lock( &eventMutex );
const char *line = [ eaglview->textField.text UTF8String ];
strncpy( consoleCommand, line, sizeof(consoleCommand)-1 );
eaglview->textField.text = [ NSString stringWithUTF8String: "" ];
// put it away
[textField resignFirstResponder];
[textField removeFromSuperview];
textField = nil;
// lock the game out temporarily
pthread_mutex_unlock( &eventMutex );
return YES;
}
@end
const char * SysIPhoneGetConsoleTextField() {
if ( eaglview->textField == nil ) {
return "";
}
return [ eaglview->textField.text UTF8String ];
}
void SysIPhoneSetConsoleTextField( const char * str) {
assert( eaglview->textField != nil );
eaglview->textField.text = [ NSString stringWithUTF8String: str ];
}
void SysIPhoneSwapBuffers() {
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
void SysIPhoneOpenURL( const char *url ) {
Com_Printf( "OpenURL char *: %s\n", url );
NSString *nss = [NSString stringWithCString: url encoding: NSASCIIStringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: nss]];
}
void SysIPhoneSetUIKitOrientation( int isLandscapeRight ) {
if ( isLandscapeRight ) {
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;
} else {
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
}
}
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key></key>
<string></string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${PRODUCT_NAME}_icon.png</string>
<key>CFBundleIdentifier</key>
<string>${PROFILE_PREFIX}.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSMainNibFile</key>
<string>MainWindow</string>
<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeLeft</string>
<key>UIRequiresPersistentWiFi</key>
<true/>
<key>UIStatusBarHidden</key>
<true/>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</plist>
+233
View File
@@ -0,0 +1,233 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.03">
<data>
<int key="IBDocument.SystemTarget">528</int>
<string key="IBDocument.SystemVersion">9G55</string>
<string key="IBDocument.InterfaceBuilderVersion">677</string>
<string key="IBDocument.AppKitVersion">949.43</string>
<string key="IBDocument.HIToolboxVersion">353.00</string>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="8"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBProxyObject" id="841351856">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
</object>
<object class="IBProxyObject" id="191355593">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
</object>
<object class="IBUICustomObject" id="664661524"/>
<object class="IBUIWindow" id="380026005">
<reference key="NSNextResponder"/>
<int key="NSvFlags">1316</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIView" id="773737154">
<reference key="NSNextResponder" ref="380026005"/>
<int key="NSvFlags">1298</int>
<string key="NSFrameSize">{320, 480}</string>
<reference key="NSSuperview" ref="380026005"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
<object class="NSColorSpace" key="NSCustomColorSpace">
<int key="NSID">2</int>
</object>
</object>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
</object>
</object>
<object class="NSPSMatrix" key="NSFrameMatrix"/>
<string key="NSFrameSize">{320, 480}</string>
<reference key="NSSuperview"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEAA</bytes>
</object>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<bool key="IBUIVisibleAtLaunch">YES</bool>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="664661524"/>
</object>
<int key="connectionID">4</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">window</string>
<reference key="source" ref="664661524"/>
<reference key="destination" ref="380026005"/>
</object>
<int key="connectionID">5</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">glView</string>
<reference key="source" ref="664661524"/>
<reference key="destination" ref="773737154"/>
</object>
<int key="connectionID">9</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<object class="NSArray" key="object" id="957960031">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">2</int>
<reference key="object" ref="380026005"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="773737154"/>
</object>
<reference key="parent" ref="957960031"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="841351856"/>
<reference key="parent" ref="957960031"/>
<string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">3</int>
<reference key="object" ref="664661524"/>
<reference key="parent" ref="957960031"/>
<string key="objectName">Game App Delegate</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">8</int>
<reference key="object" ref="773737154"/>
<reference key="parent" ref="380026005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="191355593"/>
<reference key="parent" ref="957960031"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMutableArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.CustomClassName</string>
<string>-2.CustomClassName</string>
<string>2.IBAttributePlaceholdersKey</string>
<string>2.IBEditorWindowLastContentRect</string>
<string>2.IBPluginDependency</string>
<string>3.CustomClassName</string>
<string>3.IBPluginDependency</string>
<string>8.CustomClassName</string>
<string>8.IBPluginDependency</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>UIApplication</string>
<string>UIResponder</string>
<object class="NSMutableDictionary">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<string>{{500, 343}, {320, 480}}</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>gameAppDelegate</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>EAGLView</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">10</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">EAGLView</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">EAGLView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">gameAppDelegate</string>
<string key="superclassName">NSObject</string>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMutableArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>glView</string>
<string>window</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>EAGLView</string>
<string>UIWindow</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">doomAppDelegate.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.LastKnownRelativeProjectPath">doom.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
</data>
</archive>
File diff suppressed because it is too large Load Diff
+383
View File
@@ -0,0 +1,383 @@
/*
File: SoundEngine.h
Abstract: These functions play background music tracks, multiple sound effects,
and support stereo panning with a low-latency response.
Version: 1.7
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
("Apple") in consideration of your agreement to the following terms, and your
use, installation, modification or redistribution of this Apple software
constitutes acceptance of these terms. If you do not agree with these terms,
please do not use, install, modify or redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and subject
to these terms, Apple grants you a personal, non-exclusive license, under
Apple's copyrights in this original Apple software (the "Apple Software"), to
use, reproduce, modify and redistribute the Apple Software, with or without
modifications, in source and/or binary forms; provided that if you redistribute
the Apple Software in its entirety and without modifications, you must retain
this notice and the following text and disclaimers in all such redistributions
of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may be used
to endorse or promote products derived from the Apple Software without specific
prior written permission from Apple. Except as expressly stated in this notice,
no other rights or licenses, express or implied, are granted by Apple herein,
including but not limited to any patent rights that may be infringed by your
derivative works or by other works in which the Apple Software may be
incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2008 Apple Inc. All Rights Reserved.
*/
/*==================================================================================================
SoundEngine.h
==================================================================================================*/
#if !defined(__SoundEngine_h__)
#define __SoundEngine_h__
//==================================================================================================
// Includes
//==================================================================================================
// System Includes
#ifndef WIN32
#include <CoreAudio/CoreAudioTypes.h>
#include <AudioToolbox/AudioToolbox.h>
#else
typedef int OSStatus;
typedef int SInt32;
typedef unsigned int UInt32;
typedef float Float32;
typedef unsigned char Boolean;
#endif
#if defined(__cplusplus)
extern "C"
{
#endif
//==================================================================================================
// Sound Engine
//==================================================================================================
/*!
@enum SoundEngine error codes
@abstract These are the error codes returned from the SoundEngine API.
@constant kSoundEngineErrUnitialized
The SoundEngine has not been initialized. Use SoundEngine_Initialize().
@constant kSoundEngineErrInvalidID
The specified EffectID was not found. This can occur if the effect has not been loaded, or
if an unloaded is trying to be accessed.
@constant kSoundEngineErrFileNotFound
The specified file was not found.
@constant kSoundEngineErrInvalidFileFormat
The format of the file is invalid. Effect data must be little-endian 8 or 16 bit LPCM.
@constant kSoundEngineErrDeviceNotFound
The output device was not found.
*/
enum {
kSoundEngineErrUnitialized = 1,
kSoundEngineErrInvalidID = 2,
kSoundEngineErrFileNotFound = 3,
kSoundEngineErrInvalidFileFormat = 4,
kSoundEngineErrDeviceNotFound = 5
};
/*!
@function SoundEngine_Initialize
@abstract Initializes and sets up the sound engine. Calling after a previous initialize will
reset the state of the SoundEngine, with all previous effects and music tracks
erased. Note: This is not required, loading an effect or background track will
initialize as necessary.
@param inMixerOutputRate
A Float32 that represents the output sample rate of the mixer unit. Setting this to
0 will use the default rate (the sample rate of the device)
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_Initialize(Float32 inMixerOutputRate);
OSStatus SoundEngine_Reactivate();
/*!
@function SoundEngine_Teardown
@abstract Tearsdown the sound engine.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_Teardown();
/*!
@function SoundEngine_SetMasterVolume
@abstract Sets the overall volume of all sounds coming from the process
@param inValue
A Float32 that represents the level. The range is between 0.0 and 1.0 (inclusive).
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetMasterVolume(Float32 inValue);
/*!
@function SoundEngine_SetListenerPosition
@abstract Sets the position of the listener in the 3D space
@param inX
A Float32 that represents the listener's position along the X axis.
@param inY
A Float32 that represents the listener's position along the Y axis.
@param inZ
A Float32 that represents the listener's position along the Z axis.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetListenerPosition(Float32 inX, Float32 inY, Float32 inZ);
OSStatus SoundEngine_SetListenerDirection(Float32 inX, Float32 inY, Float32 inZ);
/*!
@function SoundEngine_SetListenerGain
@abstract Sets the gain of the listener. Must be >= 0.0
@param inValue
A Float32 that represents the listener's gain
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetListenerGain(Float32 inValue);
/*!
@function SoundEngine_LoadBackgroundMusicTrack
@abstract Tells the background music player which file to play
@param inPath
The absolute path to the file to play.
@param inAddToQueue
If true, file will be added to the current background music queue. If
false, queue will be cleared and only loop the specified file.
@param inLoadAtOnce
If true, file will be loaded completely into memory. If false, data will be
streamed from the file as needed. For games without large memory pressure and/or
small background music files, this can save memory access and improve power efficiency
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_LoadBackgroundMusicTrack(const char* inPath, Boolean inAddToQueue, Boolean inLoadAtOnce);
/*!
@function SoundEngine_UnloadBackgroundMusicTrack
@abstract Tells the background music player to release all resources and stop playing.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_UnloadBackgroundMusicTrack();
/*!
@function SoundEngine_StartBackgroundMusic
@abstract Tells the background music player to start playing.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_StartBackgroundMusic();
/*!
@function SoundEngine_StopBackgroundMusic
@abstract Tells the background music player to stop playing.
@param inAddToQueue
If true, playback will stop when all tracks have completed. If false, playback
will stop immediately.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_StopBackgroundMusic(Boolean inStopAtEnd);
/*!
@function SoundEngine_SetBackgroundMusicVolume
@abstract Sets the volume for the background music player
@param inValue
A Float32 that represents the level. The range is between 0.0 and 1.0 (inclusive).
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetBackgroundMusicVolume(Float32 inValue);
/*!
@function SoundEngine_GetBackgroundMusicVolume
@abstract Gets the volume for the background music player
@result A Float32 representing the background music player volume, or 0 if it's not enabled
*/
Float32 SoundEngine_GetBackgroundMusicVolume();
/*!
@function SoundEngine_LoadLoopingEffect
@abstract Loads a sound effect from a file and return an ID to refer to that effect. Note: The files
MUST all be in the same data format and sample rate
@param inLoopFilePath
The absolute path to the file to load. This is the file that will loop continually.
@param inAttackFilePath
The absolute path to the file to load. This will play once at the start of the effect.
Set to NULL if no attack is desired, looping file will play immediately.
@param inDecayFilePath
The absolute path to the file to load. This will play once after looping has been ended.
Triggered using SoundEngine_StopEffect with the inDoDecay set to true. Set to NULL if no
decay is desired, looping file will stop immediately.
@param outEffectID
A UInt32 ID that refers to the effect.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_LoadLoopingEffect(const char* inLoopFilePath, const char* inAttackFilePath, const char* inDecayFilePath, UInt32* outEffectID);
/*!
@function SoundEngine_LoadEffect
@abstract Loads a sound effect from a file and return an ID to refer to that effect.
@param inPath
The absolute path to the file to load.
@param outEffectID
A UInt32 ID that refers to the effect.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_LoadEffect(const char* inPath, UInt32* outEffectID);
/*!
@function SoundEngine_UnloadEffect
@abstract Releases all resources associated with the given effect ID
@param inEffectID
The ID of the effect to unload.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_UnloadEffect(UInt32 inEffectID);
/*!
@function SoundEngine_StartEffect
@abstract Starts playback of an effect
@param inEffectID
The ID of the effect to start.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_StartEffect(UInt32 inEffectID);
/*!
@function SoundEngine_StopEffect
@abstract Stops playback of an effect
@param inEffectID
The ID of the effect to stop.
@param inDoDecay
Whether to play the decay file or stop immmediately (this is ignored
for non-looping effects)
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_StopEffect(UInt32 inEffectID, Boolean inDoDecay);
/*!
@function SoundEngine_Vibrate
@abstract Tells the device to vibrate
*/
#if TARGET_OS_IPHONE
#define SoundEngine_Vibrate() AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
#endif
/*!
@function SoundEngine_SetEffectPitch
@abstract Applies pitch shifting to an effect
@param inEffectID
The ID of the effect to adjust
@param inValue
A Float32 that represents the pitch scalar, with 1.0 being unchanged. Must
be greater than 0.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetEffectPitch(UInt32 inEffectID, Float32 inValue);
/*!
@function SoundEngine_SetEffectVolume
@abstract Sets the volume for an effect
@param inEffectID
The ID of the effect to adjust
@param inValue
A Float32 that represents the level. The range is between 0.0 and 1.0 (inclusive).
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetEffectLevel(UInt32 inEffectID, Float32 inValue);
/*!
@function SoundEngine_SetEffectPosition
@abstract Tells the engine whether a given effect should loop when played or if it should
play through just once and stop.
@param inEffectID
The ID of the effect to adjust
@param inX
A Float32 that represents the effect's position along the X axis. Maximum distance
is 100000.0 (absolute, not per axis), reference distance (distance from which gain
begins to attenuate) is 1.0
@param inY
A Float32 that represents the effect's position along the Y axis. Maximum distance
is 100000.0 (absolute, not per axis), reference distance (distance from which gain
begins to attenuate) is 1.0
@param inZ
A Float32 that represents the effect's position along the Z axis. Maximum distance
is 100000.0 by default (absolute, not per axis), reference distance (distance from
which gain begins to attenuate) is 1.0
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetEffectPosition(UInt32 inEffectID, Float32 inX, Float32 inY, Float32 inZ);
/*!
@function SoundEngine_SetEffectsVolume
@abstract Sets the overall volume for the effects
@param inValue
A Float32 that represents the level. The range is between 0.0 and 1.0 (inclusive).
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetEffectsVolume(Float32 inValue);
/*!
@function SoundEngine_GetEffectsVolume
@abstract Gets the overall volume for the effects
@result A Float32 representing the effects volume, or 0 if it's disabled
*/
Float32 SoundEngine_GetEffectsVolume();
/*!
@function SoundEngine_SetMaxDistance
@abstract Sets the maximum distance for effect attenuation. Gain will attenuate between the
reference distance and the maximum distance, after which gain will be 0.0
@param inValue
A Float32 that represents the level. Must be greater than 0.0.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetMaxDistance(Float32 inValue);
/*!
@function SoundEngine_SetReferenceDistance
@abstract Sets the distance at which effect gain attenuation begins. It will attenuate between
the reference distance and the maximum distance. Sounds closer than the reference
distance will have no attenuation applied
@param inValue
A Float32 that represents the level. Must be greater than 0.0.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetReferenceDistance(Float32 inValue);
/*!
@function SoundEngine_SetEffectReferenceDistance
@abstract Sets the distance at which effect gain attenuation begins. It will attenuate between
the reference distance and the maximum distance. Sounds closer than the reference
distance will have no attenuation applied
@param inEffectId
The sound engine effect Id
@param inValue
A Float32 that represents the level. Must be greater than 0.0.
@result A OSStatus indicating success or failure.
*/
OSStatus SoundEngine_SetEffectReferenceDistance(UInt32 inEffectID, Float32 inValue);
#if defined(__cplusplus)
}
#endif
#endif
+123
View File
@@ -0,0 +1,123 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// generated by fontimg
//
// struct GlyphRect { unsigned short x0, y0, x1, y1; float xoff, yoff, xadvance; };
GlyphRect glyphRects[] = {
/* ' ' */ { 2, 2, 2, 2, 0.000000, 0.000000, 7.958042 },
/* '!' */ { 6, 2, 9, 22, 2.250000, -20.750000, 7.958042 },
/* '"' */ { 14, 2, 21, 9, 1.250000, -20.750000, 10.167832 },
/* '#' */ { 26, 2, 41, 23, 0.250000, -21.000000, 15.930070 },
/* '$' */ { 46, 2, 59, 27, 1.000000, -22.500000, 15.930070 },
/* '%' */ { 64, 2, 86, 24, 1.500000, -21.000000, 25.468531 },
/* '&' */ { 92, 2, 109, 23, 1.000000, -21.000000, 19.104895 },
/* ''' */ { 114, 2, 117, 9, 1.250000, -20.750000, 5.468532 },
/* '(' */ { 122, 2, 129, 29, 1.500000, -21.000000, 9.538462 },
/* ')' */ { 134, 2, 141, 29, 1.500000, -21.000000, 9.538462 },
/* '*' */ { 146, 2, 155, 11, 0.750000, -21.000000, 11.146853 },
/* '+' */ { 160, 2, 173, 15, 1.500000, -17.000000, 16.727272 },
/* ',' */ { 178, 2, 181, 9, 2.250000, -3.000000, 7.958042 },
/* '-' */ { 186, 2, 194, 4, 0.750000, -8.750000, 9.538462 },
/* '.' */ { 198, 2, 201, 5, 2.500000, -3.000000, 7.958042 },
/* '/' */ { 206, 2, 214, 23, 0.000000, -21.000000, 7.958042 },
/* '0' */ { 218, 2, 231, 23, 1.000000, -20.750000, 15.930070 },
/* '1' */ { 236, 2, 243, 22, 3.000000, -20.750000, 15.930070 },
/* '2' */ { 2, 34, 15, 54, 0.750000, -20.750000, 15.930070 },
/* '3' */ { 20, 34, 33, 55, 1.000000, -20.750000, 15.930070 },
/* '4' */ { 38, 34, 52, 54, 0.250000, -20.750000, 15.930070 },
/* '5' */ { 58, 34, 72, 54, 1.000000, -20.250000, 15.930070 },
/* '6' */ { 76, 34, 89, 55, 1.000000, -20.750000, 15.930070 },
/* '7' */ { 94, 34, 107, 54, 1.250000, -20.250000, 15.930070 },
/* '8' */ { 112, 34, 125, 55, 1.000000, -20.750000, 15.930070 },
/* '9' */ { 130, 34, 143, 55, 1.000000, -20.750000, 15.930070 },
/* ':' */ { 148, 34, 151, 49, 2.500000, -15.000000, 7.958042 },
/* ';' */ { 156, 34, 159, 53, 2.250000, -15.000000, 7.958042 },
/* '<' */ { 164, 34, 177, 48, 1.500000, -17.250000, 16.727272 },
/* '=' */ { 182, 34, 195, 42, 1.500000, -14.500000, 16.727272 },
/* '>' */ { 200, 34, 213, 48, 1.500000, -17.250000, 16.727272 },
/* '?' */ { 218, 34, 231, 55, 1.250000, -21.000000, 15.930070 },
/* '@' */ { 2, 60, 28, 87, 1.500000, -21.000000, 29.076923 },
/* 'A' */ { 34, 60, 53, 80, -0.250000, -20.750000, 19.104895 },
/* 'B' */ { 58, 60, 73, 80, 2.000000, -20.750000, 19.104895 },
/* 'C' */ { 78, 60, 96, 81, 1.250000, -21.000000, 20.685314 },
/* 'D' */ { 102, 60, 119, 80, 2.000000, -20.750000, 20.685314 },
/* 'E' */ { 124, 60, 139, 80, 2.250000, -20.750000, 19.104895 },
/* 'F' */ { 144, 60, 158, 80, 2.250000, -20.750000, 17.496504 },
/* 'G' */ { 162, 60, 181, 81, 1.500000, -21.000000, 22.279720 },
/* 'H' */ { 186, 60, 202, 80, 2.250000, -20.750000, 20.685314 },
/* 'I' */ { 208, 60, 211, 80, 2.500000, -20.750000, 7.958042 },
/* 'J' */ { 216, 60, 227, 81, 0.750000, -20.750000, 14.321678 },
/* 'K' */ { 232, 60, 249, 80, 2.000000, -20.750000, 19.104895 },
/* 'L' */ { 2, 92, 15, 112, 2.000000, -20.750000, 15.930070 },
/* 'M' */ { 20, 92, 39, 112, 2.000000, -20.750000, 23.860140 },
/* 'N' */ { 44, 92, 60, 112, 2.000000, -20.750000, 20.685314 },
/* 'O' */ { 66, 92, 85, 113, 1.250000, -21.000000, 22.279720 },
/* 'P' */ { 90, 92, 106, 112, 2.000000, -20.750000, 19.104895 },
/* 'Q' */ { 110, 92, 130, 114, 1.000000, -21.000000, 22.279720 },
/* 'R' */ { 136, 92, 154, 112, 2.250000, -20.750000, 20.685314 },
/* 'S' */ { 160, 92, 176, 113, 1.250000, -21.000000, 19.104895 },
/* 'T' */ { 182, 92, 198, 112, 0.500000, -20.750000, 17.496504 },
/* 'U' */ { 204, 92, 220, 113, 2.250000, -20.750000, 20.685314 },
/* 'V' */ { 226, 92, 245, 112, 0.000000, -20.750000, 19.104895 },
/* 'W' */ { 2, 120, 28, 140, 0.250000, -20.750000, 27.034966 },
/* 'X' */ { 34, 120, 53, 140, 0.000000, -20.750000, 19.104895 },
/* 'Y' */ { 58, 120, 77, 140, 0.000000, -20.750000, 19.104895 },
/* 'Z' */ { 82, 120, 98, 140, 0.500000, -20.750000, 17.496504 },
/* '[' */ { 104, 120, 109, 146, 1.750000, -20.750000, 7.958042 },
/* '\' */ { 114, 120, 122, 141, 0.000000, -21.000000, 7.958042 },
/* ']' */ { 126, 120, 131, 146, 0.500000, -20.750000, 7.958042 },
/* '^' */ { 136, 120, 148, 131, 0.750000, -21.000000, 13.440559 },
/* '_' */ { 152, 120, 169, 122, -0.500000, 3.750000, 15.930070 },
/* '`' */ { 174, 120, 179, 124, 1.000000, -20.750000, 9.538462 },
/* 'a' */ { 184, 120, 197, 135, 1.000000, -15.250000, 15.930070 },
/* 'b' */ { 202, 120, 215, 141, 1.750000, -20.750000, 15.930070 },
/* 'c' */ { 220, 120, 233, 135, 1.000000, -15.250000, 14.321678 },
/* 'd' */ { 238, 120, 251, 141, 0.750000, -20.750000, 15.930070 },
/* 'e' */ { 2, 152, 15, 167, 1.000000, -15.250000, 15.930070 },
/* 'f' */ { 20, 152, 28, 173, 0.250000, -21.000000, 7.958042 },
/* 'g' */ { 34, 152, 47, 173, 0.750000, -15.250000, 15.930070 },
/* 'h' */ { 52, 152, 64, 172, 1.750000, -20.750000, 15.930070 },
/* 'i' */ { 70, 152, 72, 172, 1.750000, -20.750000, 6.363636 },
/* 'j' */ { 78, 152, 84, 179, -1.500000, -20.750000, 6.363636 },
/* 'k' */ { 88, 152, 100, 172, 1.750000, -20.750000, 14.321678 },
/* 'l' */ { 106, 152, 108, 172, 1.750000, -20.750000, 6.363636 },
/* 'm' */ { 114, 152, 134, 167, 1.750000, -15.250000, 23.860140 },
/* 'n' */ { 140, 152, 152, 167, 1.750000, -15.250000, 15.930070 },
/* 'o' */ { 158, 152, 172, 167, 0.750000, -15.250000, 15.930070 },
/* 'p' */ { 178, 152, 191, 173, 1.750000, -15.250000, 15.930070 },
/* 'q' */ { 196, 152, 209, 173, 1.000000, -15.250000, 15.930070 },
/* 'r' */ { 214, 152, 222, 167, 1.750000, -15.250000, 9.538462 },
/* 's' */ { 228, 152, 240, 167, 0.750000, -15.250000, 14.321678 },
/* 't' */ { 246, 152, 253, 172, 0.500000, -20.250000, 7.958042 },
/* 'u' */ { 2, 184, 14, 199, 1.750000, -15.000000, 15.930070 },
/* 'v' */ { 20, 184, 33, 199, 0.250000, -15.000000, 14.321678 },
/* 'w' */ { 38, 184, 58, 199, 0.000000, -15.000000, 20.685314 },
/* 'x' */ { 64, 184, 78, 199, 0.000000, -15.000000, 14.321678 },
/* 'y' */ { 84, 184, 98, 205, 0.250000, -15.000000, 14.321678 },
/* 'z' */ { 102, 184, 115, 199, 0.500000, -15.000000, 14.321678 },
/* '{' */ { 120, 184, 128, 211, 0.750000, -21.000000, 9.566434 },
/* '|' */ { 134, 184, 136, 211, 2.500000, -21.000000, 7.440559 },
/* '}' */ { 142, 184, 150, 211, 0.500000, -21.000000, 9.566434 },
/* '~' */ { 156, 184, 170, 188, 1.000000, -12.500000, 16.727272 },
/* '' */ { 176, 184, 190, 202, 3.500000, -18.000000, 21.482517 }
};
+166
View File
@@ -0,0 +1,166 @@
/*
* cmd.c
* doom
*
* Created by John Carmack on 4/14/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
typedef struct cmd_function_s {
struct cmd_function_s *next;
const char *name;
int hashid;
xcommand_t function;
} cmd_function_t;
#define MAX_STRING_TOKENS 16
#define MAX_STRING_CHARS 1024
int cmd_argc;
char *cmd_argv[ MAX_STRING_TOKENS ];
cmd_function_t *cmd_functions; // possible commands to execute
int Cmd_Argc( void ) {
return cmd_argc;
}
const char *Cmd_Argv( int arg ) {
if( arg >= cmd_argc ) {
return "";
}
return cmd_argv[ arg ];
}
void Cmd_TokenizeString( const char *text ) {
static char *stringCopy;
// clear the args from the last string
// This better not be called recursively...
if ( stringCopy ) {
free( stringCopy );
stringCopy = NULL;
}
cmd_argc = 0;
if( ! text ) {
return;
}
stringCopy = strdup( text );
char *strval = stringCopy;
while( 1 ) {
char *start = strsep( &strval," \t\r\n");
if ( !start ) {
break;
}
if ( start[0] != 0 ) {
cmd_argv[cmd_argc] = start;
if ( ++cmd_argc == MAX_STRING_TOKENS ) {
break;
}
}
}
}
void Cmd_ListCommands_f() {
for( cmd_function_t *cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
Com_Printf( "%s\n", cmd->name );
}
}
void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
cmd_function_t *cmd;
int hashid;
hashid = HashString( cmd_name );
// fail if the command already exists
for( cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
if( hashid == cmd->hashid && !strcmp( cmd_name, cmd->name ) ) {
Com_Printf( "Cmd_AddCommand: \"%s\" already defined\n", cmd_name );
return;
}
}
cmd = malloc( sizeof( cmd_function_t ) );
cmd->name = cmd_name;
cmd->hashid = hashid;
cmd->function = function;
cmd->next = cmd_functions;
cmd_functions = cmd;
}
void Cmd_ExecuteString( const char *str ) {
int l = strlen( str );
if ( str[l-1] == '\n' ) {
char *stripped = alloca( l+1 );
strcpy( stripped, str );
str = stripped;
stripped[l-1] = 0;
}
Com_Printf( "%s\n", str );
Cmd_TokenizeString( str );
const char *arg0 = Cmd_Argv( 0 );
int hashid = HashString( arg0 );
// check commands first
for( cmd_function_t *cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
if( hashid == cmd->hashid && !strcmp( arg0, cmd->name ) ) {
cmd->function();
return;
}
}
// then check cvars
cvar_t *cvar = Cvar_FindVar( arg0 );
if ( cvar ) {
Cvar_Set( arg0, Cmd_Argv( 1 ) );
return;
}
Com_Printf( "Unknown command: %s\n", arg0 );
}
// execute each line of the config file
void Cmd_ExecuteFile( const char *fullPathName ) {
Com_Printf( "Executing command file '%s'\n", fullPathName );
FILE *f = fopen( fullPathName, "rb" );
if ( !f ) {
Com_Printf( "Failed to open.\n" );
return;
}
char line[1024];
while( fgets( line, sizeof( line ), f ) ) {
Cmd_ExecuteString( line );
}
fclose( f );
}
+374
View File
@@ -0,0 +1,374 @@
/*
Copyright (C) 2004 Michael Liebscher
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General License for more details.
You should have received a copy of the GNU General License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
cvar_t *cvar_vars;
/*
-----------------------------------------------------------------------------
Function: Cvar_FindVar -Return cvar;
Parameters: var_name -[in] Name of cvar to lookup.
Returns: NULL if cvar not found, otherwise returns the cvar.
Notes:
-----------------------------------------------------------------------------
*/
cvar_t *Cvar_FindVar( const char *var_name )
{
cvar_t *var;
int hashid;
hashid = HashString( var_name );
for( var = cvar_vars ; var ; var = var->next )
{
if( hashid == var->hashid && !strcasecmp( var_name, var->name ) )
{
return var;
}
}
return NULL;
}
/*
-----------------------------------------------------------------------------
Function: Cvar_VariableValue -Get value of cvar.
Parameters: var_name -[in] Name of cvar to get value.
Returns: 0 if not found, other the value of the cvar.
Notes:
-----------------------------------------------------------------------------
*/
float Cvar_VariableValue( const char *var_name )
{
cvar_t *var;
var = Cvar_FindVar( var_name );
if( ! var )
{
return 0;
}
return (float)atof( var->string );
}
/*
-----------------------------------------------------------------------------
Function: Cvar_VariableString -Get cvar variable as string.
Parameters: var_name -[in] Name of cvar to get value.
Returns: Blank string on error, otherwise value string.
Notes:
-----------------------------------------------------------------------------
*/
char *Cvar_VariableString( const char *var_name )
{
cvar_t *var;
var = Cvar_FindVar( var_name );
if( ! var )
{
return "";
}
return var->string;
}
/*
-----------------------------------------------------------------------------
Function: Cvar_CompleteVariable -Complete cvar string name.
Parameters: partial -[in] Partial name of string to look up.
Returns: NULL if partial string not found, otherwise the complete
string name.
Notes:
-----------------------------------------------------------------------------
*/
char *Cvar_CompleteVariable( const char *partial )
{
cvar_t *cvar;
size_t len;
len = strlen( partial );
if( ! len )
{
return NULL;
}
//
// Check partial match.
//
for( cvar = cvar_vars ; cvar ; cvar = cvar->next )
{
if( ! strncmp( partial, cvar->name, len ) )
{
return cvar->name;
}
}
return NULL;
}
/*
-----------------------------------------------------------------------------
Function: Cvar_Get -Get cvar structure.
Parameters:
var_name -[in] the name of the cvar variable.
var_value -[in] string value of the cvar variable.
flags -[in] see CVARFlags for more information.
Returns: NULL on error, otherwise valid pointer to cvar_t structure.
Notes:
If the variable already exists, the value will not be set and
the flags will be or'ed.
-----------------------------------------------------------------------------
*/
cvar_t *Cvar_Get( const char *var_name, const char *var_value, CVARFlags flags ) {
cvar_t *var;
var = Cvar_FindVar( var_name );
if( var ) {
var->flags |= flags;
return var;
}
if( ! var_value ) {
return NULL;
}
var = malloc( sizeof( *var ) );
var->name = strdup( var_name );
var->string = strdup( var_value );
var->defaultString = strdup( var_value );
var->hashid = HashString( var_name );
var->modified = true;
var->value = (float)atof( var->string );
// link the variable in
var->next = cvar_vars;
cvar_vars = var;
var->flags = flags;
return var;
}
/*
-----------------------------------------------------------------------------
Function:
Parameters:
Returns:
Notes:
-----------------------------------------------------------------------------
*/
void Cvar_Set( const char *var_name, const char *value ) {
cvar_t *var;
var = Cvar_FindVar( var_name );
if( ! var ) {
Com_Printf( "Cvar '%s' doesn't exist\n", var_name );
return;
}
if( var->flags & CVAR_NOSET ) {
Com_Printf( "%s is write protected.\n", var_name );
return;
}
if( ! strcmp( value, var->string ) ) {
return; // not changed
}
var->modified = true;
free( var->string ); // free the old value string
var->string = strdup( value );
var->value = (float)atof( var->string );
}
/*
-----------------------------------------------------------------------------
Function:
Parameters:
Returns:
Notes:
-----------------------------------------------------------------------------
*/
void Cvar_SetValue( const char *var_name, float value )
{
char val[ 32 ];
if( value == (int)value )
{
snprintf( val, sizeof( val ), "%i", (int)value );
}
else
{
snprintf( val, sizeof( val ), "%f", value );
}
Cvar_Set( var_name, val );
}
/*
-----------------------------------------------------------------------------
Function: Cvar_Command -Handles variable inspection and changing from
the console.
Parameters: Nothing.
Returns: false if variable not found, otherwise true.
Notes:
-----------------------------------------------------------------------------
*/
boolean Cvar_Command( void )
{
cvar_t *v;
// check variables
v = Cvar_FindVar( Cmd_Argv( 0 ) );
if( ! v )
{
return false;
}
// perform a variable print or set
if( Cmd_Argc() == 1 )
{
Com_Printf( "\"%s\" is \"%s\"\n", v->name, v->string );
return true;
}
Cvar_Set( v->name, Cmd_Argv( 1 ) );
return true;
}
/*
-----------------------------------------------------------------------------
Function: Cvar_WriteVariables -Appends lines containing "set variable value"
for all variables with the archive flag set
to true.
Parameters:
Returns: Nothing.
Notes:
-----------------------------------------------------------------------------
*/
void Cvar_WriteVariables( const char *path )
{
cvar_t *var;
char buffer[1024];
FILE *f;
f = fopen( path, "a" );
for( var = cvar_vars ; var ; var = var->next )
{
if( var->flags & CVAR_ARCHIVE )
{
snprintf( buffer, sizeof( buffer ), "set %s %s\n", var->name, var->string );
fprintf( f, "%s", buffer );
}
}
fclose( f );
}
/*
-----------------------------------------------------------------------------
Function: Cvar_List_f -Print all cvars to the console.
Parameters: Nothing.
Returns: Nothing.
Notes:
-----------------------------------------------------------------------------
*/
void Cvar_List_f( void )
{
cvar_t *var;
int i;
i = 0;
for( var = cvar_vars ; var ; var = var->next, ++i )
{
if( var->flags & CVAR_ARCHIVE )
{
Com_Printf ("*");
}
else
{
Com_Printf (" ");
}
Com_Printf (" %s \"%s\"\n", var->name, var->string);
}
Com_Printf ("%i cvars\n", i);
}
void Cvar_Reset_f( void ) {
for( cvar_t *var = cvar_vars ; var ; var = var->next ) {
Cvar_Set( var->name, var->defaultString );
}
}
+130
View File
@@ -0,0 +1,130 @@
/*
Copyright (C) 2004 Michael Liebscher
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* cvar.h: Dynamic variable tracking.
*
* Author: Michael Liebscher <johnnycanuck@users.sourceforge.net>
* Date: 2004
*
* Acknowledgement:
* This code was derived from Quake II, and was originally
* written by Id Software, Inc.
*
*/
/*
Notes:
Dynamic variable tracking.
cvar_t variables are used to hold scalar or string variables
that can be changed or displayed at the console or prog code
as well as accessed directly in C code.
The user can access cvars from the console in three ways:
r_draworder -prints the current value
r_draworder 0 -sets the current value to 0
set r_draworder 0 -as above, but creates the cvar if not present
Cvars are restricted from having the same names as commands to keep this
module from being ambiguous.
This module is implemented by cvar.c
*/
#ifndef __CVAR_H__
#define __CVAR_H__
typedef enum _CVARFlags
{
CVAR_INIT = 0x0, // Just create it with no flag value.
CVAR_ARCHIVE = 0x1, // Set to cause it to be saved to the config file
CVAR_NOSET = 0x8, // Don't allow change from console at all,
// but can be set from the command line.
} CVARFlags;
// nothing outside the Cvar_*() functions should modify these fields!
typedef struct cvar_s {
// By putting the value first, cvars can be referenced in other code
// as just extern float *name, without having to include the cvar_t
// declaration. This is probably a bad idea that I will
// regret at some point.
float value;
char *name;
char *string;
char *defaultString;
int hashid;
int flags;
boolean modified; // set each time the cvar is changed
struct cvar_s *next;
} cvar_t;
extern cvar_t *cvar_vars;
void Cvar_List_f();
void Cvar_Reset_f();
extern cvar_t *Cvar_Get( const char *var_name, const char *value, CVARFlags flags );
// creates the variable if it doesn't exist, or returns the existing one
// if it exists, the value will not be changed, but flags will be ORed in
// that allows variables to be unarchived without needing bitflags
extern cvar_t *Cvar_FindVar( const char *var_name );
// returns NULL if it doesn't exist
extern void Cvar_Set( const char *var_name, const char *value );
// prints warning if it doesn't exist
extern void Cvar_SetValue( const char *var_name, float value );
// expands value to a string and calls Cvar_Set
extern float Cvar_VariableValue( const char *var_name );
// returns 0 if not defined or non numeric
extern char *Cvar_VariableString( const char *var_name );
// returns an empty string if not defined
extern char *Cvar_CompleteVariable( const char *partial );
// attempts to match a partial variable name for command line completion
// returns NULL if nothing fits
extern void Cvar_GetLatchedVars( void );
// any CVAR_LATCHED variables that have been set will now take effect
extern void Cvar_WriteVariables( const char *path );
// appends lines containing "set variable value" for all variables
// with the archive flag set to true.
extern void Cvar_Init( void );
extern char *Cvar_Userinfo( void );
// returns an info string containing all the CVAR_USERINFO cvars
extern char *Cvar_Serverinfo( void );
// returns an info string containing all the CVAR_SERVERINFO cvars
#endif /* __CVAR_H__ */
Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>get-task-allow</key>
<false/>
</dict>
</plist>
+39
View File
@@ -0,0 +1,39 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import <UIKit/UIKit.h>
#import <UIKit/UIAccelerometer.h>
@class EAGLView;
@interface gameAppDelegate : NSObject <UIApplicationDelegate, UIAccelerometerDelegate> {
UIWindow *window;
EAGLView *glView;
int lastAccelUpdateMsec;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet EAGLView *glView;
- (void)restartAccelerometerIfNeeded;
@end
+234
View File
@@ -0,0 +1,234 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import "doomAppDelegate.h"
#import "EAGLView.h"
#import <AudioToolbox/AudioServices.h>
#include "../doomiphone.h"
@interface UIApplication (Private)
- (void)setSystemVolumeHUDEnabled:(BOOL)enabled forAudioCategory:(NSString *)category;
- (void)setSystemVolumeHUDEnabled:(BOOL)enabled;
- (void)runFrame;
- (void)asyncTic;
@end
char iphoneDocDirectory[1024];
char iphoneAppDirectory[1024];
@implementation gameAppDelegate
@synthesize window;
@synthesize glView;
extern EAGLContext *context;
NSTimer *animationTimer;
touch_t sysTouches[MAX_TOUCHES];
touch_t gameTouches[MAX_TOUCHES];
pthread_mutex_t eventMutex; // used to sync between game and event threads
pthread_t gameThreadHandle;
volatile boolean startupCompleted;
void *GameThread( void *args ) {
if ( ![EAGLContext setCurrentContext:context]) {
printf( "Couldn't setCurrentContext for game thread\n" );
exit( 1 );
}
printf( "original game thread priority: %f\n", (float)[NSThread threadPriority] );
[NSThread setThreadPriority: 0.5];
printf( "new game thread priority: %f\n", (float)[NSThread threadPriority] );
iphoneStartup();
// make sure one frame has been run before setting
// startupCompleted, so we don't get one grey frame
iphoneFrame();
startupCompleted = TRUE; // OK to start touch / accel callbacks
while( 1 ) {
iphoneFrame();
}
}
- (void)asyncTic {
iphoneAsyncTic();
[ self restartAccelerometerIfNeeded];
}
- (void)runFrame {
iphoneFrame();
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
application.statusBarHidden = YES;
application.statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
// get the documents directory, where we will write configs and save games
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[documentsDirectory getCString: iphoneDocDirectory
maxLength: sizeof( iphoneDocDirectory ) - 1
encoding: NSASCIIStringEncoding ];
// get the app directory, where our data files live
// this gives something like:
// /var/mobile/Applications/71355F9F-6400-4267-B07D-E7980764F5A8/Applications
// when what we want is:
// /var/mobile/Applications/71355F9F-6400-4267-B07D-E7980764F5A8/doom.app
// so we get that in main() from argv[0]
#if 0
paths = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSUserDomainMask, YES);
NSString *appDirectory = [paths objectAtIndex:0];
static char iphoneAppDirectoryFromAPI[1024];
[appDirectory getCString: iphoneAppDirectoryFromAPI
maxLength: sizeof( iphoneAppDirectoryFromAPI ) - 1
encoding: NSASCIIStringEncoding ];
#endif
// disable screen dimming
[UIApplication sharedApplication].idleTimerDisabled = YES;
// start the flow of accelerometer events
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.delegate = self;
accelerometer.updateInterval = 0.01;
// use this mutex for coordinating touch handling between
// the run loop thread and the game thread
if ( pthread_mutex_init( &eventMutex, NULL ) == -1 ) {
perror( "pthread_mutex_init" );
}
// use this semaphore for signaling from the async cmd generation thread that
// the game / draw thread can wake up
// sem_init is unimplemented on iPhone
//if ( sem_init( &ticSemaphore, 0, 0 ) == -1 ) {
// perror( "sem_init" );
//}
ticSemaphore = sem_open( "ticSemaphore", O_CREAT, S_IRWXU, 0 );
if ( ticSemaphore == SEM_FAILED ) {
perror( "sem_open" );
}
// we want the main (event/async) thread to be as high a priority as possible
// so the game/render thread will be interrupted immediately.
// It looks like the default scheduling on iPhone is already what we want --
// the main thread is at 1.0, and new threads are at 0.5.
printf( "original event thread priority: %f\n", (float)[NSThread threadPriority] );
[NSThread setThreadPriority: 1.0];
printf( "new event thread priority: %f\n", (float)[NSThread threadPriority] );
#ifdef USE_GAME_THREAD
// the game thread will do the init and start running frames
pthread_create( &gameThreadHandle, NULL, GameThread, NULL );
while( !startupCompleted ) {
// pause until all startup is completed and the game is
// ready to receive accel and touch calls
usleep( 1000 );
}
// schedule the time for async command generation in network games
float interval = 1.0 / 30.0f;
[NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(asyncTic)
userInfo:nil repeats:YES];
#else
// do all the game startup work
iphoneStartup();
// schedule the time for frame updates
float interval = 1.0 / 30.0f;
// float interval = 1.0 / 5.0f; // a low framerate is useful for some timing tests
animationTimer = [NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(runFrame)
userInfo:nil repeats:YES];
// run one frame manually to avoid a grey view swap
[self runFrame];
#endif
}
- (void)applicationWillResignActive:(UIApplication *)application {
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
}
- (void)applicationWillTerminate:(UIApplication *)application {
iphoneShutdown();
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
Com_Printf( "applicationDidReceiveMemoryWarning\n" );
}
- (void)dealloc {
[window release];
[glView release];
[super dealloc];
}
- (void)restartAccelerometerIfNeeded {
// I have no idea why this seems to happen sometimes...
if ( SysIphoneMilliseconds() - lastAccelUpdateMsec > 1000 ) {
static int count;
if ( ++count < 100 ) {
printf( "Restarting accelerometer updates.\n" );
}
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.delegate = self;
accelerometer.updateInterval = 0.01;
}
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
float acc[4];
acc[0] = acceleration.x;
acc[1] = acceleration.y;
acc[2] = acceleration.z;
acc[3] = acceleration.timestamp;
iphoneTiltEvent( acc );
lastAccelUpdateMsec = SysIphoneMilliseconds();
}
@end
+28
View File
@@ -0,0 +1,28 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//
// Prefix header for all source files of the 'doom' target in the 'doom' project
//
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#endif
+200
View File
@@ -0,0 +1,200 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "doomiphone.h"
//int registration_sequence;
//#include "iphone_gl.h"
void GLCheckError(const char *message) {
GLint err = glGetError();
if ( err != GL_NO_ERROR ) {
printf( "GL ERROR %d from %s\n", err, message );
}
}
unsigned int QGLBeginStarted = 0;
struct Vertex {
float xyz[3];
float st[2];
#ifdef VERTEX_COLOR
GLubyte c[4];
#endif
};
#define MAX_VERTS 16384
typedef struct Vertex Vertex;
Vertex immediate[ MAX_VERTS ];
Vertex vab;
short quad_indexes[MAX_VERTS * 3 / 2 ];
int curr_vertex;
GLenum curr_prim;
void SetImmediateModeGLVertexArrays() {
glVertexPointer( 3, GL_FLOAT, sizeof( Vertex ), immediate[ 0 ].xyz );
glEnableClientState( GL_VERTEX_ARRAY );
glTexCoordPointer( 2, GL_FLOAT, sizeof( Vertex ), immediate[ 0 ].st );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
#ifdef VERTEX_COLOR
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( Vertex ), immediate[ 0 ].c );
glEnableClientState( GL_COLOR_ARRAY );
#endif
}
void InitImmediateModeGL() {
for ( int i = 0; i < MAX_VERTS * 3 / 2; i+=6 ) {
int q = i / 6 * 4;
quad_indexes[ i + 0 ] = q + 0;
quad_indexes[ i + 1 ] = q + 1;
quad_indexes[ i + 2 ] = q + 2;
quad_indexes[ i + 3 ] = q + 0;
quad_indexes[ i + 4 ] = q + 2;
quad_indexes[ i + 5 ] = q + 3;
}
SetImmediateModeGLVertexArrays();
}
void glBegin( GLenum prim ) {
curr_vertex = 0;
curr_prim = prim;
}
void glVertex3f( GLfloat x, GLfloat y, GLfloat z ) {
assert( curr_vertex < MAX_VERTS );
vab.xyz[ 0 ] = x;
vab.xyz[ 1 ] = y;
vab.xyz[ 2 ] = z;
immediate[ curr_vertex ] = vab;
curr_vertex++;
}
void glVertex3fv( GLfloat *xyz ) {
assert( curr_vertex < MAX_VERTS );
vab.xyz[ 0 ] = xyz[0];
vab.xyz[ 1 ] = xyz[1];
vab.xyz[ 2 ] = xyz[2];
immediate[ curr_vertex ] = vab;
curr_vertex++;
}
void glVertex2f( GLfloat x, GLfloat y ) {
assert( curr_vertex < MAX_VERTS );
vab.xyz[ 0 ] = (float)x;
vab.xyz[ 1 ] = (float)y;
vab.xyz[ 2 ] = 0.0f;
immediate[ curr_vertex ] = vab;
curr_vertex++;
}
void glVertex2i( GLint x, GLint y ) {
assert( curr_vertex < MAX_VERTS );
vab.xyz[ 0 ] = (float)x;
vab.xyz[ 1 ] = (float)y;
vab.xyz[ 2 ] = 0.0f;
immediate[ curr_vertex ] = vab;
curr_vertex++;
}
#ifdef VERTEX_COLOR
void glColor4ub( GLubyte r, GLubyte g, GLubyte b, GLubyte a ) {
vab.c[ 0 ] = r;
vab.c[ 1 ] = g;
vab.c[ 2 ] = b;
vab.c[ 3 ] = a;
}
void glColor4ubv( GLubyte *rgba ) {
vab.c[ 0 ] = rgba[0];
vab.c[ 1 ] = rgba[1];
vab.c[ 2 ] = rgba[2];
vab.c[ 3 ] = rgba[3];
}
void glColor4f( GLfloat r, GLfloat g, GLfloat b, GLfloat a ) {
vab.c[ 0 ] = (GLubyte) ( r * 255 );
vab.c[ 1 ] = (GLubyte) ( g * 255 );
vab.c[ 2 ] = (GLubyte) ( b * 255 );
vab.c[ 3 ] = (GLubyte) ( a * 255 );
}
void glColor4fv( GLfloat *rgba ) {
vab.c[ 0 ] = (GLubyte) ( rgba[0] * 255 );
vab.c[ 1 ] = (GLubyte) ( rgba[1] * 255 );
vab.c[ 2 ] = (GLubyte) ( rgba[2] * 255 );
vab.c[ 3 ] = (GLubyte) ( rgba[3] * 255 );
}
void glColor3f( GLfloat r, GLfloat g, GLfloat b ) {
vab.c[ 0 ] = (GLubyte) ( r * 255 );
vab.c[ 1 ] = (GLubyte) ( g * 255 );
vab.c[ 2 ] = (GLubyte) ( b * 255 );
vab.c[ 3 ] = 255;
}
#endif
void glTexCoord2i( GLint s, GLint t ) {
vab.st[ 0 ] = (float)s;
vab.st[ 1 ] = (float)t;
}
void glTexCoord2f( GLfloat s, GLfloat t ) {
vab.st[ 0 ] = s;
vab.st[ 1 ] = t;
}
void glTexCoord2fv( GLfloat *st ) {
vab.st[ 0 ] = st[0];
vab.st[ 1 ] = st[1];
}
void glEnd() {
#if 0
glVertexPointer( 3, GL_FLOAT, sizeof( Vertex ), immediate[ 0 ].xyz );
glTexCoordPointer( 2, GL_FLOAT, sizeof( Vertex ), immediate[ 0 ].st );
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( Vertex ), immediate[ 0 ].c );
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
#endif
if ( curr_prim == GL_QUADS ) {
glDrawElements( GL_TRIANGLES, curr_vertex / 4 * 6, GL_UNSIGNED_SHORT, quad_indexes );
} else {
glDrawArrays( curr_prim, 0, curr_vertex );
}
curr_vertex = 0;
curr_prim = 0;
}
void landscapeViewport( GLint x, GLint y, GLsizei width, GLsizei height ) {
y = 0; // !@#
if ( revLand->value ) {
glViewport( VID_HEIGHT - (y+height), x, height, width );
} else {
glViewport( y, x, height, width );
}
}
void landscapeScissor( GLint x, GLint y, GLsizei width, GLsizei height ) {
y = 0; // !@#
if ( revLand->value ) {
glScissor( VID_HEIGHT - (y+height), x, height, width );
} else {
glScissor( y, x, height, width );
}
}
+41
View File
@@ -0,0 +1,41 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __GLES_GLUE_H__
#define __GLES_GLUE_H__
#define GL_QUADS 888
void GLCheckError( const char *message );
void glBegin( GLenum prim );
void glVertex3f( float x, float y, float z );
void glVertex2i( GLint x, GLint y );
void glColor4ub( GLubyte r, GLubyte g, GLubyte b, GLubyte a );
void glColor4ubv( GLubyte *rgba );
void glColor3f( GLfloat r, GLfloat g, GLfloat b );
void glColor4f( GLfloat r, GLfloat g, GLfloat b, GLfloat a );
void glColor4fv( GLfloat *rgba );
void glTexCoord2i( GLint s, GLint t );
void glTexCoord2f( GLfloat s, GLfloat t );
void glEnd();
#endif
+215
View File
@@ -0,0 +1,215 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
hud_t huds;
void HudDraw();
void HudWrite();
void HudRead();
ibutton_t *dragHud;
int dragX, dragY;
void SetHudPic( ibutton_t *hp, const char *image ) {
pkTexture_t *gl;
gl = PK_FindTexture( image );
assert( gl );
hp->texture = gl;
hp->touch = NULL; // in case one was down when it was saved
}
void SetHudSpot( ibutton_t *hp, int x, int y, int dw, int dh ) {
hp->touch = NULL; // in case one was down when it was saved
hp->x = x - dw/2;
hp->y = y - dh/2;
hp->drawWidth = dw;
hp->drawHeight = dh;
hp->buttonFlags = 0;
hp->scale = 1.0f;
}
void HudSetTexnums() {
SetHudPic( &huds.forwardStick, "iphone/up_down.tga" );
SetHudPic( &huds.sideStick, "iphone/side_2_side.tga" );
SetHudPic( &huds.turnStick, "iphone/directional_2.tga" );
SetHudPic( &huds.turnRotor, "iphone/rotate.tga" );
SetHudPic( &huds.fire, "iphone/fire.tga" );
SetHudPic( &huds.menu, "iphone/menu_button.tga" );
SetHudPic( &huds.map, "iphone/map_button.tga" );
SetHudSpot( &huds.weaponSelect, 240, 280, 40, 40 );
}
void HudSetForScheme( int schemeNum ) {
for ( ibutton_t *hud = (ibutton_t *)&huds ; hud != (ibutton_t *)(&huds+1) ; hud++ ) {
hud->buttonFlags = BF_IGNORE;
}
static const int STICK_SIZE = 128;
static const int HALF_STICK = 128/2;
static const int BOTTOM = 320 - 44; // above the status bar
SetHudSpot( &huds.weaponSelect, 240, 280, 40, 40 ); // the touch area is doubled
// make the forward / back sticks touch taller than they draw
switch ( schemeNum ) {
default:
case 0: // turn stick
SetHudSpot( &huds.forwardStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
SetHudSpot( &huds.turnStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
SetHudSpot( &huds.fire, 480-40, BOTTOM-HALF_STICK, 80, 80 );
SetHudSpot( &huds.menu, 480-24, 24, 48, 48 );
SetHudSpot( &huds.map, 24, 24, 48, 48 );
break;
case 1: // dual stick
SetHudSpot( &huds.forwardStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
SetHudSpot( &huds.sideStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
SetHudSpot( &huds.turnStick, 480-HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
SetHudSpot( &huds.fire, 480-40, 40, 80, 80 );
SetHudSpot( &huds.menu, 48+24, 24, 48, 48 );
SetHudSpot( &huds.map, 24, 24, 48, 48 );
break;
case 2: // rotor
SetHudSpot( &huds.forwardStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
SetHudSpot( &huds.sideStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
SetHudSpot( &huds.turnRotor, 480-HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
SetHudSpot( &huds.fire, 480-40, 40, 80, 80 );
SetHudSpot( &huds.menu, 48+24, 24, 48, 48 );
SetHudSpot( &huds.map, 24, 24, 48, 48 );
break;
}
// don't process these in the update hud touch loop, because they will be
// handled with normal button calls
huds.menu.buttonFlags |= BF_HUDBUTTON;
huds.map.buttonFlags |= BF_HUDBUTTON;
// don't make the big button click sound for the fire button
huds.fire.buttonFlags |= BF_SMALL_CLICK;
}
void SnapSticks( ibutton_t *test, const ibutton_t *to ) {
if ( abs( test->x - to->x ) < test->drawWidth && abs( test->y - to->y ) < test->drawHeight ) {
test->x = to->x;
test->y = to->y;
}
}
/*
==================
HudEditFrame
==================
*/
void HudEditFrame() {
color3_t gray = { 32, 32, 32 };
if ( numTouches == 0 && numPrevTouches == 1 && dragHud ) {
Sound_StartLocalSound( "iphone/baction_01.wav" );
dragHud = NULL;
}
if ( numTouches == 1 && numPrevTouches == 0 ) {
// identify the hud being touched for drag
int x = touches[0][0];
int y = touches[0][1];
dragHud = NULL;
for ( ibutton_t *hud = (ibutton_t *)&huds ; hud != (ibutton_t *)(&huds+1) ; hud++ ) {
if ( hud->buttonFlags & BF_IGNORE ) {
continue;
}
if ( x >= hud->x && x - hud->x < hud->drawWidth && y >= hud->y && y - hud->y < hud->drawHeight ) {
dragHud = hud;
dragX = dragHud->x - x;
dragY = dragHud->y - y;
Sound_StartLocalSound( "iphone/bdown_01.wav" );
break;
}
}
}
if ( numTouches == 1 && numPrevTouches == 1 && dragHud ) {
// adjust the position of the dragHud
dragHud->x = touches[0][0] + dragX;
dragHud->y = touches[0][1] + dragY;
if ( dragHud->x < 0 ) {
dragHud->x = 0;
}
if ( dragHud->x > 480 - dragHud->drawWidth ) {
dragHud->x = 480 - dragHud->drawWidth;
}
if ( dragHud->y < 0 ) {
dragHud->y = 0;
}
if ( dragHud->y > 320 - dragHud->drawHeight ) {
dragHud->y = 320 - dragHud->drawHeight;
}
// magnet pull a matchable axis
if ( controlScheme->value == 0 ) {
if ( dragHud == &huds.forwardStick ) {
SnapSticks( &huds.turnStick, dragHud );
}
} else {
if ( dragHud == &huds.forwardStick ) {
SnapSticks( &huds.sideStick, dragHud );
}
}
}
// solid background color and some UI elements for context
R_Draw_Fill( 0, 0, 480, 320, gray );
glColor4f( 1, 1, 1, 1 );
iphoneCenterText( 240, 20, 0.75, "Drag the controls" );
// draw the status bar
extern patchnum_t stbarbg;
if ( statusBar->value ) {
// force doom to rebind, since we have changed the active GL_TEXTURE_2D
last_gltexture = NULL;
gld_DrawNumPatch(0, ST_Y, stbarbg.lumpnum, CR_DEFAULT, VPT_STRETCH);
}
// draw the active items at their current locations
for ( ibutton_t *hud = (ibutton_t *)&huds ; hud != (ibutton_t *)(&huds+1) ; hud++ ) {
if ( !hud->texture ) {
continue;
}
if ( hud->buttonFlags & BF_IGNORE ) {
continue;
}
PK_StretchTexture( hud->texture, hud->x, hud->y, hud->drawWidth, hud->drawHeight );
}
// draw the done button
static ibutton_t btnDone;
if ( !btnDone.texture ) {
// initial setup
SetButtonPicsAndSizes( &btnDone, "iphone/back_button.tga", "Done", 240 - 32, 160-32, 64, 64 );
}
if ( HandleButton( &btnDone ) ) {
menuState = IPM_CONTROLS;
}
}
+418
View File
@@ -0,0 +1,418 @@
/*
* ipak.c
* doom
*
* Created by John Carmack on 4/9/09.
* Copyright 2009 Id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
pkHeader_t *pkHeader;
int pkSize;
// images and wavs have writable state, so they need separate
// structs that also point to the source in the pak file
pkTexture_t *pkTextures;
pkWav_t *pkWavs;
void PK_LoadTexture( pkTexture_t *image );
/*
==================
PK_Init
==================
*/
void PK_Init( const char *pakFileName ) {
printf( "PK_Init( %s )\n", pakFileName );
int fd = open( pakFileName, O_RDONLY );
if ( fd == -1 ) {
printf( "Couldn't open file\n" );
assert( 0 );
}
struct stat s;
fstat( fd, &s );
pkSize = s.st_size;
pkHeader = mmap( NULL, pkSize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0 );
// mmap keeps the file internally, we can close our descriptor
close( fd );
if ( (int)pkHeader == -1 ) {
printf( "mmap failed: %s\n", strerror( errno ) );
assert( 0 );
}
if ( pkHeader->version != PKFILE_VERSION ) {
printf( "bad pak file version: 0x%x != 0x%x\n", pkHeader->version, PKFILE_VERSION );
assert( 0 );
}
// build the local image table
pkTextures = malloc( sizeof( pkTextures[0] ) * pkHeader->textures.count );
memset( pkTextures, 0, sizeof( pkTextures[0] ) * pkHeader->textures.count );
for ( int i = 0 ; i < pkHeader->textures.count ; i++ ) {
pkTextures[i].textureData = (pkTextureData_t *)( (byte *)pkHeader + pkHeader->textures.tableOfs + i * pkHeader->textures.structSize );
}
// build the local wav table
int startLoadingWavs = SysIphoneMicroseconds();
pkWavs = malloc( sizeof( pkWavs[0] ) * pkHeader->wavs.count );
memset( pkWavs, 0, sizeof( pkWavs[0] ) * pkHeader->wavs.count );
for ( int i = 0 ; i < pkHeader->wavs.count ; i++ ) {
pkWav_t *sfx = &pkWavs[i];
sfx->wavData = (pkWavData_t *)( (byte *)pkHeader + pkHeader->wavs.tableOfs + i * pkHeader->wavs.structSize );
// there is no harm in setting the OpenAl static buffer up for everything now
alGenBuffers( 1, &sfx->alBufferNum );
int alFormat;
if ( sfx->wavData->wavChannels == 1 ) {
if ( sfx->wavData->wavChannelBytes == 1 ) {
alFormat = AL_FORMAT_MONO8;
} else {
alFormat = AL_FORMAT_MONO16;
}
} else {
if ( sfx->wavData->wavChannelBytes == 1 ) {
alFormat = AL_FORMAT_STEREO8;
} else {
alFormat = AL_FORMAT_STEREO16;
}
}
#if 0
alBufferData( sfx->alBufferNum, alFormat, (byte *)pkHeader + sfx->wavData->wavDataOfs
, sfx->wavData->wavChannels*sfx->wavData->wavChannelBytes*sfx->wavData->wavNumSamples
, sfx->wavData->wavRate );
#else
// This should just store out a pointer, so the data won't get touched until it is actually
// used to play a sound.
extern void alBufferDataStatic(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
alBufferDataStatic( sfx->alBufferNum, alFormat, (byte *)pkHeader + sfx->wavData->wavDataOfs
, sfx->wavData->wavChannels*sfx->wavData->wavChannelBytes*sfx->wavData->wavNumSamples
, sfx->wavData->wavRate );
#endif
}
int endLoadingWavs = SysIphoneMicroseconds();
printf( "%i usec to load wavs\n", endLoadingWavs - startLoadingWavs );
printf( "Mapped %i bytes of %s at 0x%p\n", pkSize, pakFileName, pkHeader );
printf( "%4i textures\n", pkHeader->textures.count );
printf( "%4i wavs\n", pkHeader->wavs.count );
printf( "%4i raws\n", pkHeader->raws.count );
#if 0
// testing
for ( int j = 0 ; j < 4 ; j++ ) {
int startTime = Sys_Microseconds();
int sum = 0;
for ( int i = 0 ; i < pkSize ; i+=16 ) {
sum += ((byte *)pkHeader)[i];
}
int endTime = Sys_Microseconds();
printf( "%5.1f mb/s page-in speed (%i)\n", (float)pkSize / (endTime - startTime ), endTime - startTime );
}
for ( int i = 0 ; i < pkHeader->numTextures ; i++ ) {
printf( "-------------------------\n" );
for ( int j = 0 ; j < 8 ; j++ ) {
pkTexture_t *tex = &pkTextures[i];
int start = Sys_Microseconds();
PK_LoadTexture( tex );
int middle = Sys_Microseconds();
PK_StretchTexture( tex, 0, 0, 0, 0 );
int middle2 = Sys_Microseconds();
PK_StretchTexture( tex, 0, 0, 0, 0 );
int end = Sys_Microseconds();
printf( "%i usec load, %i usec first draw, %i usec second draw\n",
middle - start, middle2 - middle, end - middle2 );
glDeleteTextures( 1, &tex->glTexNum );
tex->glTexNum = 0;
}
}
#endif
}
/*
==================
PK_LoadTexture
==================
*/
void PK_LoadTexture( pkTexture_t *tex ) {
int startTime = SysIphoneMicroseconds();
const pkTextureData_t *imd = tex->textureData;
glGenTextures( 1, &tex->glTexNum );
glBindTexture( GL_TEXTURE_2D, tex->glTexNum );
// load the image directly from the mapped file
typedef struct {
int internalFormat;
int externalFormat;
int type;
int bpp;
} formatInfo_t;
static formatInfo_t formatInfo[9] = {
{ GL_RGB , GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16 },
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16 },
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16 },
{ GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32 },
{ GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 16 },
{ GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 0, 0, 4 },
{ GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 0, 0, 4 },
{ GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, 0, 0, 2 },
{ GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, 0, 0, 2 },
};
assert( imd->format < 9 );
formatInfo_t *fi = &formatInfo[imd->format];
unsigned char *s = (byte *)pkHeader + imd->picDataOfs;
int w = imd->uploadWidth;
int h = imd->uploadHeight;
// upload each mip level
int l = 0;
int totalSize = 0;
while( 1 ) {
int size = (w*h*fi->bpp)/8;
if ( fi->type == 0 ) {
if ( size < 32 ) {
// minimum PVRTC size
size = 32;
}
glCompressedTexImage2D( GL_TEXTURE_2D, l, fi->internalFormat, w, h, 0,
size, s );
} else {
glTexImage2D( GL_TEXTURE_2D, l, fi->internalFormat, w, h, 0,
fi->externalFormat, fi->type, s );
}
GLCheckError( "texture upload" );
totalSize += size;
if ( ++l == imd->numLevels ) {
break;
}
if ( w == 1 && h == 1 ) {
break;
}
s += size;
w >>= 1;
if ( w == 0 ) {
w = 1;
}
h >>= 1;
if ( h == 0 ) {
h = 1;
}
}
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, imd->minFilter );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, imd->magFilter );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, imd->wrapS );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, imd->wrapT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0f );
int endTime = SysIphoneMicroseconds();
printf( "%5.1f mb/s TexImage for %s\n", (float)totalSize /
( endTime - startTime ), imd->name.name );
}
/*
==================
PK_FindTexture
Fully creates the gl texture before returning.
==================
*/
pkTexture_t *PK_FindTexture( const char *imageName ) {
int index;
pkTexture_t *texData = (pkTexture_t *)PK_FindType( imageName, &pkHeader->textures, &index );
if ( !texData ) {
return NULL;
}
pkTexture_t *tex = pkTextures + index;
if ( tex->glTexNum == 0 ) {
PK_LoadTexture( tex );
}
return tex;
}
/*
==================
PK_FindWav
==================
*/
pkWav_t *PK_FindWav( const char *soundName ) {
int index;
pkWavData_t *wavData = (pkWavData_t *)PK_FindType( soundName, &pkHeader->wavs, &index );
if ( !wavData ) {
return NULL;
}
pkWav_t *wav = pkWavs + index;
// create the OpenAL buffer
return wav;
}
/*
==================
PK_FindRaw
==================
*/
const byte *PK_FindRaw( const char *rawName, int *len ) {
pkRawData_t *raw = (pkRawData_t *)PK_FindType( rawName, &pkHeader->raws, NULL );
if ( !raw ) {
if ( len ) {
*len = -1;
}
return NULL;
}
if ( len ) {
*len = raw->rawDataLen;
}
return (byte *)pkHeader + raw->rawDataOfs;
}
/*
==================
PK_HashName
==================
*/
int PK_HashName( const char *name, char canonical[MAX_PK_NAME] ) {
int o = 0;
int hash = 0;
do {
int c = name[o];
if ( c == 0 ) {
break;
}
// backslashes to forward slashes
if ( c == '\\' ) {
c = '/';
}
// to lowercase
c = tolower( c );
canonical[o++] = c;
hash = (hash << 5) - hash + c;
} while ( o < MAX_PK_NAME-1 );
canonical[o] = 0;
return hash;
}
/*
==================
PK_FindType
==================
*/
const pkName_t *PK_FindType( const char *rawName, const pkType_t *type, int *indexOutput ) {
char canonicalName[MAX_PK_NAME];
int hash = PK_HashName( rawName, canonicalName );
int hashChain = hash & (PK_HASH_CHAINS-1);
int index = type->hashChains[hashChain];
while ( index != -1 ) {
assert( index >= 0 && index < type->count );
const pkName_t *name = (pkName_t *)((byte *)pkHeader + type->tableOfs + index * type->structSize );
if ( name->nameHash == hash && !strcmp( canonicalName, name->name ) ) {
// this is it
if ( indexOutput ) {
*indexOutput = index;
}
return name;
}
index = name->nextOnHashChain;
}
// not found
if ( indexOutput ) {
*indexOutput = -1;
}
return NULL;
}
/*
==================
PK_BindTexture
==================
*/
void PK_BindTexture( pkTexture_t *tex ) {
assert( tex->glTexNum );
glBindTexture( GL_TEXTURE_2D, tex->glTexNum );
}
/*
==================
PK_DrawTexture
==================
*/
void PK_DrawTexture( pkTexture_t *tex, int x, int y ) {
PK_BindTexture( tex );
int w = tex->textureData->srcWidth;
int h = tex->textureData->srcHeight;
glBegin( GL_QUADS );
glTexCoord2f( 0.0f, 0.0f ); glVertex2i( x, y );
glTexCoord2f( tex->textureData->maxS, 0.0f ); glVertex2i( x+w, y );
glTexCoord2f( tex->textureData->maxS, tex->textureData->maxT ); glVertex2i( x+w, y+h );
glTexCoord2f( 0.0f, tex->textureData->maxT ); glVertex2i( x, y+h );
glEnd();
}
void PK_StretchTexture( pkTexture_t *tex, float x, float y, float w, float h ) {
PK_BindTexture( tex );
glBegin( GL_QUADS );
glTexCoord2f( 0.0f, 0.0f ); glVertex2i( x, y );
glTexCoord2f( tex->textureData->maxS, 0.0f ); glVertex2i( x+w, y );
glTexCoord2f( tex->textureData->maxS, tex->textureData->maxT ); glVertex2i( x+w, y+h );
glTexCoord2f( 0.0f, tex->textureData->maxT ); glVertex2i( x, y+h );
glEnd();
}
+180
View File
@@ -0,0 +1,180 @@
/*
* ipak.h
* General purpose data file management intended to be used
* as a read-only memory mapped file to play nice with iPhone OS's
* non-swapping and variable memory management.
*
* Created by John Carmack on 4/9/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//============================================================
//
// In-file structures
//
// These stuctures are in the mapped data file, and shared
// between the app and utility.
//
// Type headers are stored separately from the bulk data to minimize the
// number of active pages.
//
// The full hash of the name is stored in nameHash, and nameHash&(PK_HASH_BUCKETS-1) is
// used to chain structures of a particular type together.
//
//============================================================
#define MAX_PK_NAME 64
typedef struct {
int nameHash; // PK_HashName( name )
int nextOnHashChain; // -1 = end of chain
char name[MAX_PK_NAME]; // in canonical form: backslashes to slashes and lowercase
} pkName_t;
#define PK_HASH_CHAINS 256
typedef struct {
int tableOfs; // // &firstStruct = (byte *)dfHeader + tableOfs
int count;
int structSize; // sizeof( pkWavData_t ), etc
int hashChains[PK_HASH_CHAINS]; // -1 = end of chain
} pkType_t;
// dfWavData holds everything necessary to fully create an OpenAL sample buffer
typedef struct {
pkName_t name;
int wavDataOfs;
int wavChannels; // 1 or 2
int wavChannelBytes; // 1 or 2
int wavRate; // 22050, etc
int wavNumSamples; // each sample holds all the channels
// we may want looping information here later
} pkWavData_t;
// iPhone does not natively support palettized textures, but we
// might conceivably want to support luminance and intensity textures
// in the future.
typedef enum {
TF_565,
TF_5551,
TF_4444,
TF_8888,
TF_LA,
TF_PVR4,
TF_PVR4A,
TF_PVR2,
TF_PVR2A,
} textureFormat_t;
// dfImageData_t holds everything necessary to fully create an OpenGL texture object
typedef struct {
pkName_t name;
int picDataOfs; // the raw bits to pass to gl, mipmaps appended
// for PVR formats, the minimum size of each level is 32 bytes
int format;
int uploadWidth;
int uploadHeight;
int numLevels; // 1 for non mipmapped, otherwise log2( largest dimension )
// glTexParameters
int wrapS;
int wrapT;
int minFilter;
int magFilter;
int aniso;
// The upload sizes can be larger than the source sizes for
// non power of two sources, or for non square sources in the
// case of PVR compression.
int srcWidth;
int srcHeight;
float maxS; // srcWidth / uploadWidth
float maxT;
// Track the outlines of up to two boxes of non-transparent pixels
// to allow optimized drawing of sprites with large empty areas.
// The reason for two boxes is that the common lights have something
// at the top and something at the bottom, with nothing inbetween.
// These are inclusive bounds of the rows / columns in
// uploadWidth / uploadHeight with non-0 alpha
int numBounds;
int bounds[2][2][2];
} pkTextureData_t;
typedef struct {
pkName_t name;
int rawDataOfs; // (byte *)pkHeader + dataOfs
int rawDataLen; // there will always be a 0 byte appended to terminate strings
// that is not counted in this length
} pkRawData_t;
#define PKFILE_VERSION 0x12340002
typedef struct {
int version;
pkType_t textures;
pkType_t wavs;
pkType_t raws;
} pkHeader_t;
//============================================================
//
// In-memory, writable structures
//
//============================================================
typedef struct {
unsigned glTexNum;
const pkTextureData_t *textureData;
// we will need to add LRU links if texture caching is needed
} pkTexture_t;
typedef struct {
unsigned alBufferNum; // created with the staticBuffer extension directly in the mapped memory
const pkWavData_t *wavData;
} pkWav_t;
void PK_Init( const char *pakFileName );
const pkName_t *PK_FindType( const char *rawName, const pkType_t *type, int *index );
const byte * PK_FindRaw( const char *rawName, int *len ); // len can be NULL if you don't need it
pkTexture_t * PK_FindTexture( const char *imageName );
pkWav_t * PK_FindWav( const char *soundName );
// The name will be converted to canonical name (backslashes converted to slashes and lowercase)
// before generating a hash.
int PK_HashName( const char *name, char canonical[MAX_PK_NAME] );
void PK_BindTexture( pkTexture_t *tex );
void PK_DrawTexture( pkTexture_t *tex, int x, int y );
void PK_StretchTexture( pkTexture_t *tex, float x, float y, float w, float h );
extern pkHeader_t * pkHeader;
extern int pkSize;
// images and wavs have writable state, so they need separate
// structs that also point to the source in the pak file
extern pkTexture_t *pkTextures;
extern pkWav_t * pkWavs;
+993
View File
@@ -0,0 +1,993 @@
/*
* iphone_async.c
* doom
*
* Created by John Carmack on 7/2/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
typedef struct {
int msecFromLast;
int msecToExecute;
int sent;
int received;
int latency;
} asyncStats_t;
#define MAX_ASYNC_LOGS 256
static asyncStats_t asyncStats[MAX_ASYNC_LOGS];
int asyncTicNum;
// we save this for the packet acknowledge, and also for debugging
static packetServer_t lastServerPacket;
/*
==================
ShowNet
Graph packet receives and transmits
==================
*/
void ShowNet() {
if ( !showNet->value ) {
return;
}
color4_t red = { 255, 0, 0, 255 };
color4_t green = { 0, 255, 0, 255 };
color4_t blue = { 0, 0, 255, 255 };
int now = asyncTicNum; // latch it in case it changes
for ( int i = 1 ; i < 30 ; i++ ) {
asyncStats_t *lt = &asyncStats[(now - i ) & (MAX_ASYNC_LOGS-1)];
R_Draw_Fill( 0, i * 4, lt->sent * 10, 2, red );
R_Draw_Fill( 100, i * 4, lt->received * 10, 2, green );
R_Draw_Fill( 200, i * 4, lt->latency * 10, 2, blue );
}
}
void ShowMiniNet() {
if ( !miniNet->value ) {
return;
}
color4_t red = { 255, 0, 0, 255 };
color4_t green = { 0, 255, 0, 255 };
color4_t blue = { 0, 0, 255, 255 };
int now = asyncTicNum; // latch it in case it changes
now--;
int x = huds.menu.x;
int y = huds.menu.y;
for ( int i = 0 ; i < 10 ; i++ ) {
asyncStats_t *lt = &asyncStats[(now - i ) & (MAX_ASYNC_LOGS-1)];
R_Draw_Fill( x, y+i * 4, lt->sent * 4, 2, red );
R_Draw_Fill( x+20, y+i * 4, lt->received * 4, 2, green );
R_Draw_Fill( x+40, y+i * 4, lt->latency * 4, 2, blue );
}
}
/*
==================
UpdatePeerTiming
Calculates one way latency based on local and remote times
==================
*/
void UpdatePeerTiming( netPeer_t *peer, int remoteMilliseconds ) {
peer->lastPacketAsyncTic == asyncTicNum;
peer->lastPacketTime = SysIphoneMilliseconds();
peer->lastTimeDelta = abs( remoteMilliseconds - peer->lastPacketTime );
if ( peer->lowestTimeDelta == 0 || peer->lastTimeDelta < peer->lowestTimeDelta ) {
peer->lowestTimeDelta = peer->lastTimeDelta;
}
peer->oneWayLatency = peer->lastTimeDelta - peer->lowestTimeDelta;
if ( peer->oneWayLatency < 0 ) {
// this can happen if we context switched at a bad time
peer->lowestTimeDelta = peer->lastTimeDelta;
peer->oneWayLatency = 0;
}
// printf( "OWL:%i timeDelta:%i lowest:%i\n", peer->oneWayLatency,
// peer->lastTimeDelta, peer->lowestTimeDelta );
}
/*
==================
iphoneProcessPacket
A packet has been received over WiFi or bluetooth
==================
*/
void iphoneProcessPacket( const struct sockaddr *from, const void *data, int len ) {
if ( len < 4 ) {
printf( "discarding packet because len = %i.\n", len );
return;
}
int packetID = *(int *)data;
if ( !netgame ) {
// setup and join are only processed while in the menu system
if ( packetID == PACKET_VERSION_SETUP ) {
if ( localGameID == setupPacket.gameID ) {
// if we are sending packets, always ignore other setup packets
printf( "discarding setup packet because we are the server\n" );
return;
}
setupPacketFrameNum = iphoneFrameNum;
// save this packet
// printf( "valid setup packet\n" );
setupPacket = *(packetSetup_t *)data;
return;
}
if ( packetID == PACKET_VERSION_JOIN ) {
// we should only process join packets if we are in the multiplayer
// menu and running the current game
if ( menuState != IPM_MULTIPLAYER ) {
printf( "discarding join packet because not in IPM_MULTIPLAYER\n" );
return;
}
if ( setupPacket.gameID != localGameID ) {
printf( "discarding join packet because we aren't the server\n" );
return;
}
packetJoin_t *pj = (packetJoin_t *)data;
if ( pj->playerID == 0 ) {
// should never happen
printf( "discarding join packet because playerID is 0\n" );
return;
}
// add this player
int i;
for ( i = 0 ; i < MAXPLAYERS ; i++ ) {
if ( setupPacket.playerID[i] == pj->playerID ) {
netPlayers[i].peer.lastPacketTime = SysIphoneMilliseconds();
break;
}
}
if ( i == MAXPLAYERS ) {
// not in yet, add if possible
for ( i = 0 ; i < MAXPLAYERS ; i++ ) {
if ( setupPacket.playerID[i] == 0 ) {
setupPacket.playerID[i] = pj->playerID;
netPlayers[i].peer.address = *from;
netPlayers[i].peer.lastPacketTime = SysIphoneMilliseconds();
break;
}
}
// if all players are active, the new join gets ignored
}
// printf( "valid join packet\n" );
return;
}
// no other packets are processed unless we are in the game
printf( "discarding packet with id 0x%x when not in netgame\n", packetID );
return;
}
// the following are only for running games
if ( consoleplayer == 0 ) {
// we are the server, and should only receive packetClient_t
if ( packetID != PACKET_VERSION_CLIENT ) {
static boolean typeErrorPrinted;
if ( !typeErrorPrinted ) {
typeErrorPrinted = true;
printf( "Packet received with type 0x%x instead of 0x%x\n", packetID, PACKET_VERSION_CLIENT );
}
return;
}
packetClient_t *pc = (packetClient_t *)data;
if ( len != sizeof( *pc ) ) {
// this should always be an exact length match
return;
}
if ( pc->gameID != gameID ) {
static boolean gameErrorPrinted;
if ( !gameErrorPrinted ) {
gameErrorPrinted = true;
printf( "Packet received with gameID 0x%x instead of 0x%x\n", pc->gameID, gameID );
}
return;
}
assert( pc->consoleplayer > 0 && pc->consoleplayer < MAXPLAYERS );
netPlayer_t *np = &netPlayers[pc->consoleplayer];
if ( np->pc.packetSequence >= pc->packetSequence ) {
printf( "Out of order or duplicated packet from player %i\n", pc->consoleplayer );
return;
}
np->peer.currentPingTics = packetSequence - pc->packetAcknowledge;
if ( np->pc.packetSequence != pc->packetSequence - 1 ) {
printf( "Dropped %i packets from player %i\n", pc->packetSequence - 1 - np->pc.packetSequence,
pc->consoleplayer );
}
// good packet from client
np->pc = *pc;
UpdatePeerTiming( &np->peer, np->pc.milliseconds );
} else {
// we are a client, and should only receive server packets
if ( packetID != PACKET_VERSION_SERVER ) {
static boolean typeErrorPrinted;
if ( !typeErrorPrinted ) {
typeErrorPrinted = true;
printf( "Packet received with type 0x%x instead of 0x%x\n", packetID, PACKET_VERSION_CLIENT );
}
return;
}
packetServer_t *ps = (packetServer_t *)data;
if ( len > sizeof( *ps ) ) {
// packets will usually have less ticcmd_t, but never more
return;
}
if ( ps->gameID != gameID ) {
static boolean gameErrorPrinted;
if ( !gameErrorPrinted ) {
gameErrorPrinted = true;
printf( "Packet received with gameID 0x%x instead of 0x%x\n", ps->gameID, gameID );
}
return;
}
if ( ps->packetSequence <= lastServerPacket.packetSequence ) {
printf( "Out of order or duplicated packet from server: %i <= %i\n",
ps->packetSequence , lastServerPacket.packetSequence );
return;
}
int drop = ps->packetSequence - (lastServerPacket.packetSequence + 1);
if ( drop > 0 ) {
printf( "Dropped %i packets from server\n", drop );
}
// good packet from server
memcpy( &lastServerPacket, ps, len );
UpdatePeerTiming( &netServer, ps->milliseconds );
netServer.currentPingTics = packetSequence - ps->packetAcknowledge;
// It is possible to have a client run a tic that hasn't been run yet on the game
// server, since the server can be generating cmds and sending packets while
// its game frame is hitched for an image load, so this is not an error condition.
// assert( ps->gametic >= gametic );
// this should never happen
assert( ps->maketic >= maketic );
// if a ticcmd_t that we need has permanently rolled off the end, we are hosed.
// This shouldn't happen, since we don't create commands if all the clients
// haven't processed most of the ones already sent.
if ( ps->maketic - gametic >= BACKUPTICS ) {
printf( "BACKUPTICS exceeded: ps->maketic %i, gametic %i\n",
ps->maketic, gametic );
netGameFailure = NF_INTERRUPTED;
}
// move over the new commands
// it is possible that some early frames of these are redundant, due
// to packets crossing in flight.
ticcmd_t *cmd_p = ps->netcmds;
for ( int i = ps->starttic ; i < ps->maketic ; i++ ) {
for ( int j = 0 ; j < MAXPLAYERS ; j++ ) {
if ( playeringame[j] ) {
netcmds[j][i&BACKUPTICMASK] = *cmd_p++;
}
}
}
// copy this after the cmds have been updated
maketic = ps->maketic;
// check consistancy for all in-game players on the most
// recent gametic that the server knew this client had run
int checkTic = ps->consistancyTic;
assert( checkTic > gametic - BACKUPTICS ); // if older than this, we have lost the data
checkTic &= BACKUPTICMASK;
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
if ( playeringame[i] ) {
if ( ps->consistancy[i] != consistancy[i][checkTic] ) {
printf( "ConsistancyFailure for player %i on consistancyTic %i\n",
i, ps->consistancyTic );
netGameFailure = NF_CONSISTANCY;
}
}
}
}
}
/*
==================
SendSetupPacketIfNecessary
the server sends out a setup packet to each joined client so they
can see the game options needed to start the game.
==================
*/
void SendSetupPacketIfNecessary() {
if ( setupPacket.gameID != localGameID ) {
// we aren't the server
return;
}
if ( gametic >= 2 ) {
// everyone has already started, so they don't need more setup packets
return;
}
setupPacket.sendCount++;
// player 0 is always the server, no need to send to ourselves
for ( int i = 1 ; i < MAXPLAYERS ; i++ ) {
if ( setupPacket.playerID[i] == 0 ) {
continue;
}
int r = sendto( gameSocket, &setupPacket, sizeof( setupPacket ), 0,
&netPlayers[i].peer.address, sizeof( netPlayers[i].peer.address ) );
if ( r == -1 ) {
Com_Printf( "UDP sendTo failed: %s\n", strerror( errno ) );
close( gameSocket );
gameSocket = -1;
}
}
}
/*
==================
DeadBandAdjust
Compresses the 0.0 - 1.0 range into deadband - 1.0
==================
*/
float DeadBandAdjust( float f, float deadBand ) {
if ( f < 0 ) {
return -DeadBandAdjust( -f, deadBand );
}
if ( f > 1.0 ) {
return 1.0;
}
if ( f < deadBand ) {
return 0;
}
return (f-deadBand) / (1.0 - deadBand);
}
/*
==================
AxisHit
Returns a -1 to 1 range
If activeFraction is less than 1.0, the range will clamp
to the limits before the edge of the box is hit.
==================
*/
float AxisHit( ibutton_t *hud ) {
// will be set true if -1 or 1
hud->drawAsLimit = false;
if ( hud->buttonFlags & BF_IGNORE ) {
return 0;
}
touch_t *t = hud->touch;
if ( !t ) {
return 0;
}
int centerX, centerY;
if ( centerSticks->value ) {
// center on each touch
centerX = hud->downX;
centerY = hud->downY;
} else {
centerX = hud->x + hud->drawWidth / 2;
centerY = hud->y + hud->drawHeight / 2;
}
float w = hud->drawWidth * 0.5 * hud->scale;
float h = hud->drawHeight * 0.5 * hud->scale;
int x = t->x - centerX;
int y = t->y - centerY;
float f;
int isXaxis = ( hud != &huds.forwardStick );
if ( isXaxis ) {
f = (float)x / w;
} else {
f = (float)y / h;
}
float deadBand = stickDeadBand->value;
if ( hud == &huds.turnStick ) {
deadBand = 0;
}
if ( f > deadBand ) {
f -= deadBand;
} else if ( f < -deadBand ) {
f += deadBand;
} else {
// inside the deadband
return 0;
}
// adjust so you can hit the limit even if the control is drawn at the very edge
// of the screen
f /= (0.95-deadBand);
if ( f > 1.0f ) {
f = 1.0f;
hud->drawAsLimit = true;
} else if ( f < -1.0f ) {
f = -1.0f;
hud->drawAsLimit = true;
}
if ( hud == &huds.turnStick && rampTurn->value ) {
// do "gamma corrected" movement, so changes are always proportional
if ( f > 0 ) {
f = 0.01 * pow( 1.047, f * 100 );
} else {
f = -0.01 * pow( 1.047, f * -100 );
}
}
return f;
}
static const float NOT_TOUCHED_STATE = 99999.0f;
float RotorControl( ibutton_t *hud ) {
if ( hud->buttonFlags & BF_IGNORE ) {
return 0;
}
touch_t *t = hud->touch;
if ( !t ) {
// no touches in the control
hud->touchState = NOT_TOUCHED_STATE;
return 0;
}
float delta[2];
int centerX = hud->x + hud->drawWidth / 2;
int centerY = hud->y + hud->drawHeight / 2;
delta[0] = t->x - centerX;
delta[1] = t->y - centerY;
float rotorAngle = atan2( delta[1], delta[0] );
if ( hud->touchState == NOT_TOUCHED_STATE ) {
// just touched, haven't moved yet
hud->touchState = rotorAngle;
return 0;
}
float deltaAngle = rotorAngle - hud->touchState;
// handle the wrap around cases
if ( deltaAngle >= M_PI ) {
deltaAngle = deltaAngle - 2*M_PI;
} else if ( deltaAngle <= -M_PI ) {
deltaAngle = 2*M_PI + deltaAngle;
}
hud->touchState = rotorAngle;
hud->drawState += deltaAngle;
return deltaAngle / (2*M_PI);
}
#define TURBOTHRESHOLD 0x32
static int ClampMove( int v ) {
if ( v > TURBOTHRESHOLD ) {
return TURBOTHRESHOLD;
}
if ( v < -TURBOTHRESHOLD ) {
return -TURBOTHRESHOLD;
}
return v;
}
/*
==================
iphoneBuildTiccmd
Use touch and tilt controls to set up a doom ticcmd_t
==================
*/
static void iphoneBuildTiccmd(ticcmd_t* cmd) {
memset(cmd,0,sizeof*cmd);
// cmd->consistancy = consistancy[consoleplayer][maketic & BACKUPTICMASK];
if ( menuState != IPM_GAME ) {
// if in the menus, always generate an empty event
return;
}
// the respawn button triggers a use
if ( respawnActive ) {
cmd->buttons |= BT_USE;
respawnActive = false;
}
if ( gamestate != GS_LEVEL ) {
// at intermissions, all taps equal attack
// FIXME: better latched value
if ( numTouches == numPrevTouches + 1 ) {
cmd->buttons |= BT_ATTACK;
}
return;
}
// don't allow movement control use during automap
if ( automapmode & am_active ) {
return;
}
// don't built a tic when dead, other than the respawn use
if ( players[consoleplayer].playerstate == PST_DEAD ) {
return;
}
//------------------------
// No controls during weapon-select screen
//------------------------
boolean weaponCycle = false;
if ( drawWeaponSelect ) {
// if the weaponSelect overlay is up, continue tracking held touches
// until the are released
for ( ibutton_t *hud = (ibutton_t *)&huds ; hud != (ibutton_t *)(&huds+1) ; hud++ ) {
if ( hud->touch || hud == &huds.weaponSelect ) {
UpdateHudTouch( hud );
}
}
// Re-tapping in the weapon select area will cycle to the next weapon.
// The action happens on initial touch.
touch_t *t = huds.weaponSelect.touch;
if ( t && t->down && t->stateCount == 1 ) {
drawWeaponSelect = false;
t->stateCount++; // ensure it won't bring it back up
weaponCycle = true;
} else {
return;
}
}
//------------------------
// gameplay controls
//------------------------
// update all the hud touch states
if ( menuState == IPM_GAME ) {
UpdateHudTouch( &huds.forwardStick );
UpdateHudTouch( &huds.sideStick );
UpdateHudTouch( &huds.turnStick );
UpdateHudTouch( &huds.turnRotor );
UpdateHudTouch( &huds.weaponSelect );
}
// tap in the lower center for weapon switch
touch_t *t = huds.weaponSelect.touch;
if ( t && t->down && t->stateCount == 1 ) {
drawWeaponSelect = true;
}
// hack to let a single touch control both hud elements on combo sticks
// This is dependent on the order in the structure, and probably not a good
// way to do things.
if ( huds.sideStick.x == huds.forwardStick.x && huds.sideStick.y == huds.forwardStick.y ) {
huds.sideStick.touch = huds.forwardStick.touch;
huds.sideStick.downX = huds.forwardStick.downX;
huds.sideStick.downY = huds.forwardStick.downY;
}
if ( huds.turnStick.x == huds.forwardStick.x && huds.turnStick.y == huds.forwardStick.y ) {
huds.turnStick.touch = huds.forwardStick.touch;
huds.turnStick.downX = huds.forwardStick.downX;
huds.turnStick.downY = huds.forwardStick.downY;
}
// the fire button doesn't grab touches
{
int x = huds.fire.x - ( huds.fire.drawWidth >> 1 );
int y = huds.fire.y - ( huds.fire.drawHeight >> 1 );
int w = huds.fire.drawWidth << 1;
int h = huds.fire.drawHeight << 1;
if ( AnyTouchInBounds( x, y, w, h ) ) {
cmd->buttons |= BT_ATTACK;
huds.fire.buttonFlags |= BF_DRAW_ACTIVE; // draw with color
} else {
huds.fire.buttonFlags &= ~BF_DRAW_ACTIVE;
}
}
int forwardmove;
int sidemove;
// the edge of the drawn control should give the maximum
// legal doom movement speed
huds.forwardStick.scale = stickMove->value / 128.0f;
huds.sideStick.scale = stickMove->value / 128.0f;
forwardmove = -TURBOTHRESHOLD * AxisHit( &huds.forwardStick );
sidemove = TURBOTHRESHOLD * AxisHit( &huds.sideStick );
huds.turnStick.scale = stickTurn->value / 128.0f;
cmd->angleturn = -1500.0f * AxisHit( &huds.turnStick );
// rotary wheel
cmd->angleturn -= rotorTurn->value * RotorControl( &huds.turnRotor );
// accelerometer tilting
sidemove += tiltMove->value * DeadBandAdjust( tilt, tiltDeadBand->value );
cmd->angleturn -= tiltTurn->value * DeadBandAdjust( tilt, tiltDeadBand->value );
// clamp movements
cmd->forwardmove = ClampMove( forwardmove );
cmd->sidemove = ClampMove( sidemove );
// tap in the upper center for use
if ( TouchPressed( 140, 0, 240, 200 ) ) {
cmd->buttons |= BT_USE;
}
// auto-use if the game thread found a usable line in front of the player
if ( autoUse->value && autoUseActive ) {
if ( cmd->buttons & BT_USE ) {
// Allow a tap to briefly cancel the auto-use, which works around
// some issues with incorrectly started auto-uses preventing
// a real door from opening.
cmd->buttons &= ~BT_USE;
} else {
cmd->buttons |= BT_USE;
}
}
if ( weaponSelected != -1 ) {
cmd->buttons |= BT_CHANGE;
cmd->buttons |= weaponSelected<<BT_WEAPONSHIFT;
weaponSelected = -1;
} else {
// auto-cycle weapons when firing on empty
if ( players[consoleplayer].attackdown && !P_CheckAmmo(&players[consoleplayer]) ) {
weaponCycle = true;
}
// weapon switch
int newweapon = wp_nochange;
if ( weaponCycle ) {
// witch to next weapon when out of ammo
newweapon = P_SwitchWeapon(&players[consoleplayer]);
}
if (newweapon != wp_nochange) {
cmd->buttons |= BT_CHANGE;
cmd->buttons |= newweapon<<BT_WEAPONSHIFT;
}
}
}
/*
==================
ShouldSendPacket
WiFi can suffer from bad "pileup" because of the link-level
retransmit behavior. What we would dearly love is an ioctl or
something that returned a value of how many packets are backed
up in the transmit queue, but that would be terribly driver
specific, and we are unlikely to ever see it.
We are going to infer that there is a pileup by looking at the
round trip latency. If we ever support internet connections with
real latencies that are this large, we will have to use something
else.
==================
*/
// while 1 is usual, some combinations of AsyncTic phases will give 2 commonly,
// and we don't want to throttle back in those cases.
static const int okPacketLatency = 4;
static const int okOneWayLatency = 70;
static boolean ShouldSendPacket( netPeer_t *peer, int packetLatency ) {
if ( throttle->value == 0 ) {
// disabled completely, always send
return true;
}
if ( peer == &netServer ) {
if ( throttle->value == 2 ) {
return false; // don't send client messages at all
}
} else {
if ( throttle->value == 3 ) { // don't send server messages at all
return false;
}
}
// immediately fire back a packet if it looks like we just got one through
// clearly
if ( peer->lastPacketAsyncTic == asyncTicNum && peer->oneWayLatency < okOneWayLatency ) {
return true;
}
if ( packetLatency <= okPacketLatency ) {
return true;
}
// limit from 1 to 4, in worst case only transmit a packet
// every 16 frames, or about half a second
packetLatency -= okPacketLatency;
if ( packetLatency > 4 ) {
packetLatency = 4;
}
if ( asyncTicNum & ((1<<packetLatency)-1) ) {
return false; // inhibit transmit this frame
}
printf( "Sending on packetLatency %i\n", packetLatency );
return true; // we are throttling back, but transmit this frame
}
/*
==================
iphoneAsyncTic
This is called by a 30hz scheduled timeer in the main application thread.
Commands are generated and sent to the packet server
on this regular basis, regardless of the frame rate held by iphoneFrame().
This thread should be higher priority, so it always interrupts the game
thread.
It might be nice to run the game tics here, but the rendering code is not
thread safe with the game, so it isn't an option.
==================
*/
void iphoneAsyncTic() {
// log our timing accuracy (seems to be within a few msec -- not bad)
static int prev;
int now = SysIphoneMilliseconds();
asyncStats_t *stats = &asyncStats[asyncTicNum&(MAX_ASYNC_LOGS-1)];
asyncTicNum++;
memset( stats, 0, sizeof( *stats ) );
stats->msecFromLast = now - prev;
stats->msecToExecute = 0;
prev = now;
// listen for changes to available servers
ProcessDNSMessages();
// send out the setup packets if we are just starting the game
SendSetupPacketIfNecessary();
// don't generate any commands while loading levels
if ( iphoneFrameNum == levelLoadFrameNum ) {
return;
}
// don't let the game thread mess with touches during the async tic execution
pthread_mutex_lock( &eventMutex );
int afterLock = SysIphoneMilliseconds();
// latch the current touches for processing
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &sysTouches[i];
gameTouches[i] = *t;
// handle the special case of a touch that went down and up
// inside a single frame
if ( t->stateCount == -1 ) {
gameTouches[i].stateCount = 1;
t->stateCount = 0;
t->down = false;
} else {
t->stateCount++;
}
}
//---------------------------------
// read network packets
//
// we may receive multiple packets in one frame due to timer skew
// even with only one other player
//---------------------------------
if ( !netGameFailure ) {
byte packet[1500];
while( gameSocket > 0 ) {
struct sockaddr from;
unsigned senderLen = sizeof( from );
int r = recvfrom( gameSocket, &packet, sizeof( packet ), 0, &from, &senderLen );
if ( r == -1 ) {
if ( errno != EAGAIN ) {
perror( "recvfrom" );
}
break;
}
stats->received++;
iphoneProcessPacket( &from, packet, r );
}
}
//---------------------------------
// Create local user command
//
// We always create one, but it might not wind up being used for a game
// tic if it doesn't make it to the server at the right time.
//---------------------------------
ticcmd_t cmd;
iphoneBuildTiccmd( &cmd );
//---------------------------------
// If we are a client, send our command to the server
//---------------------------------
if ( consoleplayer != 0 ) {
if ( gameID != 0 && netgame && !netGameFailure ) {
stats->latency = packetSequence - lastServerPacket.packetAcknowledge;
if ( ShouldSendPacket( &netServer, packetSequence - lastServerPacket.packetAcknowledge ) ) {
packetClient_t cp;
memset( &cp, 0, sizeof( cp ) );
cp.packetType = PACKET_VERSION_CLIENT;
cp.gameID = gameID;
cp.packetAcknowledge = lastServerPacket.packetSequence;
cp.milliseconds = SysIphoneMilliseconds();
cp.packetSequence = packetSequence++;
cp.consoleplayer = consoleplayer;
cp.gametic = gametic;
cp.cmd = cmd;
int r = sendto( gameSocket, &cp, sizeof( cp ), 0, &netServer.address, sizeof( netServer.address ) );
stats->sent++;
if ( r == -1 ) {
printf( "UDP sendTo failed: %s\n", strerror( errno ) );
close( gameSocket );
gameSocket = -1;
}
}
}
} else {
// take our command directly
netPlayers[0].pc.cmd = cmd;
netPlayers[0].pc.gametic = gametic;
netPlayers[0].peer.lastPacketTime = now;
//---------------------------------
// Decide if we want to latch the current commands for execution by the game
//
//---------------------------------
int ticIndex = maketic & BACKUPTICMASK;
int worstTic = gametic;
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
if ( playeringame[i] ) {
netcmds[i][ticIndex] = netPlayers[i].pc.cmd;
if ( netPlayers[i].pc.gametic < worstTic ) {
worstTic = netPlayers[i].pc.gametic;
}
}
}
// only let the server get a few tics ahead of any client, so if
// anyone is having significant net delivery problems, everyone will
// stall instead of losing the player. If this is too small, then
// every little hitch that any player gets will cause everyone to hitch.
if ( maketic - worstTic < netBuffer->value ) {
maketic++;
}
//---------------------------------
// Build server packets to send to clients
//
// Always send out the current command set over the network
// even if we didn't create a new command, in case we are just
// recovering from a lot of dropped packets.
//---------------------------------
if ( netgame && !netGameFailure ) {
// since we are sampling a shared wireless network, any of the player's
// latencies should be a good enough metric
stats->latency = packetSequence - netPlayers[1].pc.packetAcknowledge;
if ( ShouldSendPacket( &netPlayers[1].peer, stats->latency ) ) {
packetServer_t gp;
memset( &gp, 0, sizeof( gp ) );
gp.packetType = PACKET_VERSION_SERVER;
gp.gameID = gameID;
gp.packetSequence = packetSequence++;
gp.maketic = maketic;
memcpy( gp.netcmds, netcmds, sizeof( gp.netcmds ) );
//---------------------------------
// Send network packets to the clients
//---------------------------------
for ( int i = 1 ; i < MAXPLAYERS ; i++ ) {
if ( !playeringame[i] ) {
continue;
}
if ( gameSocket <= 0 ) {
continue;
}
netPlayer_t *np = &netPlayers[i];
// only send over the ticcmd that this client needs
gp.starttic = np->pc.gametic;
ticcmd_t *cmd_p = gp.netcmds;
for ( int i = gp.starttic ; i < gp.maketic ; i++ ) {
for ( int j = 0 ; j < MAXPLAYERS ; j++ ) {
if ( playeringame[j] ) {
*cmd_p++ = netcmds[j][i&BACKUPTICMASK];
}
}
}
int packetSize = (byte *)cmd_p - (byte *)&gp;
// use the most recent tic that both the client and
// server have run
gp.consistancyTic = np->pc.gametic < gametic ? np->pc.gametic : gametic;
gp.consistancyTic--;
for ( int j = 0 ; j < MAXPLAYERS ; j++ ) {
gp.consistancy[j] = consistancy[j][gp.consistancyTic&BACKUPTICMASK];
}
gp.packetAcknowledge = np->pc.packetSequence;
gp.milliseconds = SysIphoneMilliseconds();
// transmit the packet
stats->sent++;
int r = sendto( gameSocket, &gp, packetSize, 0, &np->peer.address, sizeof( np->peer.address ) );
if ( r == -1 ) {
printf( "UDP sendTo failed: %s\n", strerror( errno ) );
close( gameSocket );
gameSocket = -1;
}
}
}
}
}
stats->msecToExecute = SysIphoneMilliseconds() - now;
if ( stats->msecToExecute > 6 ) {
printf( "long asyncTic %i: %i msec (%i in lock), %i packets\n", asyncTicNum - 1, stats->msecToExecute,
afterLock - now, stats->received );
}
// the game thread can now swap touches
pthread_mutex_unlock( &eventMutex );
// signal the main thread that is probably blocked on this semaphore
if ( sem_post( ticSemaphore ) == -1 ) {
perror( "sem_post");
}
}
+558
View File
@@ -0,0 +1,558 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// this is the version number displayed on the menu screen
#define DOOM_IPHONE_VERSION 0.9
// if defined, the game runs in a separate thread from the app event loop
#define USE_GAME_THREAD
typedef enum menuState {
IPM_GAME,
IPM_MAIN,
IPM_MAPS,
IPM_MULTIPLAYER,
IPM_CONTROLS,
IPM_OPTIONS,
IPM_HUDEDIT,
IPM_PACKET_TEST
} menuState_t;
extern menuState_t menuState;
void iphoneDrawMenus();
#define VID_WIDTH 480
#define VID_HEIGHT 320
#define MAX_SKILLS 5
#define MAX_MAPS 200
#define MF_TRIED 1
#define MF_COMPLETED 2
#define MF_KILLS 4
#define MF_SECRETS 8
#define MF_TREASURE 16
#define MF_TIME 32
// we want to track mapStats for downloaded content, so we
// won't have a known number of these
typedef struct {
int dataset;
int episode;
int map;
int completionFlags[MAX_SKILLS];
} mapStats_t;
// the level select screen returns this
typedef struct {
int dataset;
int episode;
int map;
int skill;
} mapStart_t;
// this structure is saved out at the head of the binary save file,
// and allows all the menus to work without having to load a game save
typedef struct {
mapStart_t map; // this is the map currently being run
int saveGameIsValid; // when 0, resume game will just be a new game
// if someone downloads more than MAX_MAPS, they won't get stat tracking on them.
int numMapStats;
mapStats_t mapStats[MAX_MAPS];
} playState_t;
extern playState_t playState;
extern boolean levelHasBeenLoaded; // determines if "resume game" does a loadGame and exiting does a saveGame
extern pkTexture_t *arialFontTexture;
// set to 1 when app is exiting to cause game thread to do a save game,
// which would not be safe to do from the event thread
extern volatile int saveOnExitState;
extern int asyncTicNum; // 30hz
extern int iphoneFrameNum; // frame rate dependent, max of 30hz
extern int levelLoadFrameNum;
extern int consoleActive;
extern boolean iphoneTimeDemo;
extern int timeDemoStart;
extern char timeDemoResultString[80];
extern cvar_t *skill;
extern cvar_t *episode;
extern cvar_t *controlScheme;
extern cvar_t *stickMove;
extern cvar_t *stickTurn;
extern cvar_t *rotorTurn;
extern cvar_t *stickDeadBand;
extern cvar_t *tiltTurn;
extern cvar_t *tiltMove;
extern cvar_t *tiltDeadBand;
extern cvar_t *tiltAverages;
extern cvar_t *music;
extern cvar_t *miniNet;
extern cvar_t *showTilt;
extern cvar_t *showTime;
extern cvar_t *showNet;
extern cvar_t *showSound;
extern cvar_t *cropSprites;
extern cvar_t *revLand;
extern cvar_t *mapScale;
extern cvar_t *drawControls;
extern cvar_t *autoUse;
extern cvar_t *statusBar;
extern cvar_t *touchClick;
extern cvar_t *messages;
extern cvar_t *timeLimit;
extern cvar_t *fragLimit;
extern cvar_t *mpDeathmatch;
extern cvar_t *mpDataset;
extern cvar_t *mpSkill;
extern cvar_t *mpEpisode;
extern cvar_t *mpMap;
extern cvar_t *glfinish;
extern cvar_t *mapSelectY;
extern cvar_t *throttle;
extern cvar_t *centerSticks;
extern cvar_t *rampTurn;
extern cvar_t *netBuffer;
extern int numTouches;
extern int touches[5][2]; // [0] = x, [1] = y in landscape mode, raster order with y = 0 at top
// so we can detect button releases
extern int numPrevTouches;
extern int prevTouches[5][2];
extern float tilt; // -1.0 to 1.0
extern float tiltPitch;
extern boolean drawWeaponSelect; // true when the weapon select overlay is up
extern int weaponSelected; // -1 for no change
typedef unsigned char color4_t[4];
typedef unsigned char color3_t[3];
// networking
enum {
PACKET_VERSION_BASE = 0x24350010,
PACKET_VERSION_SETUP,
PACKET_VERSION_JOIN,
PACKET_VERSION_CLIENT,
PACKET_VERSION_SERVER
} packetType_t;
#define DOOM_PORT 14666 // setup packets will go to DOOM_PORT+1
// the server sends out a setup packet by broadcast, and also directly addressed
// to each client that has joined the game because broadcast packets have truly
// crappy delivery characteristics over WiFi
typedef struct {
int packetType;
int gameID; // every change to anything in packetSetup_t must change gameID
int startGame; // when this is set, start running the game
int sendCount; // just for packet drop tests
mapStart_t map;
int deathmatch;
int fraglimit;
int timelimit;
int playerID[MAXPLAYERS]; // 0 = not in game
} packetSetup_t;
// If we have received a recent setup packet before hitting the multiplayer
// button, we will send a join packet to that server. Otherwise, we will
// start acting as a new server.
typedef struct {
int packetType;
// this should match the packetSetup.gameID
int gameID;
int playerID;
} packetJoin_t;
typedef struct {
int packetType;
// gameID is determined randomly during setup, any packet that doesn't
// match is discarded
int gameID;
// this could be used to tell when we are dropping client packets
// but it isn't critical
int packetSequence;
// used to show current round trip latency
int packetAcknowledge;
// the client's clock at the time the packet was sent, used
// to track one-way latency
int milliseconds;
// the server could match this up based on ip alone, but it is nice to have
int consoleplayer;
// the last tic that the client has run
int gametic;
// some commands will get missed over the network
ticcmd_t cmd;
} packetClient_t;
typedef struct {
int packetType;
// gameID is determined randomly during setup, any packet that doesn't
// match is discarded
int gameID;
// used to detect packet drops
int packetSequence;
// used to show current round trip latency
int packetAcknowledge;
// the server's clock at the time the packet was sent, used
// to track one-way latency
int milliseconds;
// consistancyTic will be the last acknowledged gametic for this
// particular client
int consistancyTic;
// constancy is used to see if somehow the game running on the
// client has diverged from the one running on the server,
// which is an unrecoverable error
short consistancy[MAXPLAYERS];
// this will be the last pc.gametic from the player
int starttic;
// netcmds[][(maketic-1)&BACKUPTICMASK] is the most recent
int maketic;
// only the [playersInGame*(maketic-starttic)] will be transmitted
ticcmd_t netcmds[MAXPLAYERS*BACKUPTICS];
} packetServer_t;
extern int gameSocket;
extern struct sockaddr_in gameSocketAddress;
extern int playerID;
extern int gameID;
extern int localGameID;
extern int packetSequence;
// Only one game can be set up at a time on a given wireless segment, although
// several independent games can be played.
// If a valid setupPacket has arrived in the last second, that will be the
// displayed game, otherwise the local system starts sending out setupPackets.
extern packetSetup_t setupPacket;
extern int setupPacketFrameNum;
extern int localGameID; // change every time we take over as the sender of setupPackets
// set after each game tic if a usable line is in front of the player
extern boolean autoUseActive;
extern boolean respawnActive;
typedef enum {
NF_NONE,
NF_CONSISTANCY,
NF_INTERRUPTED
} netFail_t;
extern netFail_t netGameFailure; // set by asyncThread
typedef struct {
int interfaceIndex; // we must use the right socket to send packets
struct sockaddr address;
int oneWayLatency; // will always have 30+ msec of jitter
int lastPacketAsyncTic; // to easily tell if it just arrived
int lastPacketTime; // local milliseconds of last receive
int lastTimeDelta; // packet milliseconds - local milliseconds
int lowestTimeDelta; // min'd with lastTimeDelta each arrival
int currentPingTics; // packetSequence - last packetAcknowledge
} netPeer_t;
typedef struct {
netPeer_t peer;
packetClient_t pc; // most recent packet received
// TODO: There would be some benefit to ensuring that no edge transitions on
// buttons are missed due to clock/net jitter.
} netPlayer_t;
// all received packets, whether bluetooth or WiFi, go through here
void iphoneProcessPacket( const struct sockaddr *from, const void *data, int len );
extern netPeer_t netServer;
extern netPlayer_t netPlayers[MAXPLAYERS];
extern sem_t * ticSemaphore;
typedef struct {
int numGameTics;
int numPingTics;
int enterFrame;
int afterSleep;
int beforeSwap;
int afterSwap;
} logTime_t;
#define MAX_LOGGED_TIMES 512
extern logTime_t loggedTimes[MAX_LOGGED_TIMES]; // indexed by iphoneFrameNum
void LoadWallTexture( int wallPicNum );
float StringFontWidth( const char *str );
int TouchDown( int x, int y, int w, int h );
int TouchReleased( int x, int y, int w, int h );
int TouchPressed( int x, int y, int w, int h );
// y is the baseline for font drawing
float iphoneDrawText( float x, float y, float scale, const char *str );
float iphoneCenterText( float x, float y, float scale, const char *str );
void StartGame();
void iphoneOpenAutomap();
void iphoneDrawNotifyText();
void iphoneSet2D( void );
void R_Draw_Fill( int x, int y, int w, int h, color3_t c );
void R_Draw_Blend( int x, int y, int w, int h, color4_t c );
void InitImmediateModeGL();
int iphoneRotateForLandscape();
void iphoneCheckForLandscapeReverse();
void iphonePacifierUpdate();
void iphoneDrawScreen();
extern int damageflash;
extern int bonusFrameNum;
extern int attackDirTime[2];
#define BF_IGNORE 1 // don't draw or process touches
#define BF_INACTIVE 2 // draw, but no touch processing at all
#define BF_GLOW 4 // animated overbright glow
#define BF_DIMMED 8 // draw darker, but still selectable
#define BF_CENTERTEXT 16 // text in middle of button, not underneath
#define BF_TRANSPARENT 32 // blend translucent
#define BF_HUDBUTTON 64 // don't process in UpdateHudTouch
#define BF_DRAW_ACTIVE 128 // for fire button
#define BF_SMALL_CLICK 256 // for fire button
typedef struct {
int x, y;
int drawWidth, drawHeight;
pkTexture_t *texture;
const char *title;
struct touch_s *touch;
float scale; // ramps up and down after touches
int frameNum; // reset scale if not checked on previous frame
int buttonFlags;
boolean twoFingerPress; // if a second finger came down before a release for timedemo / etc
boolean pressed; // true when a touch goes down in it
// stuff for hud controls
boolean drawAsLimit; // color tint when further movement won't do anything
float touchState; // rotor angle
float drawState; // offsets for rotors
int downX, downY; // initial touch went down here
} ibutton_t;
typedef struct {
ibutton_t forwardStick;
ibutton_t sideStick;
ibutton_t turnStick;
ibutton_t turnRotor;
ibutton_t fire;
ibutton_t menu;
ibutton_t map;
ibutton_t weaponSelect;
} hud_t;
extern hud_t huds;
void HudSetForScheme( int schemeNum );
void HudSetTexnums();
void HudEditFrame();
boolean StartNetGame();
int BackButton();
void ResumeGame();
//---------------------------------------
// Touch and button
//---------------------------------------
typedef struct touch_s {
boolean down;
int x, y;
// int prevX, prevY; // will be set to x, y on first touch, copied after each game frame
int stateCount; // set to 1 on first event that state changes, incremented each game frame (-1 is a special tapped-and-released code)
void *controlOwner;
} touch_t;
#define MAX_TOUCHES 5
extern touch_t sysTouches[MAX_TOUCHES];
extern touch_t gameTouches[MAX_TOUCHES];
extern pthread_mutex_t eventMutex; // used to sync between game and event threads
touch_t *TouchInBounds( int x, int y, int w, int h );
touch_t *AnyTouchInBounds( int x, int y, int w, int h );
touch_t *UpdateHudTouch( ibutton_t *hud );
bool NewTextButton( ibutton_t *b, const char *title, int x, int y, int w, int h );
void SetButtonPics( ibutton_t *button, const char *picName, const char *title, int x, int y );
void SetButtonPicsAndSizes( ibutton_t *button, const char *picBase, const char *title, int x, int y, int w, int h );
boolean HandleButton( ibutton_t *button );
//---------------------------------------
// Doom stuff we use directly
//---------------------------------------
void G_DoSaveGame (boolean menu);
extern short consistancy[MAXPLAYERS][BACKUPTICS];
extern boolean levelTimer;
extern int levelTimeCount;
extern boolean levelFragLimit;
extern int levelFragLimitCount;
//---------------------------------------
// iphone_sound.c
//---------------------------------------
void Sound_Init( void );
void Sound_StartLocalSound( const char *sound );
void Sound_StartLocalSoundAtVolume( const char *sound, float volume );
void ShowSound();
//---------------------------------------
// iphone_net.c
//---------------------------------------
// dump all the interfaces and ip addresses for debugging
void ReportNetworkInterfaces();
// open a UDP socket, pass "en0" for wifi
int UDPSocket( const char *interfaceName, int portnum );
// return false if the multiplayer button should be disabled
boolean NetworkAvailable();
// this can be called every frame in the menu to highlight
// the multiplayer icon when a server is already up
boolean NetworkServerAvailable();
// returns "WiFi", "BlueTooth", or "" for display on the
// main menu multiplayer icon
const char *NetworkServerTransport();
// this queries DNS for the actual address
boolean ResolveNetworkServer( struct sockaddr *addr );
// If we are starting a server instead of joining one, make
// us available as a bonjour service until we start the game
// or back out of the multiplayer menu. Returns false if
// someone else grabbed it just before we could.
boolean RegisterGameService();
void TerminateGameService();
// called by AsyncTic() to check for server state changes,
// registers for service browsing on first call.
void ProcessDNSMessages();
// draw a graph of packets sent and received
void ShowNet();
void ShowMiniNet();
//---------------------------------------
// iphone_mapSelect.c
//---------------------------------------
// returns false if nothing was selected
// if map->map is -1, the back button was hit instead of choosing a level
boolean iphoneMapSelectMenu( mapStart_t *map );
mapStats_t *FindMapStats( int dataset, int episode, int map, boolean create );
const char *FindMapName( int dataset, int episode, int map );
//---------------------------------------
// iphone_start.c
//
// game harness routines
//---------------------------------------
void ResumeGame();
boolean StartNetGame();
void StartSaveGame();
void StartSinglePlayerGame( mapStart_t map );
void StartDemoGame( boolean timeDemoMode );
//---------------------------------------
// interfaces from the original game code
//---------------------------------------
void iphoneSetNotifyText( const char *str, ... );
void iphoneIntermission( wbstartstruct_t* wbstartstruct );
void iphoneStartLevel();
void iphoneStartMusic();
void iphoneStopMusic();
void iphonePlayMusic( const char *name );
void iphonePauseMusic();
void iphoneResumeMusic();
//---------------------------------------
// interfaces to Objective-C land
//---------------------------------------
// The event thread will fill this after hitting enter
// on the console. The game thread should check it,
// execute it, and clear it under mutex.
extern char consoleCommand[1024];
void SysIPhoneSwapBuffers();
void SysIPhoneVibrate();
void SysIPhoneOpenURL( const char *url );
void SysIPhoneSetUIKitOrientation( int isLandscapeRight );
const char * SysIPhoneGetConsoleTextField();
void SysIPhoneSetConsoleTextField(const char *);
void SysIPhoneInitAudioSession();
int SysIPhoneOtherAudioIsPlaying();
int SysIphoneMilliseconds();
int SysIphoneMicroseconds();
const char * SysIphoneGetAppDir();
const char * SysIphoneGetDocDir();
//---------------------------------------
// interfaces from Objective-C land
//---------------------------------------
void iphoneStartup();
void iphoneShutdown();
void iphoneFrame();
void iphoneAsyncTic();
void iphoneTiltEvent( float *tilts );
+35
View File
@@ -0,0 +1,35 @@
/*
* iphone_email.h
* Doom
*
* Created by Greg Hodges on 10/20/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//Appends the console buffer while replacings non-URLscheme compatible text
void AppendConsoleBuffer(const char *buf);
//Emails the console buffer to id software
void EmailConsole();
+235
View File
@@ -0,0 +1,235 @@
/*
* iphone_email.c
* Doom
*
* Created by Greg Hodges on 10/20/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "iphone_email.h"
#include <time.h>
char *consoleBuffer = NULL; //buffer for the console output
int size = 0;
/*
* ReplaceAt()
* Replaces the char at location with the insertString
*/
void ReplaceAt( char *oldString, const char *insertString, int location)
{
int length = strlen(oldString);
int chunkLength = strlen(insertString);
char *newString = malloc(length + chunkLength + 1);//the 1 includes space for the null terminating character
#if 0
//strcpy(newString, old);
strncpy(newString, oldString, location);
newString[location] = '\0';
strcat(newString, insertString);
strcpy(&newString[location+chunkLength], &oldString[location + 1]);
free(oldString);
oldString = newString;
#endif
//copy the front part
char frontPart[location+1];
strncpy(frontPart, oldString, location);
frontPart[location] = '\0';
printf("\nfrontPart: %s", frontPart);
//copy the back part
char backPart[length - location];
strcpy(backPart, &oldString[location+1]);
backPart[length - location - 1] = '\0';
printf("\nbackPart: %s\n\n", backPart);
//put it all together in the new string
newString[0] = '\0';
strcat(newString, frontPart);
strcat(newString, insertString);
strcat(newString, backPart);
//delete old string
free(oldString);
//replace old string
oldString = newString;
}
/*
* AppendBuffer()
* Directly appends the console buffer
*/
void AppendBuffer(const char *buf)
{
int length = strlen(buf) + 1; //strlen doesn't include the null terminating character
char *temp = malloc(length);
strcpy(temp, buf);
for (int i = 0; i < length; ++i)
{
if (temp[i] == ' ' || temp[i] == '\n' || temp[i] == '=' )
temp[i] = '_';
}
#if 0
int i = 0;
while (temp[i] != '\0')
{
if (temp[i] == ' ')
ReplaceAt(temp, "_testString_", i);
++i;
}
length = strlen(temp) + 1;
#endif
//copy the old & new string into a buffer
char *newBuf = malloc(size + length);
if (consoleBuffer)
{
strcpy(newBuf, consoleBuffer);
}
strcpy(&newBuf[size], temp);
//delete the old string and have it point to the new one
free(consoleBuffer);
consoleBuffer = newBuf;
size = strlen(consoleBuffer);
//delete the temp string
free(temp);
}
/*
* AppendChunk()
* Just append a chunk of the incoming string from start to i
*/
void AppendChunk(const char *buf, int start, int i)
{
int size = i+1 - start;
char chunk[size];
chunk[0] = '\0';
strncpy(chunk, &buf[start], size-1);
chunk[size-1] = '\0';
AppendBuffer(chunk);
}
/*
* AppendConsoleBuffer()
* Appends the console buffer while replacings non-URLscheme compatible text
*/
void AppendConsoleBuffer(const char *buf)
{
int length = strlen(buf) + 1; //strlen doesn't include the null terminating character
char *temp = malloc(length);
strcpy(temp, buf);
int start = 0;
int i = 0;
for (i = 0; i < length; ++i)
{
//replace space and = with _
//spaces and = tend to mess up the URL scheme
if (temp[i] == ' ' )
{
temp[i] = '_';
AppendChunk(temp, start, i); ++i; start = i;
AppendBuffer("\%20"); //line space
}
if (temp[i] == '=' )
{
temp[i] = '_';
AppendChunk(temp, start, i); ++i; start = i;
AppendBuffer("\%3D"); //=
}
if (temp[i] == '\n')
{
temp[i] = '_';
AppendChunk(temp, start, i); ++i; start = i;
AppendBuffer("\%0A"); //line return
}
}
//append the last of the chunk
i = length - 1;
if (start < i)
{
AppendChunk(temp, start, i);
}
// AppendString("\%0A"); //line return
}
/*
*
* Email the console buffer to id
*
*/
void EmailConsole()
{
if (!consoleBuffer)
return;
//copy everything from consoleBuffer into a char*
char *buffer = malloc(1024+size+1);
#if 0
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = gmtime ( &rawtime );
char dateBuffer[128];
sprintf(dateBuffer, asctime(timeinfo));
for (int i = 0; i < strlen(dateBuffer); ++i)
{
if (dateBuffer[i] == ' ' || dateBuffer[i] == ':')
dateBuffer[i] = '_';
}
printf("formatted time: %s\n", dateBuffer);
sprintf( buffer, "mailto:iphonesupport@idsoftware.com?subject=DoomClassicReport%s&body=%s", dateBuffer, consoleBuffer);
#else
sprintf( buffer, "mailto:iphonesupport@idsoftware.com?subject=DoomClassicReport&body=%s", consoleBuffer);
#endif
//call the mail app
NSURL *url = [[NSURL alloc] initWithString:[[NSString alloc] initWithCString:buffer]];
[[UIApplication sharedApplication] openURL:url];
free(buffer);
}
File diff suppressed because it is too large Load Diff
+522
View File
@@ -0,0 +1,522 @@
/*
Copyright (C) 2004-2005 Michael Liebscher <johnnycanuck@users.sourceforge.net>
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
doom
----
modifications to config.h
don't add the d_ipxgate.c file
don't add d_server.c file
don't add mmus2mid.[ch] files
don't add w_memcache.c, use w_mmap.c instead
added new SDL_opengl.h, changed code files from <SDL_opengl.h> to "SDL_opengl.h"
commented out #include <SDL.h>
Commented out the static on D_DoomMainSetup();
Add define HAVE_CONFIG_H to the target settings
Add define _DEBUG
#if around uses of GL_TEXTURE_RESIDENT in gl_texture.c
// JDC: not in GLES, not needed since it is the default condition glDisable(GL_POLYGON_SMOOTH);
// JDC #define USE_VERTEX_ARRAYS in gl_main.c
add the iphoneRotateForLandscape calls
static JDC removed short consistancy[MAXPLAYERS][BACKUPTICS];
// JDC glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// JDC glDisableClientState(GL_VERTEX_ARRAY);
#ifdef IPHONE // JDC
#define MAX_SCREENWIDTH 480
#define MAX_SCREENHEIGHT 320
#else
code notes
----------
all the fixed point mess
goofy polar clipping
path traverse should have used a BSP tree
rename vldoor_e close and open enums to not conflict with unistd
opengl_error_break
WiFi internet play
---------
voip instead of key chat
extra asset work
----------------------
perfect weapon scales
demo of each level?
additive art separation -- attacks, buttons, etc
tuned option
------------
last kill / last secret / last item messages
more achievements in general
better item pickup sounds
scale blood spray with distance
easier item pickup
crosshair
better movement clipping to walls
better key pickup sound
textured automap
better low health warning
better feedback on where bullets hit
better bob cycle when going down stairs
map item should also show all monsters and items to make it cool
barrels explode easier
wave FOV when berzerk
pistol shots are useless in deathmatch
show opponent / enemy health when attacking them?
cpu optimizations
-------------
convert to 16 bit vertex data
void gld_BindPatch(GLTexture *gltexture, int cm) is expensive
fixedMul / fixedDiv in asm (negligable performance gain)
atlas all of the non-character items (and bodies?)
remove the pitch changes from sound?
gpu optimizations
------------------
use fog instead of full screen blend for damage/pickup flash (3 msec on IP3G)
pvrtc walls and floors
art needed
----------
new map / menu button
awards on medals
icon
thumbsticks
settings gears
must do
-------
pause music in menu
timelimit carry over to new levels
server going to demo causes client to crash
fixed 10/13/09
--------------
recover from sound interruption
caps for slider bar text
reset defaults doesn't reset reverse-landscape
pause music when going to the menu
reset timelimit each level on deathmatch
disable demos, new game, and web site during multiplayer
fixed
-----
automap / menu button actions with multi-thread
capitalized "game saved."
Added punctuation to "Ned a blue keycard", etc
added "defaults" button in options
todo
----
don't accept fire from an owned touch?
text scaling in buttons isn't perfect
better view angle transport
audio bugs
rotor control shouldn't be dimmed
volume button hack?
change background color for networking
tapping weapon change to cycle
is openal thread safe? we issue touch clicks from asynctic
disable multiplayer button if no ethernet addresses found
touch latching issues
add one tic latency to server?
select new game while in netgame
multiplayer arrow colors
remove players with stale joins
rocket explosion offset
texture preload status bar stuff and blood / impacts
stereo panning for headphones
directly build 16 bit textures instead of translating from 32 bit
texture wrap seam after end of e3m8
sliders should be touch-latch controls
respawn flash sounds sometimes not playing?
don't allow starting on secret levels
loaded savegame spot on different level didn't get view height on first frame
use graphic?
don't ever close doors with auto-use
separators in map select
rotor speed adjust
check all powerup effects
remove uses of prboom.wad?
check patch outlining code
flash all controls on initial level load
touch sounds when cancelling demo playback
play sound on respawn
catch memory warning, purge textures
use wad sound data
stop sound cleanly
entire gun doesn't get fullbright with muzzle flash
require four touches in line for console
restartable pwad interface
somewhat normal based lighting on walls
help menu
visual tilt indicator
tilt draw the turnstick when active
console
*/
#include "../doomiphone.h"
cvar_t *skill;
cvar_t *episode;
cvar_t *controlScheme;
cvar_t *stickTurn;
cvar_t *stickMove;
cvar_t *stickDeadBand;
cvar_t *rotorTurn;
cvar_t *tiltTurn;
cvar_t *tiltMove;
cvar_t *tiltDeadBand;
cvar_t *tiltAverages;
cvar_t *miniNet;
cvar_t *music;
cvar_t *showTilt;
cvar_t *showTime;
cvar_t *showNet;
cvar_t *showSound;
cvar_t *cropSprites;
cvar_t *revLand;
cvar_t *mapScale;
cvar_t *drawControls;
cvar_t *autoUse;
cvar_t *statusBar;
cvar_t *touchClick;
cvar_t *messages;
cvar_t *timeLimit;
cvar_t *fragLimit;
cvar_t *mpDeathmatch;
cvar_t *mpSkill;
cvar_t *mpDataset;
cvar_t *mpEpisode;
cvar_t *mpMap;
cvar_t *noBlend;
cvar_t *glfinish;
cvar_t *mapSelectY;
cvar_t *throttle;
cvar_t *centerSticks;
cvar_t *rampTurn;
cvar_t *netBuffer;
#define VERSION_BCONFIG ( 0x89490000 + sizeof( huds ) + sizeof( playState ) )
void Sys_Error( const char *format, ... )
{
va_list argptr;
char string[ 1024 ];
va_start( argptr, format );
(void)vsnprintf( string, sizeof( string ), format, argptr );
va_end( argptr );
fprintf( stderr, "Error: %s\n", string );
_exit( 1 );
}
#define plyr (players+consoleplayer) /* the console player */
void Give_f() {
plyr->armorpoints = idfa_armor; // Ty 03/09/98 - deh
plyr->armortype = idfa_armor_class; // Ty 03/09/98 - deh
// You can't own weapons that aren't in the game // phares 02/27/98
for (int i=0;i<NUMWEAPONS;i++)
if (!(((i == wp_plasma || i == wp_bfg) && gamemode == shareware) ||
(i == wp_supershotgun && gamemode != commercial)))
plyr->weaponowned[i] = true;
for (int i=0;i<NUMAMMO;i++)
if (i!=am_cell || gamemode!=shareware)
plyr->ammo[i] = plyr->maxammo[i];
plyr->message = s_STSTR_FAADDED;
for (int i=0;i<NUMCARDS;i++)
if (!plyr->cards[i]) // only print message if at least one key added
{ // however, caller may overwrite message anyway
plyr->cards[i] = true;
}
}
void God_f() {
plyr->cheats ^= CF_GODMODE;
if (plyr->cheats & CF_GODMODE)
{
if (plyr->mo)
plyr->mo->health = god_health; // Ty 03/09/98 - deh
plyr->health = god_health;
plyr->message = s_STSTR_DQDON; // Ty 03/27/98 - externalized
}
else
plyr->message = s_STSTR_DQDOFF; // Ty 03/27/98 - externalized
}
void ResetMaps_f() {
playState.numMapStats = 0;
memset( playState.mapStats, 0, sizeof( playState.mapStats ) );
}
/*
==================
iphoneStartup
==================
*/
struct addrinfo *addrinfoHead;
void D_DoomMainSetup();
void iphoneStartup() {
int start = SysIphoneMilliseconds();
// print networking information
ReportNetworkInterfaces();
// microseconds will be plenty random for playerID and localGameID
playerID = localGameID = SysIphoneMicroseconds();
InitImmediateModeGL();
// init OpenAL before pak file, so the pak file can
// make all the al static buffers
Sound_Init();
// get our new-style pak file
char pakFile[1024];
sprintf( pakFile, "%s/base/base.iPack", SysIphoneGetAppDir() );
PK_Init( pakFile );
// register console commands
Cmd_AddCommand( "listcvars", Cvar_List_f );
Cmd_AddCommand( "resetcvars", Cvar_Reset_f );
Cmd_AddCommand( "resetmaps", ResetMaps_f );
Cmd_AddCommand( "listcmds", Cmd_ListCommands_f );
Cmd_AddCommand( "give", Give_f );
Cmd_AddCommand( "god", God_f );
Cmd_AddCommand( "mail", EmailConsole ); //gsh, mails the console to id
// register console variables
Cvar_Get( "version", va( "%3.1f %s %s", DOOM_IPHONE_VERSION, __DATE__, __TIME__ ), 0 );
skill = Cvar_Get( "skill", "1", CVAR_ARCHIVE );
episode = Cvar_Get( "episode", "0", CVAR_ARCHIVE );
controlScheme = Cvar_Get( "controlScheme", "0", CVAR_ARCHIVE );
stickTurn = Cvar_Get( "stickTurn", "128", CVAR_ARCHIVE );
stickMove = Cvar_Get( "stickMove", "128", CVAR_ARCHIVE );
stickDeadBand = Cvar_Get( "stickDeadBand", "0.2", CVAR_ARCHIVE );
rotorTurn = Cvar_Get( "rotorTurn", "50000", CVAR_ARCHIVE );
tiltTurn = Cvar_Get( "tiltTurn", "0", CVAR_ARCHIVE );
tiltMove = Cvar_Get( "tiltMove", "0", CVAR_ARCHIVE );
tiltDeadBand = Cvar_Get( "tiltDeadBand", "0.08", CVAR_ARCHIVE );
tiltAverages = Cvar_Get( "tiltAverages", "3", CVAR_ARCHIVE );
centerSticks = Cvar_Get( "centerSticks", "1", CVAR_ARCHIVE );
rampTurn = Cvar_Get( "rampTurn", "1", CVAR_ARCHIVE );
music = Cvar_Get( "music", "1", CVAR_ARCHIVE );
cropSprites = Cvar_Get( "cropSprites", "1", 0 );
revLand = Cvar_Get( "revLand", "0", CVAR_ARCHIVE );
mapScale = Cvar_Get( "mapScale", "10", CVAR_ARCHIVE );
drawControls = Cvar_Get( "drawControls", "1", CVAR_ARCHIVE );
autoUse = Cvar_Get( "autoUse", "0", CVAR_ARCHIVE );
statusBar = Cvar_Get( "statusBar", "1", CVAR_ARCHIVE );
touchClick = Cvar_Get( "touchClick", "0.15", CVAR_ARCHIVE );
messages = Cvar_Get( "messages", "1", CVAR_ARCHIVE );
mapSelectY = Cvar_Get( "mapSelectY", "0", CVAR_ARCHIVE );
miniNet = Cvar_Get( "miniNet", "1", CVAR_ARCHIVE );
// multiplayer setup
timeLimit = Cvar_Get( "timeLimit", "0", CVAR_ARCHIVE );
fragLimit = Cvar_Get( "fragLimit", "5", CVAR_ARCHIVE );
mpDeathmatch = Cvar_Get( "mpDeathmatch", "0", CVAR_ARCHIVE );
mpDataset = Cvar_Get( "mpDataset", "0", CVAR_ARCHIVE );
mpEpisode = Cvar_Get( "mpEpisode", "1", CVAR_ARCHIVE );
mpSkill = Cvar_Get( "mpSkill", "1", CVAR_ARCHIVE );
mpMap = Cvar_Get( "mpMap", "1", CVAR_ARCHIVE );
// debug tools
showTilt = Cvar_Get( "showTilt", "-1", 0 );
showTime = Cvar_Get( "showTime", "0", 0 );
showNet = Cvar_Get( "showNet", "0", 0 );
showSound = Cvar_Get( "showSound", "0", 0 );
noBlend = Cvar_Get( "noBlend", "0", 0 ); // disable the damae blends for screenshots
glfinish = Cvar_Get( "glfinish", "0", 0 );
throttle = Cvar_Get( "throttle", "1", 0 ); // network packet throttle enable
netBuffer = Cvar_Get( "netBuffer", "4", 0 ); // max tics to buffer ahead
// load the archived cvars
Cmd_ExecuteFile( va( "%s/config.cfg", SysIphoneGetDocDir() ) );
// make sure volume changes and incoming calls draw the right orientation
SysIPhoneSetUIKitOrientation( revLand->value );
// start the intro music if it wasn't disabled with the music cvar
iphonePlayMusic( "intro" );
// iphonePlayMusic( "e1m1" );
// these should get overwritten by the config loading
memset( &playState, 0, sizeof( playState ) );
playState.map.skill = 1;
playState.map.episode = 1;
playState.map.map = 1;
HudSetForScheme( 0 );
// load the binary config file
FILE *f = fopen( va( "%s/binaryConfig.bin", SysIphoneGetDocDir() ), "rb" );
if ( f ) {
int version;
version = 0;
fread( &version, 1, sizeof( version ), f );
if ( version != VERSION_BCONFIG ) {
Com_Printf( "Binary config file bad version.\n" );
} else {
fread( &playState, 1, sizeof( playState ), f );
fread( &huds, 1, sizeof( huds ), f );
version = 0;
fread( &version, 1, sizeof( version ), f );
if ( version != VERSION_BCONFIG ) {
Com_Error( "Binary config file bad trailing version.\n" );
}
}
fclose( f );
}
Com_Printf( "startup time: %i msec\n", SysIphoneMilliseconds() - start );
start = SysIphoneMilliseconds();
// the texnums might have been different in the savegame
HudSetTexnums();
arialFontTexture = PK_FindTexture( "iphone/arialImageLAL.tga" );
Com_Printf( "preloadBeforePlay(): %i msec\n", SysIphoneMilliseconds() - start );
Com_Printf( "---------- D_DoomMain ----------\n" );
D_DoomMainSetup();
// put savegames here
strcpy( basesavegame, SysIphoneGetDocDir() );
// prBoom seems to draw the static pic screens without setting up 2D, causing
// a bad first frame
iphoneSet2D();
menuState = IPM_MAIN;
#if 0
// jump right to the save spot for debugging
ResumeGame();
#endif
}
/*
==================
iphoneShutdown
Write out configs and save the game at this position
==================
*/
void iphoneShutdown() {
FILE *fp;
char path[1024];
cvar_t *var;
char buffer[1024];
// write the ascii config file
snprintf( path, sizeof( path ), "%s/config.cfg", SysIphoneGetDocDir() );
fp = fopen( path, "w" );
if( ! fp ) {
Com_Printf( "Could not write config.cfg.\n" );
return;
}
// write out commands to set the archived cvars
for( var = cvar_vars ; var ; var = var->next ) {
if( var->flags & CVAR_ARCHIVE ) {
snprintf( buffer, sizeof( buffer ), "%s %s\n", var->name, var->string );
fprintf( fp, "%s", buffer );
Com_Printf( "%s", buffer );
}
}
fclose( fp );
// write the binary config file
FILE *f = fopen( va( "%s/binaryConfig.bin", SysIphoneGetDocDir() ), "wb" );
if ( !f ) {
Com_Printf( "Could not write binaryConfig.cfg.\n" );
return;
}
int version = VERSION_BCONFIG;
fwrite( &version, 1, sizeof( version ), f );
fwrite( &playState, 1, sizeof( playState ), f );
fwrite( &huds, 1, sizeof( huds ), f );
fwrite( &version, 1, sizeof( version ), f );
fclose( f );
// write the Doom savegame, unless no game level
// was actually started on this app invokation, or we
// are at an intermission / finale, or we are dead
if ( levelHasBeenLoaded && !netgame && gamestate == GS_LEVEL
&& players[consoleplayer].playerstate != PST_DEAD ) {
// let the game thread perform a savegame, since it
// would be unsafe to do it in this thread
saveOnExitState = 1;
while( saveOnExitState != 2 ) {
sem_post( ticSemaphore );
usleep( 10000 );
}
}
// not sure if we should do this, or let UIKit exit...
exit( 0 );
}
+443
View File
@@ -0,0 +1,443 @@
/*
* iphone_mapSelect.c
* doom
*
* Created by John Carmack on 4/19/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
typedef struct {
int dataset;
int episode;
int map;
const char *name;
} mapData_t;
mapData_t mapData[] = {
{ 0, 1, 1, "E1M1: Hangar" },
{ 0, 1, 2, "E1M2: Nuclear Plant" },
{ 0, 1, 3, "E1M3: Toxin Refinery" },
{ 0, 1, 4, "E1M4: Command Control" },
{ 0, 1, 5, "E1M5: Phobos Lab" },
{ 0, 1, 6, "E1M6: Central Processing" },
{ 0, 1, 7, "E1M7: Computer Station" },
{ 0, 1, 8, "E1M8: Phobos Anomaly" },
{ 0, 1, 9, "E1M9: Military Base" },
{ 0, 2, 1, "E2M1: Deimos Anomaly" },
{ 0, 2, 2, "E2M2: Containment Area" },
{ 0, 2, 3, "E2M3: Refinery" },
{ 0, 2, 4, "E2M4: Deimos Lab" },
{ 0, 2, 5, "E2M5: Command Center" },
{ 0, 2, 6, "E2M6: Halls of the Damned" },
{ 0, 2, 7, "E2M7: Spawning Vats" },
{ 0, 2, 8, "E2M8: Tower of Babel" },
{ 0, 2, 9, "E2M9: Fortress of Mystery" },
{ 0, 3, 1, "E3M1: Hell Keep" },
{ 0, 3, 2, "E3M2: Slough of Despair" },
{ 0, 3, 3, "E3M3: Pandemonium" },
{ 0, 3, 4, "E3M4: House of Pain" },
{ 0, 3, 5, "E3M5: Unholy Cathedral" },
{ 0, 3, 6, "E3M6: Mt. Erebus" },
{ 0, 3, 7, "E3M7: Limbo" },
{ 0, 3, 8, "E3M8: Dis" },
{ 0, 3, 9, "E3M9: Warrens" },
{ 0, 4, 1, "E4M1: Hell Beneath" },
{ 0, 4, 2, "E4M2: Perfect Hatred" },
{ 0, 4, 3, "E4M3: Sever The Wicked" },
{ 0, 4, 4, "E4M4: Unruly Evil" },
{ 0, 4, 5, "E4M5: They Will Repent" },
{ 0, 4, 6, "E4M6: Against Thee Wickedly" },
{ 0, 4, 7, "E4M7: And Hell Followed" },
{ 0, 4, 8, "E4M8: Unto The Cruel" },
{ 0, 4, 9, "E4M9: Fear" },
#if 0
{ 0, 0, 0, "level 1: entryway" },
{ 0, 0, 0, "level 2: underhalls" },
{ 0, 0, 0, "level 3: the gantlet" },
{ 0, 0, 0, "level 4: the focus" },
{ 0, 0, 0, "level 5: the waste tunnels" },
{ 0, 0, 0, "level 6: the crusher" },
{ 0, 0, 0, "level 7: dead simple" },
{ 0, 0, 0, "level 8: tricks and traps" },
{ 0, 0, 0, "level 9: the pit" },
{ 0, 0, 0, "level 10: refueling base" },
{ 0, 0, 0, "level 11: 'o' of destruction!" },
{ 0, 0, 0, "level 12: the factory" },
{ 0, 0, 0, "level 13: downtown" },
{ 0, 0, 0, "level 14: the inmost dens" },
{ 0, 0, 0, "level 15: industrial zone" },
{ 0, 0, 0, "level 16: suburbs" },
{ 0, 0, 0, "level 17: tenements" },
{ 0, 0, 0, "level 18: the courtyard" },
{ 0, 0, 0, "level 19: the citadel" },
{ 0, 0, 0, "level 20: gotcha!" },
{ 0, 0, 0, "level 21: nirvana" },
{ 0, 0, 0, "level 22: the catacombs" },
{ 0, 0, 0, "level 23: barrels o' fun" },
{ 0, 0, 0, "level 24: the chasm" },
{ 0, 0, 0, "level 25: bloodfalls" },
{ 0, 0, 0, "level 26: the abandoned mines" },
{ 0, 0, 0, "level 27: monster condo" },
{ 0, 0, 0, "level 28: the spirit world" },
{ 0, 0, 0, "level 29: the living end" },
{ 0, 0, 0, "level 30: icon of sin" },
{ 0, 0, 0, "level 31: wolfenstein" },
{ 0, 0, 0, "level 32: grosse" },
{ 0, 0, 0, "level 1: congo" },
{ 0, 0, 0, "level 2: well of souls" },
{ 0, 0, 0, "level 3: aztec" },
{ 0, 0, 0, "level 4: caged" },
{ 0, 0, 0, "level 5: ghost town" },
{ 0, 0, 0, "level 6: baron's lair" },
{ 0, 0, 0, "level 7: caughtyard" },
{ 0, 0, 0, "level 8: realm" },
{ 0, 0, 0, "level 9: abattoire" },
{ 0, 0, 0, "level 10: onslaught" },
{ 0, 0, 0, "level 11: hunted" },
{ 0, 0, 0, "level 12: speed" },
{ 0, 0, 0, "level 13: the crypt" },
{ 0, 0, 0, "level 14: genesis" },
{ 0, 0, 0, "level 15: the twilight" },
{ 0, 0, 0, "level 16: the omen" },
{ 0, 0, 0, "level 17: compound" },
{ 0, 0, 0, "level 18: neurosphere" },
{ 0, 0, 0, "level 19: nme" },
{ 0, 0, 0, "level 20: the death domain" },
{ 0, 0, 0, "level 21: slayer" },
{ 0, 0, 0, "level 22: impossible mission" },
{ 0, 0, 0, "level 23: tombstone" },
{ 0, 0, 0, "level 24: the final frontier" },
{ 0, 0, 0, "level 25: the temple of darkness" },
{ 0, 0, 0, "level 26: bunker" },
{ 0, 0, 0, "level 27: anti-christ" },
{ 0, 0, 0, "level 28: the sewers" },
{ 0, 0, 0, "level 29: odyssey of noises" },
{ 0, 0, 0, "level 30: the gateway of hell" },
{ 0, 0, 0, "level 31: cyberden" },
{ 0, 0, 0, "level 32: go 2 it" },
{ 0, 0, 0, "level 1: system control" },
{ 0, 0, 0, "level 2: human bbq" },
{ 0, 0, 0, "level 3: power control" },
{ 0, 0, 0, "level 4: wormhole" },
{ 0, 0, 0, "level 5: hanger" },
{ 0, 0, 0, "level 6: open season" },
{ 0, 0, 0, "level 7: prison" },
{ 0, 0, 0, "level 8: metal" },
{ 0, 0, 0, "level 9: stronghold" },
{ 0, 0, 0, "level 10: redemption" },
{ 0, 0, 0, "level 11: storage facility" },
{ 0, 0, 0, "level 12: crater" },
{ 0, 0, 0, "level 13: nukage processing" },
{ 0, 0, 0, "level 14: steel works" },
{ 0, 0, 0, "level 15: dead zone" },
{ 0, 0, 0, "level 16: deepest reaches" },
{ 0, 0, 0, "level 17: processing area" },
{ 0, 0, 0, "level 18: mill" },
{ 0, 0, 0, "level 19: shipping/respawning" },
{ 0, 0, 0, "level 20: central processing" },
{ 0, 0, 0, "level 21: administration center" },
{ 0, 0, 0, "level 22: habitat" },
{ 0, 0, 0, "level 23: lunar mining project" },
{ 0, 0, 0, "level 24: quarry" },
{ 0, 0, 0, "level 25: baron's den" },
{ 0, 0, 0, "level 26: ballistyx" },
{ 0, 0, 0, "level 27: mount pain" },
{ 0, 0, 0, "level 28: heck" },
{ 0, 0, 0, "level 29: river styx" },
{ 0, 0, 0, "level 30: last call" },
{ 0, 0, 0, "level 31: pharaoh" },
{ 0, 0, 0, "level 32: caribbean" },
#endif
{ 0, 0, 0, NULL }
};
/*
===================
FindMapStats
Finds or creats a mapStats_t structure for the given level.
This can return NULL if the entire array is filled up, which may
happen when people have an absurd number of downloaded levels.
===================
*/
mapStats_t *FindMapStats( int dataset, int episode, int map, boolean create ) {
for ( int i = 0 ; i < playState.numMapStats ; i++ ) {
mapStats_t *ms = &playState.mapStats[i];
if ( ms->dataset == dataset && ms->episode == episode && ms->map == map ) {
return ms;
}
}
if ( playState.numMapStats == MAX_MAPS ) {
// all full.
return NULL;
}
if ( !create ) {
return NULL;
}
mapStats_t *cms = &playState.mapStats[playState.numMapStats];
cms->dataset = dataset;
cms->episode = episode;
cms->map = map;
playState.numMapStats++;
return cms;
}
/*
==================
FindMapName
episodes and maps are one base
==================
*/
const char *FindMapName( int dataset, int episode, int map ) {
for ( mapData_t *md = mapData ; md->name ; md++ ) {
if ( md->dataset == dataset && md->episode == episode && md->map == map ) {
return md->name;
}
}
return "UNKNOWN MAP NAME";
}
/*
==================
iphoneMapSelectMenu
Skills are zero based:
sk_baby=0,
sk_easy,
sk_medium,
sk_hard,
sk_nightmare=4
episodes are one base
==================
*/
static const int MAP_ROW_HEIGHT = 52;
ibutton_t btnSkills[4];
int dragVelocity;
ibutton_t dragScroll;
mapData_t *selectedMap;
int totalDrag; // for determining if a release will activate the level
boolean iphoneMapSelectMenu( mapStart_t *map ) {
static int prevDragY;
if ( !dragScroll.x ) {
// first initialization
dragScroll.drawWidth = 480 - 80 - 64;
dragScroll.drawHeight = 320;
dragScroll.x = 64 + dragScroll.drawWidth / 2;
dragScroll.y = 160;
static char * skillNames[4] = {
"iphone/skill_easy.tga",
"iphone/skill_normal.tga",
"iphone/skill_hard.tga",
"iphone/skill_nightmare.tga" }; // not really "nightmare" skill since easy is "baby"
for ( int i = 0 ; i < 4 ; i++ ) {
SetButtonPics( &btnSkills[i], skillNames[i], "", 400, i*80 );
}
}
// check for drag-scrolling
if ( dragScroll.touch ) {
if ( dragScroll.touch->y != prevDragY ) {
dragVelocity = dragScroll.touch->y - prevDragY;
prevDragY = dragScroll.touch->y;
totalDrag += abs( dragVelocity );
}
}
Cvar_SetValue( mapSelectY->name, mapSelectY->value - dragVelocity );
// decay the dragVelocity
for ( int i = 0 ; i < 2 ; i++ ) {
if ( dragVelocity < 0 ) {
dragVelocity++;
} else if ( dragVelocity > 0 ) {
dragVelocity--;
}
}
int iskill = (int)skill->value;
if ( iskill < 0 ) {
iskill = 0;
Cvar_SetValue( skill->name, iskill );
} else if ( iskill >= MAX_SKILLS ) {
iskill = MAX_SKILLS-1;
Cvar_SetValue( skill->name, iskill );
}
// snap back to bounds if dragging past end
if ( mapSelectY->value < 0 ) {
Cvar_SetValue( mapSelectY->name, 0 );
}
int numMaps = 0;
for ( mapData_t *map = mapData ; map->name != NULL ; map++ ) {
numMaps++;
}
if ( mapSelectY->value > numMaps * MAP_ROW_HEIGHT - 320 ) {
Cvar_SetValue( mapSelectY->name, numMaps * MAP_ROW_HEIGHT - 320 );
}
// scrolling display of levels
int y = -mapSelectY->value;
mapData_t *startMap = NULL;
for ( mapData_t *map = mapData ; map->name != NULL ; map++ ) {
if ( y > -64 && y < 320 ) {
// find the mapStat_t for this map, if it has ever been started
int completionFlags = 0;
mapStats_t *ms = FindMapStats( map->dataset, map->episode, map->map, false );
if ( ms ) {
completionFlags = ms->completionFlags[iskill];
}
// if we aren't already dragging, check for a touch on a map button
if ( !dragScroll.touch ) {
touch_t *touch = TouchInBounds( 120, y, 400-128, 48 );
if ( touch ) {
Sound_StartLocalSound( "iphone/bdown_01.wav" );
dragScroll.touch = touch;
prevDragY = touch->y;
touch->controlOwner = &dragScroll;
selectedMap = map;
}
}
// color background based on selected / entered / completed state
if ( selectedMap == map ) {
glColor4f( 1,1,1,1 ); // launch if released
} else if ( completionFlags & MF_COMPLETED ) {
glColor4f( 0.2, 0.5, 0.2, 1 );
} else if ( completionFlags & MF_TRIED ) {
glColor4f( 0.5, 0.2, 0.2, 1 );
} else {
glColor4f( 0.4, 0.4, 0.4, 1 );
}
// use -1 x to avoid a texture wrap seam
PK_StretchTexture( PK_FindTexture( "iphone/long_string_box.tga" ), 110, y, 400-114, 48 );
glColor4f( 1,1,1,1 );
// draw the text
float w = StringFontWidth( map->name );
float fontScale = 0.75;
if ( w > 360 ) {
fontScale *= ( 360 / w );
}
iphoneDrawText( 120, y+32, fontScale, map->name );
// add the awards
if ( completionFlags & MF_KILLS ) {
PK_DrawTexture( PK_FindTexture( "iphone/kills.tga" ), 80, y+4 );
}
if ( completionFlags & MF_TIME ) {
PK_DrawTexture( PK_FindTexture( "iphone/par.tga" ), 40, y+4 );
}
if ( completionFlags & MF_SECRETS ) {
PK_DrawTexture( PK_FindTexture( "iphone/secrets.tga" ), 0, y+4 );
}
}
y += MAP_ROW_HEIGHT;
}
if ( numTouches == 0 ) {
totalDrag = 0;
}
// draw the skill level
for ( int i = 0 ; i < 4 ; i++ ) {
if ( i == iskill ) {
btnSkills[i].buttonFlags = 0;
} else {
btnSkills[i].buttonFlags = BF_DIMMED;
}
if ( HandleButton( &btnSkills[i] ) ) {
Cvar_SetValue( skill->name, i );
}
}
glColor4f( 1, 1, 1, 1 );
// handle back button before checking for touches in the awards area
if ( BackButton() ) {
map->map = -1;
return true;
}
// if we aren't already dragging, check for a touch anywhere outside the skill buttons
if ( !dragScroll.touch ) {
touch_t *touch = TouchInBounds( 0, 0, 400-128, 320 );
if ( touch ) {
dragScroll.touch = touch;
prevDragY = touch->y;
touch->controlOwner = &dragScroll;
selectedMap = NULL;
Sound_StartLocalSoundAtVolume( "iphone/controller_down_01_SILENCE.wav", touchClick->value );
}
} else {
// if we dragged more than a few pixels, don't launch the level
if ( totalDrag > 8 ) {
selectedMap = NULL;
}
if ( !dragScroll.touch->down ) {
// lifted finger
dragScroll.touch = NULL;
if ( selectedMap ) {
Sound_StartLocalSound( "iphone/baction_01.wav" );
startMap = selectedMap;
selectedMap = NULL;
} else {
Sound_StartLocalSoundAtVolume( "iphone/controller_up_01_SILENCE.wav", touchClick->value );
}
}
}
if ( !startMap ) {
return false;
}
map->skill = iskill;
map->episode = startMap->episode;
map->map = startMap->map;
map->dataset = startMap->dataset;
return true;
}
+958
View File
@@ -0,0 +1,958 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
// Only one game can be set up at a time on a given wireless segment, although
// several independent games can be played.
// If a valid setupPacket has arrived in the last second, that will be the
// displayed game, otherwise the local system starts sending out setupPackets.
packetSetup_t setupPacket;
int setupPacketFrameNum;
int localGameID; // change every time we take over as the sender of setupPackets
boolean levelHasBeenLoaded; // determines if "resume game" does a loadGame and exiting does a saveGame
menuState_t menuState;
color4_t highlightColor = { 128, 128, 128, 255 };
color4_t colorPressed = { 128, 128, 0, 255 };
void SetupEmptyNetGame();
void R_Draw_Blend( int x, int y, int w, int h, color4_t c ) {
glDisable( GL_TEXTURE_2D );
glColor4ubv( c );
glBegin( GL_QUADS );
glVertex2i( x, y );
glVertex2i( x+w, y );
glVertex2i( x+w, y+h );
glVertex2i( x, y+h );
glEnd();
glColor3f( 1, 1, 1 );
glEnable( GL_TEXTURE_2D );
}
void R_Draw_Fill( int x, int y, int w, int h, color3_t c ) {
// as of 2.2 OS, doing a clear with a small scissor rect is MUCH slower
// than drawing geometry
color4_t c4;
c4[0] = c[0];
c4[1] = c[1];
c4[2] = c[2];
c4[3] = 255;
R_Draw_Blend( x, y, w, h, c4 );
}
/*
==================
iphoneSlider
Returns true if modified
==================
*/
#define SF_DISABLED 1 // grey out, don't respond to touches
#define SF_INTEGER 2 // don't add percent
boolean iphoneSlider( int x, int y, int w, int h, const char *title, cvar_t *cvar,
float min, float max, int sliderFlags ) {
float value = cvar->value;
char str[80];
float f = ( value - min ) / ( max - min );
if ( f < 0 ) {
f = 0;
}
if ( f > 1 ) {
f = 1;
}
// draw the background
PK_StretchTexture( PK_FindTexture( "iphone/slider_shadow.tga" ), x, y, w, h );
// draw the current range
PK_BindTexture( PK_FindTexture( "iphone/slider_bar.tga" ) );
#if 0
// proportional
glBegin( GL_QUADS );
glTexCoord2f( 0.0f, 0.0f ); glVertex2i( x, y );
glTexCoord2f( f, 0.0f ); glVertex2i( x+w*f, y );
glTexCoord2f( f, 1.0f ); glVertex2i( x+w*f, y+h );
glTexCoord2f( 0.0f, 1.0f ); glVertex2i( x, y+h );
glEnd();
#else
// dragging thumb
glBegin( GL_QUADS );
glTexCoord2f( 0.0f, 0.0f ); glVertex2i( x+w*f-8, y );
glTexCoord2f( 1.0f, 0.0f ); glVertex2i( x+w*f+8, y );
glTexCoord2f( 1.0f, 1.0f ); glVertex2i( x+w*f+8, y+h );
glTexCoord2f( 0.0f, 1.0f ); glVertex2i( x+w*f-8, y+h );
glEnd();
#endif
// draw the title and fraction
if ( sliderFlags & SF_INTEGER ) {
sprintf( str, "%s : %i", title, (int)value );
} else {
sprintf( str, "%s : %i%%", title, (int)(f*100+0.5) );
}
iphoneCenterText( x+ w/2, y+h-10, 0.75, str );
// check for touches
if ( numTouches > 0 && touches[0][0] >= x && touches[0][0] < x + w
&& touches[0][1] >= y && touches[0][1] < y+ h ) {
float newValue;
float delta;
f = (float)( touches[0][0] - x ) / w;
if ( sliderFlags & SF_INTEGER ) {
newValue = rint( min + f * ( max - min ) );
} else {
// round to tenths
f = (int)( ( f + 0.05 ) * 10 ) * 0.1f;
if ( f < 0 ) {
f = 0;
}
if ( f > 1.0 ) {
f = 1.0;
}
newValue = min + f * ( max - min );
}
delta = fabs( newValue - cvar->value );
if ( f == 0 && cvar->value == 0 ) {
// special case of disable-at-0
} else if ( delta > 0.01 ) {
Cvar_SetValue( cvar->name, newValue );
Sound_StartLocalSound( "iphone/slide_01.wav" );
return true;
}
return false;
}
return false;
}
/*
==================
BackButton
==================
*/
ibutton_t btnBack;
int BackButton() {
if ( !btnBack.texture ) {
SetButtonPicsAndSizes( &btnBack, "iphone/back_button.tga", "", 0, 0, 48, 48 );
}
return HandleButton( &btnBack );
}
void GetMoreLevels( int x, int y ) {
// directly to the app store for more levels
SysIPhoneOpenURL( "http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=304694876" );
}
/*
==================
iphoneMainMenu
==================
*/
ibutton_t btnResumeGame;
ibutton_t btnNewGame;
ibutton_t btnControls;
ibutton_t btnMultiplayer;
ibutton_t btnWWW;
ibutton_t btnDemo;
void iphoneMainMenu() {
if ( !btnResumeGame.texture ) {
// initial setup
SetButtonPics( &btnResumeGame, "iphone/resume_game.tga", "Resume Game", 16, 4 );
SetButtonPics( &btnNewGame, "iphone/new_game.tga", "New Game", 176, 4 );
SetButtonPics( &btnDemo, "iphone/demo.tga", "Demos", 336, 4 );
SetButtonPics( &btnMultiplayer, "iphone/multiplay.tga", "Multiplayer", 16, 168 );
SetButtonPics( &btnWWW, "iphone/website.tga", "Website", 176, 168 );
SetButtonPics( &btnControls, "iphone/controls.tga", "Options", 336, 168 );
}
if ( netgame ) {
// disable buttons if we are already in a netgame
btnNewGame.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
btnMultiplayer.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
btnWWW.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
btnDemo.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
}
if ( HandleButton( &btnResumeGame ) ) {
ResumeGame();
}
if ( HandleButton( &btnNewGame ) ) {
menuState = IPM_MAPS;
}
if ( HandleButton( &btnControls ) ) {
menuState = IPM_CONTROLS;
}
if ( !NetworkAvailable() ) {
// disable multiplayer if we don't have a good device
btnMultiplayer.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
} else if ( netgame ) {
// disable multiplayer if we are already in a netgame
btnMultiplayer.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
} else if ( NetworkServerAvailable() ) {
// blink the multiplayer button if a local server is available
btnMultiplayer.buttonFlags = BF_GLOW;
} else {
btnMultiplayer.buttonFlags = 0;
}
if ( HandleButton( &btnMultiplayer ) ) {
// get the address for the local service, which may
// start up a bluetooth personal area network
boolean serverResolved = ResolveNetworkServer( &netServer.address );
// open our socket now that the network interfaces have been configured
// Explicitly open on interface 1, which is en0. If bluetooth ever starts
// working better, we can handle multiple interfaces.
if ( gameSocket <= 0 ) {
gameSocket = UDPSocket( "en0", DOOM_PORT );
}
// get the address for the local service
if ( !serverResolved ) {
// nobody else is acting as a server, so start one here
RegisterGameService();
SetupEmptyNetGame();
}
menuState = IPM_MULTIPLAYER;
}
// draw the available interfaces over the blinking net button
if ( NetworkServerAvailable() ) {
iphoneCenterText( btnMultiplayer.x + btnMultiplayer.drawWidth / 2,
btnMultiplayer.y + btnMultiplayer.drawHeight/2, 0.75,
NetworkServerTransport() );
}
if ( HandleButton( &btnWWW ) ) {
// menuState = IPM_PACKET_TEST; // !@# debug
SysIPhoneOpenURL( "http://www.idsoftware.com/doomclassic/" );
}
if ( HandleButton( &btnDemo ) ) {
StartDemoGame( btnDemo.twoFingerPress );
}
if ( btnDemo.twoFingerPress ) {
strcpy( timeDemoResultString, "TIMEDEMO" );
}
// draw the timedemo results on top of the button
if ( timeDemoResultString[0] ) {
iphoneCenterText( btnDemo.x + btnDemo.drawWidth / 2, btnDemo.y + btnDemo.drawHeight/2, 0.75,
timeDemoResultString );
}
}
/*
==================
iphoneControlMenu
==================
*/
ibutton_t btnSchemes[4];
ibutton_t btnSettings;
ibutton_t btnMove;
void iphoneControlMenu() {
int i;
// iphoneCenterText( 240, 16, 0.75, __DATE__" "__TIME__ );
// 112 units between
if ( BackButton() ) {
menuState = IPM_MAIN;
}
if ( NewTextButton( &btnSettings, "Settings", 480-128, 0, 128, 48 ) ) {
menuState = IPM_OPTIONS;
}
if ( NewTextButton( &btnMove, "Move Controls", 48 + (480-(128+160+48))/2, 0, 160, 48 ) ) {
menuState = IPM_HUDEDIT;
}
if ( !btnSchemes[0].texture ) {
for ( int i = 0 ; i < 3 ; i++ ) {
SetButtonPicsAndSizes( &btnSchemes[i], va("iphone/config_%i.tga",i+1), "",
32 + (96+64) * i, 48 + ( 112 - 64 )*0.5, 96, 64 );
}
}
for ( i = 0 ; i < 3 ; i++ ) {
char str[128];
sprintf( str, "iphone/config_%i.tga", i+1 );
if ( i != controlScheme->value ) {
btnSchemes[i].buttonFlags = BF_DIMMED;
} else {
btnSchemes[i].buttonFlags = 0;
}
if ( HandleButton( &btnSchemes[i] ) ) {
Cvar_SetValue( controlScheme->name, i );
HudSetForScheme( i );
}
}
iphoneSlider( 20, 160, 440, 40, "Move stick size", stickMove, 64, 128, 0 );
iphoneSlider( 20, 210, 440, 40, "Turn stick size", stickTurn, 64, 128, 0 );
iphoneSlider( 20, 260, 440, 40, "Tilt move speed", tiltMove, 100, 300, 0 );
if ( tiltMove->value == 100 ) {
Cvar_SetValue( tiltMove->name, 0 );
}
if ( tiltMove->value ) {
Cvar_SetValue( tiltTurn->name, 0 );
}
#if 0
iphoneSlider( 20, 280, 440, 40, "Tilt turn speed", tiltTurn, 1500, 3500, 0 );
if ( tiltTurn->value == 1500 ) {
Cvar_SetValue( tiltTurn->name, 0 );
}
if ( tiltTurn->value ) {
Cvar_SetValue( tiltMove->name, 0 );
}
#endif
}
void SetupEmptyNetGame() {
Com_Printf( "SetupEmptyNetGame()\n" );
// no current setup packet, so initialize with this phone's default values
localGameID = SysIphoneMicroseconds();
memset( &setupPacket, 0, sizeof( setupPacket ) );
setupPacket.gameID = localGameID;
setupPacket.packetType = PACKET_VERSION_SETUP;
setupPacket.map.dataset = mpDataset->value;
setupPacket.map.episode = mpEpisode->value;
setupPacket.map.map = mpMap->value;
setupPacket.map.skill = mpSkill->value;
setupPacket.deathmatch = mpDeathmatch->value;
setupPacket.timelimit = timeLimit->value;
setupPacket.fraglimit = fragLimit->value;
setupPacket.playerID[0] = playerID;
}
/*
==================
SendJoinPacket
These will be sent to the server ever frame we are in the multiplayer menu.
==================
*/
void SendJoinPacket() {
packetJoin_t pj;
pj.packetType = PACKET_VERSION_JOIN;
pj.gameID = setupPacket.gameID;
pj.playerID = playerID;
int r = sendto( gameSocket, &pj, sizeof( pj ), 0,
&netServer.address, sizeof( netServer.address ) );
if ( r == -1 ) {
struct sockaddr_in *addr = (struct sockaddr_in *)&netServer.address;
byte *ip = (byte *)&addr->sin_addr;
Com_Printf( "UDP sendTo %i.%i.%i.%i failed: %s\n",
ip[0], ip[1], ip[2], ip[3], strerror( errno ) );
close( gameSocket );
gameSocket = -1;
}
}
/*
==================
iphoneMultiplayerMenu
==================
*/
ibutton_t btnCoop;
ibutton_t btnDeathmatch;
ibutton_t btnMap;
ibutton_t btnNetSettings;
typedef enum {
NM_MAIN,
NM_MAP_SELECT,
NM_OPTIONS
} netMenu_t;
netMenu_t netMenu;
void iphoneMultiplayerMenu() {
if ( gameSocket <= 0 ) {
// no socket, so no multiplayer
TerminateGameService(); // don't advertise for any more new players
setupPacket.gameID = 0; // stop sending packets
menuState = IPM_MAIN;
return;
}
boolean server = ( setupPacket.gameID == localGameID );
// different screen when selecting a map to play
if ( netMenu == NM_MAP_SELECT ) {
mapStart_t map;
if ( !iphoneMapSelectMenu( &map ) ) {
// haven't selected anything yet
return;
}
netMenu = NM_MAIN;
if ( map.map != -1 ) {
// selected something new, didn't hit the back arrow
setupPacket.map = map;
}
} else if ( netMenu == NM_OPTIONS ) {
Cvar_SetValue( fragLimit->name, setupPacket.fraglimit );
if ( iphoneSlider( 104, 64, 272, 40, "frag limit", fragLimit, 0, 20, SF_INTEGER ) ) {
if ( server ) {
setupPacket.fraglimit = fragLimit->value;
}
}
Cvar_SetValue( timeLimit->name, setupPacket.timelimit );
if ( iphoneSlider( 104, 64+56, 272, 40, "time limit", timeLimit, 0, 20, SF_INTEGER ) ) {
if ( server ) {
setupPacket.timelimit = timeLimit->value;
}
}
if ( BackButton() ) {
netMenu = NM_MAIN;
}
return;
}
if ( !btnDeathmatch.texture ) {
// initial setup
SetButtonPicsAndSizes( &btnDeathmatch, "iphone/deathmatch.tga", "Deathmatch", 4+48, 64, 96, 96 );
SetButtonPicsAndSizes( &btnCoop, "iphone/co-op.tga", "Cooperative", 480-148, 64, 96, 96 );
}
if ( BackButton() ) {
if ( server ) {
TerminateGameService(); // don't advertise for any more new players
setupPacket.gameID = 0; // stop sending packets
}
menuState = IPM_MAIN;
}
if ( !server ) {
// we aren't the server
// send our join packet every frame
SendJoinPacket();
if ( setupPacketFrameNum < iphoneFrameNum - 30 ) {
// haven't received a current server packet
char str[1024];
struct sockaddr_in *sin = (struct sockaddr_in *)&netServer.address;
byte *ip = (byte *)&sin->sin_addr;
sprintf( str, "Joining server at %i.%i.%i.%i:%i\n", ip[0], ip[1], ip[2], ip[3],
ntohs( sin->sin_port ) );
iphoneCenterText( 240, 160, 0.75, str );
return;
}
} else {
// cull out any players that haven't given us a packet in a couple seconds
int now = SysIphoneMilliseconds();
for ( int i = 1 ; i < MAXPLAYERS ; i++ ) {
if ( setupPacket.playerID[i] && now - netPlayers[i].peer.lastPacketTime > 1000 ) {
printf( "Dropping player %i: last:%i now:%i\n", i, netPlayers[i].peer.lastPacketTime, now );
setupPacket.playerID[i] = 0;
}
}
}
// draw the level and allow clicking to change
Cvar_SetValue( mpDataset->name, setupPacket.map.dataset );
Cvar_SetValue( mpEpisode->name, setupPacket.map.episode );
Cvar_SetValue( mpMap->name, setupPacket.map.map );
Cvar_SetValue( mpSkill->name, setupPacket.map.skill );
// map select button / display
if ( NewTextButton( &btnMap, FindMapName( mpDataset->value, mpEpisode->value, mpMap->value ), 64, 0, 480-128, 48 ) ) {
if ( server ) {
// clients can't go into this menu
netMenu = NM_MAP_SELECT;
}
}
if ( setupPacket.deathmatch ) {
btnDeathmatch.buttonFlags = 0;
btnCoop.buttonFlags = BF_DIMMED;
} else {
btnDeathmatch.buttonFlags = BF_DIMMED;
btnCoop.buttonFlags = 0;
}
if ( HandleButton( &btnDeathmatch ) ) {
if ( server ) {
Cvar_SetValue( mpDeathmatch->name, 3 ); // weapons stay, items respawn rules
setupPacket.deathmatch = mpDeathmatch->value;
}
}
if ( HandleButton( &btnCoop ) ) {
if ( server ) {
Cvar_SetValue( mpDeathmatch->name, 0 );
setupPacket.deathmatch = mpDeathmatch->value;
}
}
if ( NewTextButton( &btnNetSettings, "Settings", 240-64, 64+24, 128, 48 ) ) {
netMenu = NM_OPTIONS;
}
for ( int i = 0 ; i < 4 ; i ++ ) {
int x = 45 + ( 64+45) * i;
int y = 64+128;
// FIXME: show proper player colors
byte color[4][4] = { { 0, 255, 0, 255 }, { 128, 128, 128, 255 }, { 128,64,0, 255 }, {255,0,0, 255 } };
glColor4ubv( color[i] );
PK_DrawTexture( PK_FindTexture( "iphone/multi_backdrop.tga" ), x, y );
glColor4f( 1, 1, 1, 1 );
if ( setupPacket.playerID[i] == playerID ) {
// bigger outline for your player slot
PK_StretchTexture( PK_FindTexture( "iphone/multi_frame.tga" ), x, y, 64, 64 );
}
// draw doom guy face
if ( setupPacket.playerID[i] != 0 ) {
PK_DrawTexture( PK_FindTexture( "iphone/multi_face.tga" ), x, y );
#if 0
// temp display IP address
byte *ip = (byte *)&setupPacket.address[i].sin_addr;
iphoneDrawText( x-16, (i&1) ? y+16 : y+48, 0.75, va("%i.%i.%i.%i", ip[0], ip[1], ip[2], ip[3] ) );
#endif
}
}
if ( server ) {
// flash a tiny pic when transmitting
if ( iphoneFrameNum & 1 ) {
glColor4f( 1,1,1,1 );
} else {
glColor4f( 0.5,0.5,0.5,1 );
}
iphoneCenterText( 470, 310, 0.75, "*" );
glColor4f( 1,1,1,1 );
}
if ( setupPacketFrameNum == iphoneFrameNum ) {
iphoneCenterText( 450, 310, 0.75, "*" );
}
// iphoneDrawText( 0, 310, 0.75, va("%i:%i", localGameID, setupPacket.gameID ) );
// only draw the start button if we have at least two players in game
int numPlayers = 0;
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
if ( setupPacket.playerID[i] != 0 ) {
numPlayers++;
}
}
if ( numPlayers > 1 ) {
if ( server ) {
static ibutton_t btnStart;
if ( NewTextButton( &btnStart, "Start game", 240-80, 320-48, 160, 48 ) ) {
setupPacket.startGame = 1;
StartNetGame();
TerminateGameService(); // don't advertise for any more new players
return;
}
} else {
iphoneCenterText( 240, 320-10, 0.75, "Waiting for server to start the game" );
}
} else {
byte *ip = (byte *)&gameSocketAddress.sin_addr;
iphoneCenterText( 240, 320-10, 0.75, va("Waiting for players on %i.%i.%i.%i",
ip[0], ip[1], ip[2], ip[3] ) );
}
}
static ibutton_t optionButtons[2][6];
static ibutton_t defaultsButton;
boolean OptionButton( int col, int row, const char *title ) {
assert( col >= 0 && col < 2 && row >= 0 && row < 6 );
return NewTextButton( &optionButtons[col][row], title, 10 + 235 * col, 64 + 50 * row, 225, 48 );
}
/*
==================
iphoneOptionsMenu
==================
*/
void iphoneOptionsMenu() {
if ( BackButton() ) {
menuState = IPM_CONTROLS;
}
boolean musicState = music->value;
if ( SysIPhoneOtherAudioIsPlaying() ) {
// music always off when ipod music is playing
musicState = false;
}
if ( NewTextButton( &defaultsButton, "Defaults", 240-225/2, 2, 225, 48 ) ) {
// reset all cvars except the reverse-landscape mode value
float value = revLand->value;
Cvar_Reset_f();
Cvar_SetValue( revLand->name, value );
HudSetForScheme(0);
iphoneStartMusic();
}
if ( OptionButton( 0, 0, autoUse->value ? "Auto use: ON" : "Auto use: OFF" ) ) {
Cvar_SetValue( autoUse->name, !autoUse->value );
}
if ( OptionButton( 0, 1, statusBar->value ? "Status bar: ON" : "Status bar: OFF" ) ) {
Cvar_SetValue( statusBar->name, !statusBar->value );
}
if ( OptionButton( 0, 2, touchClick->value ? "Touch click: ON" : "Touch click: OFF" ) ) {
Cvar_SetValue( touchClick->name, !touchClick->value );
}
if ( OptionButton( 0, 3, messages->value ? "Text messages: ON" : "Text messages: OFF" ) ) {
Cvar_SetValue( messages->name, !messages->value );
}
if ( OptionButton( 1, 0, drawControls->value ? "Draw controls: ON" : "Draw controls: OFF" ) ) {
Cvar_SetValue( drawControls->name, !drawControls->value );
}
if ( OptionButton( 1, 1, musicState ? "Music: ON" : "Music: OFF" ) ) {
if ( !SysIPhoneOtherAudioIsPlaying() ) {
Cvar_SetValue( music->name, !music->value );
if ( music->value ) {
iphoneStartMusic();
} else {
iphoneStopMusic();
}
}
}
if ( OptionButton( 1, 2, centerSticks->value ? "Center sticks: ON" : "Center sticks: OFF" ) ) {
Cvar_SetValue( centerSticks->name, !centerSticks->value );
}
if ( OptionButton( 1, 3, rampTurn->value ? "Ramp turn: ON" : "Ramp turn: OFF" ) ) {
Cvar_SetValue( rampTurn->name, !rampTurn->value );
}
}
/*
===================
iphoneIntermission
The end-of-level switch was just hit, note the state and awards
for the map select menu
===================
*/
void iphoneIntermission( wbstartstruct_t* wb ) {
if ( deathmatch || netgame ) {
// no achievements in deathmatch mode
return;
}
// find the current episode / map combination
// if a mapStat_t doesn't exist for this yet, create one
// mark this level / skill combination as tried
mapStats_t *cms = FindMapStats( playState.map.dataset, playState.map.episode, playState.map.map, true );
if ( !cms ) {
return;
}
int skill = playState.map.skill;
cms->completionFlags[skill] |= MF_COMPLETED;
// add the awards
if ( wb->plyr[0].stime < wb->partime ) {
cms->completionFlags[skill] |= MF_TIME;
}
int numkills = 0;
int numsecrets = 0;
int numitems = 0;
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
if ( wb->plyr[i].in ) {
numkills += wb->plyr[i].skills;
numitems += wb->plyr[i].sitems;
numsecrets += wb->plyr[i].ssecret;
}
}
if ( numkills >= wb->maxkills ) {
cms->completionFlags[skill] |= MF_KILLS;
}
if ( numitems >= wb->maxitems ) {
cms->completionFlags[skill] |= MF_TREASURE;
}
if ( numsecrets >= wb->maxsecret ) {
cms->completionFlags[skill] |= MF_SECRETS;
}
}
/*
===================
iphoneStartLevel
Do a savegame with the current state
===================
*/
void iphoneStartLevel() {
if ( deathmatch || netgame ) {
// no achievements in deathmatch mode
// reset the levelTimer
if ( levelTimer && setupPacket.timelimit > 0 ) {
// 30 hz, minutes
levelTimeCount = setupPacket.timelimit * 30 * 60;
}
return;
}
playState.map.map = gamemap;
// automatic save game
G_SaveGame( 0, "entersave" );
G_DoSaveGame(true);
// mark this level as tried
mapStats_t *cms = FindMapStats( playState.map.dataset, playState.map.episode, playState.map.map, true );
if ( cms ) {
cms->completionFlags[playState.map.skill] |= MF_TRIED;
}
}
/*
===================
DrawLiveBackground
Draw a randomish moving cloudy background
===================
*/
void DrawLiveBackground() {
static float bgVectors[2][2] = { { 0.01, 0.015 }, { -0.01, -0.02 } };
float fade[2];
// slide and fade a couple textures around
static float tc[2][4][2];
for ( int i = 0 ; i < 2 ; i++ ) {
int ofs = iphoneFrameNum + i * 32;
float dist = ( ofs & 63 );
for ( int j = 0 ; j < 2 ; j ++ ) {
for ( int k = 0 ; k < 2 ; k++ ) {
if ( rand()&1 ) {
if ( bgVectors[j][k] < 0.03 ) {
bgVectors[j][k] += 0.0001;
}
} else {
if ( bgVectors[j][k] > -0.03 ) {
bgVectors[j][k] -= 0.0001;
}
}
}
}
fade[i] = sin( ( dist - 16 ) / 32.0 * M_PI ) * 0.5 + 0.5;
fade[i] *= 0.7;
for ( int j = 0 ; j < 2 ; j++ ) {
tc[i][0][j] += bgVectors[i][j];
tc[i][0][j] -= floor( tc[i][0][j] );
}
tc[i][1][0] = tc[i][0][0]+1;
tc[i][1][1] = tc[i][0][1]+0;
tc[i][2][0] = tc[i][0][0]+0;
tc[i][2][1] = tc[i][0][1]+1;
tc[i][3][0] = tc[i][0][0]+1;
tc[i][3][1] = tc[i][0][1]+1;
}
// Fill rate performance is an issue just for two scrolling layers under
// modest GUI objects. Using a PVR2 texture and a single multitexture
// pass helps. If all the GUI objects were drawn with depth buffering,
// the surface rejection would help out, but bumping depth after every
// draw would be a bit of a chore.
#if 0
glClear( GL_DEPTH_BUFFER_BIT );
glDepthMask( 1 ); // write the depth buffer
glEnable( GL_DEPTH_TEST ); // depth test this background
#endif
PK_BindTexture( PK_FindTexture( "iphone/livetile_1.tga" ) );
glDisable( GL_BLEND );
glDisable( GL_DEPTH_TEST );
// multitexture setup
glActiveTexture( GL_TEXTURE1 );
glClientActiveTexture( GL_TEXTURE1 );
glEnable( GL_TEXTURE_2D );
PK_BindTexture( PK_FindTexture( "iphone/livetile_1.tga" ) );
glTexCoordPointer( 2, GL_FLOAT, 8, tc[1][0] );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
// glColor4f( fade[0], fade[0], fade[0], fade[1] );
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( tc[0][0][0], tc[0][0][1] ); glVertex3f( 0, 0, 0.5 );
glTexCoord2f( tc[0][1][0], tc[0][1][1] ); glVertex3f( 480, 0, 0.5 );
glTexCoord2f( tc[0][2][0], tc[0][2][1]+1 ); glVertex3f( 0, 320, 0.5 );
glTexCoord2f( tc[0][3][0], tc[0][3][1]+1 ); glVertex3f( 480, 320, 0.5 );
glEnd();
// unbind the second texture
glBindTexture( GL_TEXTURE_2D, 0 );
glDisable( GL_TEXTURE_2D );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glActiveTexture( GL_TEXTURE0 );
glClientActiveTexture( GL_TEXTURE0 );
glColor4f( 1, 1, 1, 1 );
glEnable( GL_BLEND );
#if 0
// Enable depth test, but not depth writes, so the tile dorting
// minimizes the amount of time drawing the background when it
// is mostly covered.
glEnable( GL_DEPTH_TEST );
glDepthMask( 0 );
#endif
}
#define MAX_PACKET_LOG 64
int currentPacketLog;
int packetLogMsec[MAX_PACKET_LOG];
void iphonePacketTester() {
glClear( GL_COLOR_BUFFER_BIT );
if ( BackButton() ) {
menuState = IPM_MAIN;
return;
}
struct sockaddr_in sender;
unsigned senderLen = sizeof( sender );
byte buffer[1024];
while( 1 ) {
int r = recvfrom( gameSocket, buffer, sizeof( buffer ), 0, (struct sockaddr *)&sender, &senderLen );
if ( r == -1 ) {
break;
}
packetSetup_t *sp = (packetSetup_t *)buffer;
if ( sp->sendCount == setupPacket.sendCount ) {
Com_Printf( "Duplicated receive: %i\n", sp->sendCount );
} else if ( sp->sendCount < setupPacket.sendCount ) {
Com_Printf( "Out of order receive: %i < %i\n", sp->sendCount, setupPacket.sendCount );
} else if ( sp->sendCount > setupPacket.sendCount + 1 ) {
Com_Printf( "Dropped %i packets before %i\n", sp->sendCount - 1 - setupPacket.sendCount, sp->sendCount );
}
setupPacket = *sp;
packetLogMsec[currentPacketLog&(MAX_PACKET_LOG-1)] = SysIphoneMilliseconds();
currentPacketLog++;
}
color4_t activeColor = { 0, 255, 0, 255 };
for ( int i = 1 ; i < MAX_PACKET_LOG ; i++ ) {
int t1 = packetLogMsec[(currentPacketLog - i)&(MAX_PACKET_LOG-1)];
int t2 = packetLogMsec[(currentPacketLog - i - 1)&(MAX_PACKET_LOG-1)];
int msec = t1 - t2;
R_Draw_Fill( 0, i * 4, msec, 2, activeColor );
}
}
/*
===================
iphoneStartMenu
===================
*/
void iphoneStartMenu() {
mapStart_t map;
if ( !iphoneMapSelectMenu( &map ) ) {
return;
}
if ( map.map == -1 ) {
// hit the back button
menuState = IPM_MAIN;
return;
}
StartSinglePlayerGame( map );
}
/*
===================
iphoneDrawMenus
===================
*/
void iphoneDrawMenus() {
if ( menuState == IPM_PACKET_TEST ) {
// do this before the slow drawing background to get 60hz update rate
iphonePacketTester();
return;
}
// draw the slow double-cloud layer
DrawLiveBackground();
// check for game start in a received setup packet
if ( !netgame && setupPacket.startGame ) {
if ( StartNetGame() ) {
setupPacket.startGame = false;
// we aren't in this game
return;
}
}
// interactive menus
switch ( menuState ) {
case IPM_MAIN: iphoneMainMenu(); break;
case IPM_MULTIPLAYER: iphoneMultiplayerMenu(); break;
case IPM_MAPS: iphoneStartMenu(); break;
case IPM_CONTROLS: iphoneControlMenu(); break;
case IPM_OPTIONS: iphoneOptionsMenu(); break;
case IPM_HUDEDIT: HudEditFrame(); break;
}
}
+604
View File
@@ -0,0 +1,604 @@
/*
* iphone_net.c
* doom
*
* Created by John Carmack on 7/8/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
Deal with all the DNS / bonjour service discovery and resolution
*/
#include "../doomiphone.h"
#include <dns_sd.h>
#include <netdb.h> // for gethostbyname
#include <net/if.h> // for if_nameindex()
DNSServiceRef browseRef;
DNSServiceRef clientServiceRef;
DNSServiceRef serviceRef;
boolean serviceRefValid;
typedef struct {
int interfaceIndex;
char browseName[1024];
char browseRegtype[1024];
char browseDomain[1024];
} service_t;
boolean localServer;
// we can find services on both WiFi and Bluetooth interfaces
#define MAX_SERVICE_INTEFACES 4
service_t serviceInterfaces[MAX_SERVICE_INTEFACES];
boolean gotServerAddress;
struct sockaddr resolvedServerAddress;
static const char *serviceName = "_DoomServer._udp.";
int InterfaceIndexForName( const char *ifname );
void DNSServiceRegisterReplyCallback (
DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
void *context ) {
if ( errorCode == kDNSServiceErr_NoError ) {
localServer = true;
} else {
localServer = false;
}
}
boolean RegisterGameService() {
DNSServiceErrorType err = DNSServiceRegister(
&serviceRef,
kDNSServiceFlagsNoAutoRename, // we want a conflict error
InterfaceIndexForName( "en0" ), // pass 0 for all interfaces
"iPhone Doom Classic",
serviceName,
NULL, // domain
NULL, // host
htons( DOOM_PORT ),
0, // txtLen
NULL, // txtRecord
DNSServiceRegisterReplyCallback,
NULL // context
);
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceRegister error\n" );
} else {
// block until we get a response, process it, and run the callback
err = DNSServiceProcessResult( serviceRef );
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceProcessResult error\n" );
}
}
return localServer;
}
void TerminateGameService() {
if ( localServer ) {
localServer = false;
}
DNSServiceRefDeallocate( serviceRef );
memset( serviceInterfaces, 0, sizeof( serviceInterfaces ) );
}
void DNSServiceQueryRecordReplyCallback (
DNSServiceRef DNSServiceRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *fullname,
uint16_t rrtype,
uint16_t rrclass,
uint16_t rdlen,
const void *rdata,
uint32_t ttl,
void *context ) {
assert( rdlen == 4 );
const byte *ip = (const byte *)rdata;
char interfaceName[IF_NAMESIZE];
if_indextoname( interfaceIndex, interfaceName );
printf( "DNSServiceQueryRecordReplyCallback: %s, interface[%i] = %s, [%i] = %i.%i.%i.%i\n",
fullname, interfaceIndex, interfaceName, rdlen, ip[0], ip[1], ip[2], ip[3] );
ReportNetworkInterfaces();
memset( &resolvedServerAddress, 0, sizeof( resolvedServerAddress ) );
struct sockaddr_in *sin = (struct sockaddr_in *)&resolvedServerAddress;
sin->sin_len = sizeof( resolvedServerAddress );
sin->sin_family = AF_INET;
sin->sin_port = htons( DOOM_PORT );
memcpy( &sin->sin_addr, ip, 4 );
gotServerAddress = true;
}
DNSServiceFlags callbackFlags;
void DNSServiceResolveReplyCallback (
DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *fullname,
const char *hosttarget,
uint16_t port,
uint16_t txtLen,
const unsigned char *txtRecord,
void *context ) {
char interfaceName[IF_NAMESIZE];
if_indextoname( interfaceIndex, interfaceName );
printf( "Resolve: interfaceIndex [%i]=%s : %s @ %s\n", interfaceIndex, interfaceName, fullname, hosttarget );
callbackFlags = flags;
#if 0
struct hostent * host = gethostbyname( hosttarget );
if ( host ) {
printf( "h_name: %s\n", host->h_name );
if ( host->h_aliases ) { // this can be NULL
for ( char **list = host->h_aliases ; *list ; list++ ) {
printf( "h_alias: %s\n", *list );
}
}
printf( "h_addrtype: %i\n", host->h_addrtype );
printf( "h_length: %i\n", host->h_length );
if ( !host->h_addr_list ) { // I doubt this would ever be NULL...
return;
}
for ( char **list = host->h_addr_list ; *list ; list++ ) {
printf( "addr: %i.%i.%i.%i\n", ((byte *)*list)[0], ((byte *)*list)[1], ((byte *)*list)[2], ((byte *)*list)[3] );
}
memset( &resolvedServerAddress, 0, sizeof( resolvedServerAddress ) );
resolvedServerAddress.sin_len = sizeof( resolvedServerAddress );
resolvedServerAddress.sin_family = host->h_addrtype;
resolvedServerAddress.sin_port = htons( DOOM_PORT );
assert( host->h_length == 4 );
memcpy( &resolvedServerAddress.sin_addr, *host->h_addr_list, host->h_length );
gotServerAddress = true;
}
#else
DNSServiceRef queryRef;
// look up the name for this host
DNSServiceErrorType err = DNSServiceQueryRecord (
&queryRef,
kDNSServiceFlagsForceMulticast,
interfaceIndex,
hosttarget,
kDNSServiceType_A, // we want the host address
kDNSServiceClass_IN,
DNSServiceQueryRecordReplyCallback,
NULL /* may be NULL */
);
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceQueryRecord error\n" );
} else {
// block until we get a response, process it, and run the callback
err = DNSServiceProcessResult( queryRef );
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceProcessResult error\n" );
}
DNSServiceRefDeallocate( queryRef );
}
#endif
}
boolean NetworkServerAvailable() {
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
return true;
}
}
return false;
}
// returns "WiFi", "BlueTooth", or "" for display on the
// main menu multiplayer icon
const char *NetworkServerTransport() {
int count = 0;
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
count++;
}
}
static char str[1024];
str[0] = 0;
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
int index = serviceInterfaces[i].interfaceIndex;
if ( index == 0 ) {
continue;
}
if ( str[0] ) {
strcat( str, "+" );
}
if ( index == -1 ) {
strcat( str, "BT-NEW" );
} else if ( index == 1 ) {
strcat( str, "LOOP" ); // we should never see this!
} else if ( index == 2 ) {
strcat( str, "WiFi" );
} else {
strcat( str, "BT-EST" );
}
}
return str;
}
boolean ResolveNetworkServer( struct sockaddr *addr ) {
if ( !NetworkServerAvailable() ) {
return false;
}
gotServerAddress = false;
DNSServiceRef resolveRef;
// An unconnected bluetooth service will report an interfaceIndex of -1, so if
// we have a wifi link with an interfaceIndex > 0, use that
// explicitly.
service_t *service = NULL;
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex > 0 ) {
service = &serviceInterfaces[i];
char interfaceName[IF_NAMESIZE];
if_indextoname( service->interfaceIndex, interfaceName );
printf( "explicitly resolving server on interface %i = %s\n", service->interfaceIndex, interfaceName );
break;
}
}
if ( !service ) {
#if 0 // don't support bluetooth now
// settle for the unconnected bluetooth service
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
service = &serviceInterfaces[i];
break;
}
}
#endif
if ( !service ) {
printf( "No serviceInterface current.\n" );
return false;
}
}
// look up the name for this service
DNSServiceErrorType err = DNSServiceResolve (
&resolveRef,
kDNSServiceFlagsForceMulticast, // always on local link
service->interfaceIndex > 0 ? service->interfaceIndex : 0, // don't use -1 for bluetooth
service->browseName,
service->browseRegtype,
service->browseDomain,
DNSServiceResolveReplyCallback,
NULL /* context */
);
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceResolve error\n" );
} else {
// We can get two callbacks when both wifi and bluetooth are enabled
callbackFlags = 0;
do {
err = DNSServiceProcessResult( resolveRef );
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceProcessResult error\n" );
}
} while ( callbackFlags & kDNSServiceFlagsMoreComing );
DNSServiceRefDeallocate( resolveRef );
}
if ( gotServerAddress ) {
*addr = resolvedServerAddress;
return true;
}
return false;
}
void DNSServiceBrowseReplyCallback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *serviceName,
const char *regtype,
const char *replyDomain,
void *context ) {
printf( "DNSServiceBrowseReplyCallback %s: interface:%i name:%s regtype:%s domain:%s\n",
(flags & kDNSServiceFlagsAdd) ? "ADD" : "REMOVE",
interfaceIndex, serviceName, regtype, replyDomain );
if ( flags & kDNSServiceFlagsAdd ) {
// add it to the list
if ( interfaceIndex == 1 ) {
printf( "Not adding service on loopback interface.\n" );
} else {
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
service_t *service = &serviceInterfaces[i];
if ( service->interfaceIndex == 0 ) {
strncpy( service->browseName, serviceName, sizeof( service->browseName ) -1 );
strncpy( service->browseRegtype, regtype, sizeof( service->browseRegtype ) -1 );
strncpy( service->browseDomain, replyDomain, sizeof( service->browseDomain ) -1 );
service->interfaceIndex = interfaceIndex;
break;
}
}
}
} else {
// remove it from the list
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex == interfaceIndex ) {
serviceInterfaces[i].interfaceIndex = 0;
}
}
}
}
void ProcessDNSMessages() {
static boolean initialized;
if ( !initialized ) {
initialized = true;
DNSServiceErrorType err = DNSServiceBrowse (
&browseRef,
0, /* flags */
0, /* interface */
serviceName,
NULL, /* domain */
DNSServiceBrowseReplyCallback,
NULL /* context */
);
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceBrowse error\n" );
return;
}
}
// poll the socket for updates
int socket = DNSServiceRefSockFD( browseRef );
if ( socket <= 0 ) {
return;
}
fd_set set;
FD_ZERO( &set );
FD_SET( socket, &set );
struct timeval tv;
memset( &tv, 0, sizeof( tv ) );
if ( select( socket+1, &set, NULL, NULL, &tv ) > 0 ) {
DNSServiceProcessResult( browseRef );
}
}
void ReportNetworkInterfaces() {
struct ifaddrs *ifap;
printf( "getifaddrs():\n" );
if ( getifaddrs( &ifap ) == -1 ) {
perror( "getifaddrs(): " );
} else {
for ( struct ifaddrs *ifa = ifap ; ifa ; ifa = ifa->ifa_next ) {
struct sockaddr_in *ina = (struct sockaddr_in *)ifa->ifa_addr;
if ( ina->sin_family == AF_INET ) {
byte *ip = (byte *)&ina->sin_addr;
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=AF_INET ip: %i.%i.%i.%i\n", ifa->ifa_name, ifa->ifa_flags,
ina->sin_family, ip[0], ip[1], ip[2], ip[3] );
} else if ( ina->sin_family == AF_LINK ) {
struct if_data *data = (struct if_data *)ifa->ifa_data;
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=AF_LINK ifi_ipackets: %i\n", ifa->ifa_name, ifa->ifa_flags,
ina->sin_family, data->ifi_ipackets );
} else {
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=???\n", ifa->ifa_name, ifa->ifa_flags,
ina->sin_family );
}
}
freeifaddrs( ifap );
}
printf( "if_nameindex():\n" );
struct if_nameindex *ifnames = if_nameindex();
if ( !ifnames ) {
perror( "if_ameindex():" );
} else {
for ( int i = 0 ; ifnames[i].if_index != 0 ; i++ ) {
printf( "%i : %s\n", ifnames[i].if_index, ifnames[i].if_name );
}
if_freenameindex( ifnames );
}
}
boolean NetworkAvailable() {
struct ifaddrs *ifap;
if ( getifaddrs( &ifap ) == -1 ) {
return false;
}
// We can't tell if bluetooth is available from here, because
// the interface doesn't appear until after the service is found,
// but I decided not to support bluetooth for now due to the poor performance.
boolean goodInterface = false;
for ( struct ifaddrs *ifa = ifap ; ifa ; ifa = ifa->ifa_next ) {
struct sockaddr_in *ina = (struct sockaddr_in *)ifa->ifa_addr;
if ( ina->sin_family == AF_INET ) {
if ( !strcmp( ifa->ifa_name, "en0" ) ) {
goodInterface = true;
}
}
}
freeifaddrs( ifap );
return goodInterface;
}
int InterfaceIndexForAddress( struct sockaddr_in *adr ) {
// FIXME: compare against getifaddrs
return 0;
}
struct sockaddr_in AddressForInterfaceName( const char *ifname ) {
struct sockaddr_in s;
memset( &s, 0, sizeof( s ) );
struct ifaddrs *ifap;
if ( getifaddrs( &ifap ) == -1 ) {
perror( "getifaddrs()" );
return s;
}
struct ifaddrs *ifa;
for ( ifa = ifap ; ifa ; ifa = ifa->ifa_next ) {
struct sockaddr_in *ina = (struct sockaddr_in *)ifa->ifa_addr;
if ( ina->sin_family == AF_INET && !strcmp( ifa->ifa_name, ifname ) ) {
byte *ip = (byte *)&ina->sin_addr;
printf( "AddressForInterfaceName( %s ) = ifa_name: %s ifa_flags: %i sa_family: %i=AF_INET ip: %i.%i.%i.%i\n",
ifname, ifa->ifa_name, ifa->ifa_flags,
ina->sin_family, ip[0], ip[1], ip[2], ip[3] );
freeifaddrs( ifap );
return *ina;
}
}
freeifaddrs( ifap );
printf( "AddressForInterfaceName( %s ): Couldn't find IP address\n", ifname );
return s;
}
int InterfaceIndexForName( const char *ifname ) {
struct if_nameindex *ifnames = if_nameindex();
if ( !ifnames ) {
perror( "if_nameindex()" );
return 0;
}
for ( int i = 0 ; ifnames[i].if_index != 0 ; i++ ) {
if ( !strcmp( ifname, ifnames[i].if_name ) ) {
int index = ifnames[i].if_index;
if_freenameindex( ifnames );
return index;
}
}
printf( "InterfaceIndexForName( %s ): Couldn't find interface\n", ifname );
if_freenameindex( ifnames );
return 0;
}
struct sockaddr_in AddressForInterfaceIndex( int interfaceIndex ) {
struct sockaddr_in addr;
memset( &addr, 0, sizeof( addr ) );
addr.sin_len = sizeof( addr );
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
if ( interfaceIndex == 0 ) {
return addr;
}
struct if_nameindex *ifnames = if_nameindex();
if ( !ifnames ) {
perror( "if_ameindex()" );
return addr;
}
for ( int i = 0 ; ifnames[i].if_index != 0 ; i++ ) {
if ( ifnames[i].if_index == interfaceIndex ) {
addr = AddressForInterfaceName( ifnames[i].if_name );
if_freenameindex( ifnames );
return addr;
}
}
printf( "AddressForInterfaceIndex( %i ): Couldn't find interface\n", interfaceIndex );
if_freenameindex( ifnames );
return addr;
}
struct sockaddr_in gameSocketAddress;
int UDPSocket( const char *interfaceName, int portnum ) {
int udpSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if ( udpSocket == -1 ) {
Com_Printf( "UDP socket failed: %s\n", strerror( errno ) );
return -1;
}
struct sockaddr_in addr = AddressForInterfaceName( interfaceName );
addr.sin_port = htons( portnum );
byte *ip = (byte *)&addr.sin_addr;
gameSocketAddress = addr;
Com_Printf( "UDPSocket( %s, %i ) = %i.%i.%i.%i\n", interfaceName, portnum,
ip[0], ip[1], ip[2], ip[3] );
if ( bind( udpSocket, (struct sockaddr *)&addr, sizeof( addr ) ) == -1 ) {
Com_Printf( "UDP bind failed: %s\n", strerror( errno ) );
close( udpSocket );
return -1;
}
#if 0
// enable broadcast
int on = 1;
if ( setsockopt( udpSocket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) ) == -1 ) {
Com_Printf( "UDP setsockopt failed: %s\n", strerror( errno ) );
close( udpSocket );
return -1;
}
#endif
#if 0
// set the type-of-service, in hopes that the link level drivers use it
// to stop buffering huge amounts of data when there are line errors
int tos = 0x10; /* IPTOS_LOWDELAY; */ /* see <netinet/in.h> */
if ( setsockopt( udpSocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos) ) == -1 ) {
Com_Printf( "setsockopt IP_TOS failed: %s\n", strerror( errno ) );
}
#endif
// enable non-blocking IO
if ( fcntl( udpSocket, F_SETFL, O_NONBLOCK ) == -1 ) {
Com_Printf( "UDP fcntl failed: %s\n", strerror( errno ) );
close( udpSocket );
return -1;
}
return udpSocket;
}
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef IPHONE_QGL_ENUMERANTS_H
#define IPHONE_QGL_ENUMERANTS_H
#ifdef QGL_LOG_GL_CALLS
#include <OpenGLES/ES1/gl.h>
#ifdef __cplusplus
extern "C" {
#endif
const char *StringFromGLEnumerant( GLenum enumerant );
#ifdef __cplusplus
}
#endif
#endif // QGL_LOG_GL_CALLS
#endif // IPHONE_QGL_ENUMERANTS_H
File diff suppressed because it is too large Load Diff
+304
View File
@@ -0,0 +1,304 @@
/*
* iphone_sound.c
* doom
*
* Created by John Carmack on 4/16/09.
* Copyright 2009 Id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
#import <AudioToolbox/AudioServices.h>
typedef struct {
unsigned sourceName; // OpenAL sourceName
pkWav_t *sfx; // NULL if unused
float volume; // stored for showSound display
} channel_t;
static ALCcontext *Context;
static ALCdevice *Device;
#define MAX_CHANNELS 16
static channel_t s_channels[ MAX_CHANNELS ];
cvar_t *s_sfxVolume;
void Sound_StartLocalSound( const char *filename ) {
Sound_StartLocalSoundAtVolume( filename, 1.0f );
}
void Sound_StartLocalSoundAtVolume( const char *filename, float volume ) {
pkWav_t *sfx;
sfx = PK_FindWav( filename );
if( ! sfx ) {
Com_Printf( "Sound_StartLocalSound: could not cache (%s)\n", filename );
return;
}
// printf( "sound:%s\n", filename );
// channel 0 is reserved for UI sounds, the other channels
// are for DOOM sounds
channel_t *ch = &s_channels[ 0 ];
ch->sfx = sfx;
ch->volume = s_sfxVolume->value * volume;
alSourceStop( ch->sourceName );
alSourcef( ch->sourceName, AL_GAIN, ch->volume );
alSourcei( ch->sourceName, AL_BUFFER, sfx->alBufferNum );
alSourcef( ch->sourceName, AL_PITCH, 1.0f );
alSourcePlay( ch->sourceName );
}
static void Sound_Play_f( void ) {
if( Cmd_Argc() == 1 ) {
Com_Printf( "Usage: play <soundfile>\n" );
return;
}
Sound_StartLocalSound( Cmd_Argv( 1 ) );
}
// we won't allow music to be toggled on or off in the menu when this is true
int otherAudioIsPlaying;
int SysIPhoneOtherAudioIsPlaying() {
return otherAudioIsPlaying;
}
void interruptionListener( void *inUserData, UInt32 inInterruption)
{
printf("Session interrupted! --- %s ---\n", inInterruption == kAudioSessionBeginInterruption ? "Begin Interruption" : "End Interruption");
if ( inInterruption == kAudioSessionBeginInterruption ) {
printf("Audio interrupted.\n" );
iphonePauseMusic();
alcMakeContextCurrent( NULL );
AudioSessionSetActive( false );
} else if ( inInterruption == kAudioSessionEndInterruption ) {
printf("Audio restored.\n" );
OSStatus r = AudioSessionSetActive( true );
if ( r != kAudioSessionNoError ) {
printf( "AudioSessionSetActive( true ) failed: 0x%x\n", r );
} else {
printf( "AudioSessionSetActive( true ) succeeded.\n" );
}
alcMakeContextCurrent( Context );
if( alcGetError( Device ) != ALC_NO_ERROR ) {
Com_Error( "Failed to alcMakeContextCurrent\n" );
}
iphoneResumeMusic();
}
}
void Sound_Init( void ) {
Com_Printf( "\n------- Sound Initialization -------\n" );
s_sfxVolume = Cvar_Get( "s_sfxVolume", "1.0", 0 );
Cmd_AddCommand( "play", Sound_Play_f );
// make sure background ipod music mixes with our sound effects
Com_Printf( "...Initializing AudioSession\n" );
OSStatus status = 0;
status = AudioSessionInitialize(NULL, NULL, interruptionListener, NULL); // else "couldn't initialize audio session"
// if there is iPod music playing in the background, we want to use
// the AmbientSound catagory, otherwise we will leave it at the default.
// If we always set it to AmbientSound, then the mp3 background music
// playback goes to software on 3.0 for a huge slowdown.
UInt32 propOtherAudioIsPlaying = 'othr'; // kAudioSessionProperty_OtherAudioIsPlaying
UInt32 size = sizeof( otherAudioIsPlaying );
AudioSessionGetProperty( propOtherAudioIsPlaying, &size, &otherAudioIsPlaying );
Com_Printf("OtherAudioIsPlaying = %d\n", otherAudioIsPlaying );
if ( otherAudioIsPlaying ) {
UInt32 audioCategory = kAudioSessionCategory_AmbientSound;
status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory);
}
status = AudioSessionSetActive(true); // else "couldn't set audio session active\n"
Com_Printf( "...Initializing OpenAL subsystem\n" );
// get the OpenAL device
Device = alcOpenDevice( NULL );
if( Device == NULL ) {
Com_Printf( "Failed to alcOpenDevice\n" );
}
// set the mixer output rate lower, so we don't waste time doing 44khz
// must be done before the context is created!
extern ALvoid alcMacOSXMixerOutputRate(const ALdouble value);
alcMacOSXMixerOutputRate( 22050 );
// Create context(s)
Context = alcCreateContext( Device, NULL );
if( Context == NULL ) {
Com_Error( "Failed to alcCreateContext\n" );
}
// Set active context
alcGetError( Device );
alcMakeContextCurrent( Context );
if( alcGetError( Device ) != ALC_NO_ERROR ) {
Com_Error( "Failed to alcMakeContextCurrent\n" );
}
// allocate all the channels we are going to use
channel_t *ch;
int i;
for( i = 0, ch = s_channels ; i < MAX_CHANNELS ; ++i, ++ch ) {
alGenSources( 1, &ch->sourceName );
if( alGetError() != AL_NO_ERROR ) {
Com_Error( "Allocating AL sound sources" );
}
alSourcei( ch->sourceName, AL_SOURCE_RELATIVE, AL_FALSE );
}
Com_Printf( "------------------------------------\n" );
}
/*
==================
ShowSound
Display active sound channels
==================
*/
void ShowSound() {
if ( !showSound->value ) {
return;
}
channel_t *ch;
int i;
for( i = 0, ch = s_channels ; i < MAX_CHANNELS ; ++i, ++ch ) {
int state;
alGetSourcei( ch->sourceName, AL_SOURCE_STATE, &state );
if ( state != AL_PLAYING ) {
continue;
}
int v = ch->volume * 255;
if ( v > 255 ) {
v = 255;
}
color4_t color = { v, v, v, 255 };
R_Draw_Fill( i*16, 0, 12, 12, color );
}
}
/*
==================================================================
PrBoom interface
==================================================================
*/
// Init at program start...
void I_InitSound(void) {}
// ... shut down and relase at program termination.
void I_ShutdownSound(void) {}
// Initialize channels?
void I_SetChannels(void) {}
// Get raw data lump index for sound descriptor.
int I_GetSfxLumpNum (sfxinfo_t *sfx) {
// find the pkWav_t for this sfxinfo
char upper[16], *d = upper;
for ( const char *c = sfx->name ; *c ; c++ ) {
*d++ = toupper( *c );
}
*d = 0;
pkWav_t *pkwav = PK_FindWav( va( "newsfx/DS%s.wav", upper ) );
return pkwav - pkWavs;
}
// Starts a sound in a particular sound channel.
// volume ranges 0 - 64
// seperation tanges is 128 straight ahead, 0 = all left ear, 255 = all right ear
// pitch centers around 128
int I_StartSound(int sfx_id, int channel, int vol, int sep, int pitch, int priority) {
sfxinfo_t *dsfx = &S_sfx[sfx_id];
assert( dsfx->lumpnum >= 0 && dsfx->lumpnum < pkHeader->wavs.count );
pkWav_t *sfx = &pkWavs[dsfx->lumpnum];
// printf( "sound: %s chan:%i vol:%i sep:%i pitch:%i priority:%i\n", sfx->wavData->name.name, channel, vol, sep, pitch, priority );
assert( channel >= 0 && channel < MAX_CHANNELS - 1 );
channel_t *ch = &s_channels[ 1+channel ];
alSourceStop( ch->sourceName );
if ( ch->sfx == sfx ) {
// restarting the same sound
alSourceRewind( ch->sourceName );
} else {
alSourcei( ch->sourceName, AL_BUFFER, sfx->alBufferNum );
}
ch->sfx = sfx;
ch->volume = s_sfxVolume->value * vol / 64.0;
alSourcef( ch->sourceName, AL_GAIN, ch->volume );
alSourcef( ch->sourceName, AL_PITCH, pitch / 128.0f );
alSourcePlay( ch->sourceName );
return (int)ch;
}
// Stops a sound channel.
void I_StopSound(int handle) {}
// Called by S_*() functions
// to see if a channel is still playing.
// Returns 0 if no longer playing, 1 if playing.
boolean I_SoundIsPlaying(int handle) {
channel_t *ch = (channel_t *)handle;
if ( !ch ) {
return false;
}
int state;
alGetSourcei( ch->sourceName, AL_SOURCE_STATE, &state );
return state == AL_PLAYING;
}
// Called by m_menu.c to let the quit sound play and quit right after it stops
boolean I_AnySoundStillPlaying(void) { return false; }
// Updates the volume, separation,
// and pitch of a sound channel.
void I_UpdateSoundParams(int handle, int vol, int sep, int pitch) {}
+231
View File
@@ -0,0 +1,231 @@
/*
* iphone_start.c
* doom
*
* Created by John Carmack on 7/7/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
/*
==================
ResumeGame
==================
*/
void ResumeGame() {
if ( levelHasBeenLoaded && !demoplayback ) {
// return to the already started game
iphoneResumeMusic();
drawWeaponSelect = false;
weaponSelected = -1;
automapmode = 0;
advancedemo = false;
menuState = IPM_GAME;
return;
}
if ( !playState.saveGameIsValid ) {
// they hit "resume game" on the first app lounch, so just start E1M1
mapStart_t map;
map.skill = 1;
map.episode = 1;
map.map = 1;
StartSinglePlayerGame( map );
} else {
StartSaveGame();
}
}
/*
==================
GameSetup
==================
*/
void GameSetup() {
// resume game just goes back to playing, it doesn't load anything
levelHasBeenLoaded = true;
// make sure doom doesn't cycle to the next demo loop and kill the new game
advancedemo = false;
// not in a timedemo yet
timeDemoStart = 0;
iphoneTimeDemo = false;
// display the game next time through the main loop
drawWeaponSelect = false;
weaponSelected = -1;
automapmode = 0;
menuState = IPM_GAME;
demoplayback = false;
levelTimer = false;
levelFragLimit = false;
// single player game
netgame = false;
deathmatch = false;
nomonsters = false;
memset( playeringame, 0, sizeof( playeringame ) );
consoleplayer = 0;
displayplayer = 0;
playeringame[consoleplayer] = 1;
}
/*
=======================
StartSaveGame
Can be called by both the resume game button after launch, or the
load game button after a player death
=======================
*/
void StartSaveGame() {
GameSetup();
G_LoadGame( 0, true );
G_DoLoadGame();
}
/*
=======================
StartSinglePlayerGame
=======================
*/
void StartSinglePlayerGame( mapStart_t map ) {
playState.map = map;
playState.saveGameIsValid = true; // assume we will save the game on exit
// mark this level / skill combination as tried
//
mapStats_t *cms = FindMapStats( playState.map.dataset, playState.map.episode, playState.map.map, true );
if ( cms ) {
// if we are at MAX_MAPS, no stat tracking...
cms->completionFlags[playState.map.skill] |= MF_TRIED;
}
GameSetup();
// start the map
G_InitNew( playState.map.skill, playState.map.episode, playState.map.map );
}
/*
=======================
StartNetGame
Begins a game based on the contents of setupPacket
=======================
*/
boolean StartNetGame() {
// make sure we are supposed to be in this game
int slot = -1;
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
if ( setupPacket.playerID[i] == playerID ) {
slot = i;
}
}
if ( slot == -1 ) {
return false;
}
GameSetup();
consoleplayer = displayplayer = slot;
netgame = true; // respawn without restarting levels
if ( setupPacket.deathmatch ) {
// deathmatch game
deathmatch = setupPacket.deathmatch; // could be either 1 or 2 for altdeath
nomonsters = true;
if ( setupPacket.timelimit > 0 ) {
levelTimer = true;
// 30 hz, minutes
levelTimeCount = setupPacket.timelimit * 30 * 60;
}
if ( setupPacket.fraglimit > 0 ) {
levelFragLimit = true;
levelFragLimitCount = setupPacket.fraglimit;
}
} else {
// coop game
deathmatch = false;
nomonsters = false;
}
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
if ( setupPacket.playerID[i] != 0 ) {
playeringame[i] = 1;
} else {
playeringame[i] = 0;
}
}
gametic = 0;
maketic = 1; // allow everyone to run the first frame without waiting for a packet
memset( netcmds, 0, sizeof( netcmds ) );
memset( consistancy, 0, sizeof( consistancy ) );
gameID = setupPacket.gameID;
// start the map
G_InitNew( setupPacket.map.skill, setupPacket.map.episode, setupPacket.map.map );
return true;
}
/*
=======================
StartDemoGame
The demo button has been hit on the main menu
=======================
*/
void StartDemoGame( boolean timeDemoMode ) {
if ( levelHasBeenLoaded && !netgame && !demoplayback && usergame && gamestate == GS_LEVEL ) {
// save the current game before starting the demos
levelHasBeenLoaded = false;
G_SaveGame( 0, "quicksave" );
G_DoSaveGame(true);
}
GameSetup();
if ( timeDemoMode ) {
iphoneTimeDemo = true;
}
// always skip to the next one on each exit from the menu
advancedemo = true;
}
+62
View File
@@ -0,0 +1,62 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
#import <AudioToolbox/AudioServices.h>
int SysIphoneMicroseconds() {
struct timeval tp;
struct timezone tzp;
static int secbase;
gettimeofday( &tp, &tzp );
if( ! secbase ) {
secbase = tp.tv_sec;
return tp.tv_usec;
}
int curtime = (tp.tv_sec - secbase) * 1000000 + tp.tv_usec;
return curtime;
}
int SysIphoneMilliseconds() {
return SysIphoneMicroseconds()/1000;
}
extern char iphoneDocDirectory[1024];
extern char iphoneAppDirectory[1024];
const char *SysIphoneGetAppDir() {
return iphoneAppDirectory;
}
const char *SysIphoneGetDocDir() {
return iphoneDocDirectory;
}
void SysIPhoneVibrate() {
AudioServicesPlaySystemSound( kSystemSoundID_Vibrate );
}
+299
View File
@@ -0,0 +1,299 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// define this to get only the first episode on selections, and the
// automatic sell screen at the end of episode 1
#define EPISODE_ONE_ONLY
// this is the version number displayed on the menu screen
#define DOOM_IPHONE_VERSION 0.1
// if defined, the game runs in a separate thread from the app event loop
#define USE_GAME_THREAD
typedef enum menuState {
IPM_GAME,
IPM_MAIN,
IPM_MAPS,
IPM_MULTIPLAYER,
IPM_MASTER,
IPM_CONTROLS,
IPM_OPTIONS,
IPM_HUDEDIT
} menuState_t;
extern menuState_t menuState;
void iphoneDrawMenus();
#define VID_WIDTH 480
#define VID_HEIGHT 320
#define MAX_SKILLS 5
#define MAX_MAPS 200
#define MF_TRIED 1
#define MF_COMPLETED 2
#define MF_KILLS 4
#define MF_SECRETS 8
#define MF_TREASURE 16
#define MF_TIME 32
// we want to track mapStats for downloaded content, so we
// won't have a known number of these
typedef struct {
int dataset;
int episode;
int map;
int completionFlags[MAX_SKILLS];
} mapStats_t;
// this structure is saved out at the head of the binary save file,
// and allows all the menus to work without having to load a game save
typedef struct {
int version;
int episode;
int map;
int skill;
int mapsStarted; // when 0, resume game will just be a new game
// if someone downloads more than MAX_MAPS, they won't get stat tracking on them.
int numMapStats;
mapStats_t mapStats[MAX_MAPS];
} currentMap_t;
extern currentMap_t currentMap;
extern boolean levelHasBeenLoaded; // determines if "resume game" does a loadGame and exiting does a saveGame
extern pkTexture_t *fontTexture;
extern pkTexture_t *numberPics[10];
extern int iphoneFrameNum;
extern int levelLoadFrameNum;
extern int consoleActive;
extern cvar_t *skill;
extern cvar_t *episode;
extern cvar_t *controlScheme;
extern cvar_t *stickMove;
extern cvar_t *stickTurn;
extern cvar_t *rotorTurn;
extern cvar_t *stickDeadBand;
extern cvar_t *tiltTurn;
extern cvar_t *tiltMove;
extern cvar_t *tiltDeadBand;
extern cvar_t *tiltAverages;
extern cvar_t *music;
extern cvar_t *showTilt;
extern cvar_t *showTime;
extern cvar_t *cropSprites;
extern cvar_t *revLand;
extern cvar_t *mapScale;
extern cvar_t *hideControls;
extern cvar_t *tapFire;
extern cvar_t *skipSleep;
extern cvar_t *autoUse;
extern cvar_t *statusBar;
extern cvar_t *touchClick;
extern int numTouches;
extern int touches[5][2]; // [0] = x, [1] = y in landscape mode, raster order with y = 0 at top
// so we can detect button releases
extern int numPrevTouches;
extern int prevTouches[5][2];
extern float tilt; // -1.0 to 1.0
extern float tiltPitch;
extern boolean drawWeaponSelect; // true when the weapon select overlay is up
extern int weaponSelected; // -1 for no change
typedef unsigned char color4_t[4];
typedef unsigned char color3_t[3];
typedef struct {
int enterFrame;
int beforeSwap;
int afterSwap;
int afterSleep;
} logTime_t;
#define MAX_LOGGED_TIMES 512
extern logTime_t loggedTimes[MAX_LOGGED_TIMES]; // indexed by iphoneFrameNum
void LoadWallTexture( int wallPicNum );
int TouchDown( int x, int y, int w, int h );
int TouchReleased( int x, int y, int w, int h );
int iphoneDrawText( int x, int y, const char *str );
int iphoneCenterText( int x, int y, const char *str );
void iphoneDrawNumber( int x, int y, int number, int charWidth, int charHeight );
void iphoneDrawPic( int x, int y, int w, int h, const char *pic );
int iphoneDrawPicWithTouch( int x, int y, int w, int h, const char *pic );
void StartGame();
void iphoneOpenAutomap();
void iphoneDrawNotifyText();
void iphoneSet2D( void );
bool TextButton( const char *title, int x, int y, int w, int h );
void R_Draw_Fill( int x, int y, int w, int h, color3_t c );
void R_Draw_Blend( int x, int y, int w, int h, color4_t c );
void InitImmediateModeGL();
int iphoneRotateForLandscape();
void iphoneCheckForLandscapeReverse();
void iphonePacifierUpdate();
void iphoneDrawScreen();
extern int damageflash;
extern int bonusFrameNum;
extern int attackDirTime[2];
#define HF_DISABLED 1
#define HF_NODRAW 2 // invisible button
typedef struct {
int x, y; // mdpoint
int drawWidth, drawHeight;
int touchWidth, touchHeight; // allow touches outside the actual bounds
pkTexture_t *texture;
boolean drawAsLimit; // draw with red tint to show further movement won't do anything
float touchState;
float drawState; // offsets for rotors
int hudFlags;
struct touch_s *touch;
} hudPic_t;
typedef struct {
hudPic_t forwardStick;
hudPic_t sideStick;
hudPic_t turnStick;
hudPic_t turnRotor;
hudPic_t fire;
hudPic_t menu;
hudPic_t map;
hudPic_t weaponSelect;
} hud_t;
extern hud_t huds;
void HudSetForScheme( int schemeNum );
void HudSetTexnums();
void HudEditFrame();
void Sound_StartLocalSound( const char *sound );
void Sound_StartLocalSoundAtVolume( const char *sound, float volume );
int BackButton();
void ResumeGame();
//---------------------------------------
// Touch and button
//---------------------------------------
typedef struct touch_s {
boolean down;
int x, y;
int prevX, prevY; // will be set to x, y on first touch, copied after each game frame
int stateCount; // set to 1 on first event that state changes, incremented each game frame
void *controlOwner;
void *identification;
} touch_t;
#define MAX_TOUCHES 5
extern touch_t sysTouches[MAX_TOUCHES];
extern touch_t gameTouches[MAX_TOUCHES];
extern pthread_mutex_t eventMutex; // used to sync between game and event threads
touch_t *TapInBounds( int x, int y, int w, int h );
touch_t *TouchInBounds( int x, int y, int w, int h );
touch_t *UpdateHudTouch( hudPic_t *hud );
typedef struct {
pkTexture_t *texture;
const char *title;
int x, y, w, h;
touch_t *touch;
float scale; // ramps up and down after touches
int frameNum; // reset scale if not checked on previous frame
} ibutton_t;
void SetButtonPics( ibutton_t *button, const char *picName, const char *title, int x, int y );
void SetButtonText( ibutton_t *button, const char *title, int x, int y, int w, int h );
boolean HandleButton( ibutton_t *button );
//---------------------------------------
// Doom stuff we call directly
//---------------------------------------
void G_DoSaveGame (boolean menu);
//---------------------------------------
// iphone_mapSelect.c
//---------------------------------------
void DisplayLoadingScreen();
void iphoneMapSelectMenu();
//---------------------------------------
// interfaces from the original game code
//---------------------------------------
void iphoneSetNotifyText( const char *str, ... );
void iphoneIntermission( wbstartstruct_t* wbstartstruct );
//---------------------------------------
// interfaces to Objective-C land
//---------------------------------------
// The event thread will fill this after hitting enter
// on the console. The game thread should check it,
// execute it, and clear it under mutex.
extern char consoleCommand[1024];
void SysIPhoneSwapBuffers();
void SysIPhoneVibrate();
void SysIPhoneOpenURL( const char *url );
void SysIPhoneSetUIKitOrientation( int isLandscapeRight );
const char * SysIPhoneGetConsoleTextField();
void SysIPhoneSetConsoleTextField(const char *);
void SysIPhoneInitAudioSession();
int SysIPhoneOtherAudioIsPlaying();
int SysIphoneMilliseconds();
int SysIphoneMicroseconds();
const char * SysIphoneGetAppDir();
const char * SysIphoneGetDocDir();
//---------------------------------------
// interfaces from Objective-C land
//---------------------------------------
void iphoneStartup();
void iphoneShutdown();
void iphoneFrame();
void iphoneTiltEvent( float *tilts );
void iphoneTouchEvent( int numTouches, int touches[16] );
void iphoneActivateConsole();
void iphoneDeactivateConsole();
void iphoneExecuteCommandLine();
+49
View File
@@ -0,0 +1,49 @@
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import <UIKit/UIKit.h>
#include <unistd.h>
#include <string.h>
extern char iphoneAppDirectory[1024];
extern int myargc;
extern char **myargv;
int main(int argc, char *argv[]) {
// save for doom
myargc = argc;
myargv = argv;
// get the app directory based on argv[0]
strcpy( iphoneAppDirectory, argv[0] );
int len = strlen( iphoneAppDirectory );
for( int i = len-1; i >= 0; i-- ) {
if ( iphoneAppDirectory[i] == '/' ) {
iphoneAppDirectory[i] = 0;
break;
}
iphoneAppDirectory[i] = 0;
}
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
+91
View File
@@ -0,0 +1,91 @@
/*
* misc.c
* doom
*
* Created by John Carmack on 4/13/09.
* Copyright 2009 idSoftware. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
void Com_Printf( const char *fmt, ... ) {
va_list argptr;
va_start( argptr, fmt );
//gsh, send output to the console buffer
char buffer[1024];
vsnprintf( buffer, sizeof( buffer ), fmt, argptr );
AppendConsoleBuffer(buffer);
vprintf( fmt, argptr );
va_end( argptr );
}
void Com_Error( const char *fmt, ... ) {
va_list argptr;
va_start( argptr, fmt );
//gsh, send output to the console buffer
char buffer[1024];
vsnprintf( buffer, sizeof( buffer ), fmt, argptr );
AppendConsoleBuffer(buffer);
vprintf( fmt, argptr );
va_end( argptr );
//gsh, email the console to id
EmailConsole();
// drop into the editor
abort();
exit( 1 );
}
char *va( const char *format, ... ) {
va_list argptr;
static char string[ 1024 ];
va_start( argptr, format );
(void)vsnprintf( string, sizeof( string ), format, argptr );
va_end( argptr );
string[ sizeof( string ) - 1 ] = '\0';
return string;
}
int HashString( const char *string ) {
int hash = *string;
if( hash ) {
for( string += 1; *string != '\0'; ++string ) {
hash = (hash << 5) - hash + tolower(*string);
}
}
return hash;
}
+74
View File
@@ -0,0 +1,74 @@
/*
* misc.h
* doom
*
* Created by John Carmack on 4/13/09.
* Copyright 2009 idSoftware. All rights reserved.
*
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
char *va( const char *format, ... );
void Com_Printf( const char *fmt, ... );
void Com_Error( const char *fmt, ... );
int HashString( const char *string );
/*
Command execution takes a NUL-terminated string, breaks it into tokens,
then searches for a command or variable that matches the first token.
*/
typedef void (*xcommand_t) (void);
// called by the init functions of other parts of the program to
// register commands and functions to call for them.
// The cmd_name is referenced later, so it should not be in temp memory
// if function is NULL, the command will be forwarded to the server
// as a clc_stringcmd instead of executed locally
void Cmd_AddCommand( const char *cmd_name, xcommand_t function );
// print all the added commands
void Cmd_ListCommands_f();
// attempts to match a partial command for automatic command line completion
// returns NULL if nothing fits
char *Cmd_CompleteCommand( const char *partial );
// The functions that execute commands get their parameters with these
// functions. Cmd_Argv () will return an empty string, not a NULL
// if arg > argc, so string operations are always safe.
int Cmd_Argc( void );
const char *Cmd_Argv( int arg );
// Takes a NUL-terminated string. Does not need to be /n terminated.
// breaks the string up into argc / argv tokens.
void Cmd_TokenizeString( const char *text );
// Parses a single line of text into arguments and tries to execute it
// as if it was typed at the console
void Cmd_ExecuteString( const char *text );
// execute each line of the config file
void Cmd_ExecuteFile( const char *fullPathName );
+364
View File
@@ -0,0 +1,364 @@
/*
* prboomInterface.c
* doom
*
* Created by John Carmack on 4/14/09.
* Copyright 2009 Id Software. All rights reserved.
*
* Stuff to get prboom to compile without SDL
*/
/*
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../doomiphone.h"
int desired_fullscreen;
int usejoystick;
int joyright;
int joyleft;
int joydown;
int joyup;
int gl_colorbuffer_bits;
int gl_depthbuffer_bits;
int snd_card;
int mus_card;
int endoom_mode;
int use_fullscreen;
int snd_samplerate;
int ms_to_next_tick;
int realtic_clock_rate;
/* I_SafeExit
* This function is called instead of exit() by functions that might be called
* during the exit process (i.e. after exit() has already been called)
* Prevent infinitely recursive exits -- killough
*
* JDC: we don't do any atexit() calls on iphone, so this shouldn't be necessary
*/
void I_SafeExit(int rc) {
static int has_exited;
if (!has_exited) {
has_exited=rc ? 2 : 1;
exit(rc);
}
}
void I_uSleep( unsigned long usec ) {
usleep( usec );
}
/*
* HasTrailingSlash
*
* cphipps - simple test for trailing slash on dir names
*/
boolean HasTrailingSlash(const char* dn)
{
return ( (dn[strlen(dn)-1] == '/') );
}
char* I_FindFile(const char* wfname, const char* ext)
{
char *p = malloc( 1024 );
sprintf( p, "%s/base/%s", SysIphoneGetAppDir(), wfname );
if (access(p,F_OK))
strcat(p, ext); // try adding the extension
if (!access(p,F_OK)) {
lprintf(LO_INFO, " found %s\n", p);
return p;
}
free( p );
return NULL;
#if 0
// lookup table of directories to search
static const struct {
const char *dir; // directory
const char *sub; // subdirectory
const char *env; // environment variable
const char *(*func)(void); // for I_DoomExeDir
} search[] = {
{NULL}, // current working directory
{NULL, NULL, "DOOMWADDIR"}, // run-time $DOOMWADDIR
{DOOMWADDIR}, // build-time configured DOOMWADDIR
{NULL, "doom", "HOME"}, // ~/doom
{NULL, NULL, "HOME"}, // ~
{NULL, NULL, NULL, I_DoomExeDir}, // config directory
{"/usr/local/share/games/doom"},
{"/usr/share/games/doom"},
{"/usr/local/share/doom"},
{"/usr/share/doom"},
};
int i;
/* Precalculate a length we will need in the loop */
size_t pl = strlen(wfname) + strlen(ext) + 4;
for (i = 0; i < sizeof(search)/sizeof(*search); i++) {
char * p;
const char * d = NULL;
const char * s = NULL;
/* Each entry in the switch sets d to the directory to look in,
* and optionally s to a subdirectory of d */
// switch replaced with lookup table
if (search[i].env) {
if (!(d = getenv(search[i].env)))
continue;
} else if (search[i].func)
d = search[i].func();
else
d = search[i].dir;
s = search[i].sub;
p = malloc((d ? strlen(d) : 0) + (s ? strlen(s) : 0) + pl);
sprintf(p, "%s%s%s%s%s", d ? d : "", (d && !HasTrailingSlash(d)) ? "/" : "",
s ? s : "", (s && !HasTrailingSlash(s)) ? "/" : "",
wfname);
if (access(p,F_OK))
strcat(p, ext);
if (!access(p,F_OK)) {
lprintf(LO_INFO, " found %s\n", p);
return p;
}
free(p);
}
return NULL;
#endif
}
boolean I_StartDisplay(void) {
return true;
}
void I_EndDisplay(void) {}
int I_GetTime_RealTime(void) { return 0; }
fixed_t I_GetTimeFrac (void) { return 0; }
void I_GetTime_SaveMS(void) {}
unsigned long I_GetRandomTimeSeed(void) { return 0; }
//const char* I_GetVersionString(char* buf, size_t sz);
//const char* I_SigString(char* buf, size_t sz, int signum);
const char *I_DoomExeDir(void) { return SysIphoneGetAppDir(); }
//void I_SetAffinityMask(void);
/*
* I_Read
*
* cph 2001/11/18 - wrapper for read(2) which handles partial reads and aborts
* on error.
*/
void I_Read(int fd, void* vbuf, size_t sz)
{
unsigned char* buf = vbuf;
while (sz) {
int rc = read(fd,buf,sz);
if (rc <= 0) {
I_Error("I_Read: read failed: %s", rc ? strerror(errno) : "EOF");
}
sz -= rc; buf += rc;
}
}
/*
* I_Filelength
*
* Return length of an open file.
*/
int I_Filelength(int handle)
{
struct stat fileinfo;
if (fstat(handle,&fileinfo) == -1)
I_Error("I_Filelength: %s",strerror(errno));
return fileinfo.st_size;
}
//
// MUSIC I/O
//
void I_InitMusic(void) {}
void I_ShutdownMusic(void) {}
void I_UpdateMusic(void) {}
// Volume.
void I_SetMusicVolume(int volume) {}
// PAUSE game handling.
void I_PauseSong(int handle) {}
void I_ResumeSong(int handle) {}
// Registers a song handle to song data.
int I_RegisterSong(const void *data, size_t len) {
return 0;
}
// cournia - tries to load a music file
int I_RegisterMusic( const char* filename, musicinfo_t *music ) {
return 0;
}
// Called by anything that wishes to start music.
// plays a song, and when the song is done,
// starts playing it again in an endless loop.
// Horrible thing to do, considering.
void I_PlaySong(int handle, int looping) {
}
// Stops a song over 3 seconds.
void I_StopSong(int handle) {
}
// See above (register), then think backwards
void I_UnRegisterSong(int handle) {
}
void I_PreInitGraphics(void){}
void I_CalculateRes(unsigned int width, unsigned int height){}
void I_ShutdownGraphics(void){}
void I_SetPalette(int pal){}
void I_UpdateNoBlit (void){}
void I_FinishUpdate (void){}
int I_ScreenShot (const char *fname){return 0;}
// CPhipps -
// I_SetRes
// Sets the screen resolution
void I_SetRes(void)
{
int i;
I_CalculateRes(SCREENWIDTH, SCREENHEIGHT);
// set first three to standard values
for (i=0; i<3; i++) {
screens[i].width = SCREENWIDTH;
screens[i].height = SCREENHEIGHT;
screens[i].byte_pitch = SCREENPITCH;
screens[i].short_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE16);
screens[i].int_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE32);
}
// statusbar
screens[4].width = SCREENWIDTH;
screens[4].height = (ST_SCALED_HEIGHT+1);
screens[4].byte_pitch = SCREENPITCH;
screens[4].short_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE16);
screens[4].int_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE32);
lprintf(LO_INFO,"I_SetRes: Using resolution %dx%d\n", SCREENWIDTH, SCREENHEIGHT);
}
void I_UpdateVideoMode(void)
{
lprintf(LO_INFO, "I_UpdateVideoMode: %dx%d\n", SCREENWIDTH, SCREENHEIGHT );
V_InitMode(VID_MODEGL);
I_SetRes();
#if 0
screens[0].not_on_heap = true;
screens[0].data = NULL;
screens[0].byte_pitch = screen->pitch;
screens[0].short_pitch = screen->pitch / V_GetModePixelDepth(VID_MODE16);
screens[0].int_pitch = screen->pitch / V_GetModePixelDepth(VID_MODE32);
#endif
V_AllocScreens();
R_InitBuffer(SCREENWIDTH, SCREENHEIGHT);
gld_Init(SCREENWIDTH, SCREENHEIGHT);
}
void I_InitGraphics(void)
{
char titlebuffer[2048];
static int firsttime=1;
SCREENWIDTH = 480;
SCREENHEIGHT = 320;
if (firsttime)
{
firsttime = 0;
atexit(I_ShutdownGraphics);
lprintf(LO_INFO, "I_InitGraphics: %dx%d\n", SCREENWIDTH, SCREENHEIGHT);
/* Set the video mode */
I_UpdateVideoMode();
/* Setup the window title */
strcpy(titlebuffer,PACKAGE);
strcat(titlebuffer," ");
strcat(titlebuffer,VERSION);
// SDL_WM_SetCaption(titlebuffer, titlebuffer);
/* Initialize the input system */
// I_InitInputs();
}
}
/* I_StartTic
* Called by D_DoomLoop,
* called before processing each tic in a frame.
* Quick syncronous operations are performed here.
* Can call D_PostEvent.
*/
void I_StartTic (void){}
/* I_StartFrame
* Called by D_DoomLoop,
* called before processing any tics in a frame
* (just after displaying a frame).
* Time consuming syncronous operations
* are performed here (joystick reading).
* Can call D_PostEvent.
*/
void I_StartFrame (void){}
void I_Init(){}
unsigned int SDL_GetTicks() { return 0; };
int (*I_GetTime)(void) = I_GetTime_RealTime;
+446
View File
@@ -0,0 +1,446 @@
/*
*/
General Polygon Tesselation
---------------------------
This note describes a tesselator for polygons consisting of one or
more closed contours. It is backward-compatible with the current
OpenGL Utilities tesselator, and is intended to replace it. Here is
a summary of the major differences:
- input contours can be intersecting, self-intersecting, or degenerate.
- supports a choice of several winding rules for determining which parts
of the polygon are on the "interior". This makes it possible to do
CSG operations on polygons.
- boundary extraction: instead of tesselating the polygon, returns a
set of closed contours which separate the interior from the exterior.
- returns the output as a small number of triangle fans and strips,
rather than a list of independent triangles (when possible).
- output is available as an explicit mesh (a quad-edge structure),
in addition to the normal callback interface.
- the algorithm used is extremely robust.
The interface
-------------
The tesselator state is maintained in a "tesselator object".
These are allocated and destroyed using
GLUtesselator *gluNewTess( void );
void gluDeleteTess( GLUtesselator *tess );
Several tesselator objects may be used simultaneously.
Inputs
------
The input contours are specified with the following routines:
void gluTessBeginPolygon( GLUtesselator *tess );
void gluTessBeginContour( GLUtesselator *tess );
void gluTessVertex( GLUtesselator *tess, GLUcoord coords[3], void *data );
void gluTessEndContour( GLUtesselator *tess );
void gluTessEndPolygon( GLUtesselator *tess );
Within each BeginPolygon/EndPolygon pair, there can be zero or more
calls to BeginContour/EndContour. Within each contour, there are zero
or more calls to gluTessVertex(). The vertices specify a closed
contour (the last vertex of each contour is automatically linked to
the first).
"coords" give the coordinates of the vertex in 3-space. For useful
results, all vertices should lie in some plane, since the vertices
are projected onto a plane before tesselation. "data" is a pointer
to a user-defined vertex structure, which typically contains other
information such as color, texture coordinates, normal, etc. It is
used to refer to the vertex during rendering.
The library can be compiled in single- or double-precision; the type
GLUcoord represents either "float" or "double" accordingly. The GLU
version will be available in double-precision only. Compile with
GLU_TESS_API_FLOAT defined to get the single-precision version.
When EndPolygon is called, the tesselation algorithm determines
which regions are interior to the given contours, according to one
of several "winding rules" described below. The interior regions
are then tesselated, and the output is provided as callbacks.
Rendering Callbacks
-------------------
Callbacks are specified by the client using
void gluTessCallback( GLUtesselator *tess, GLenum which, void (*fn)());
If "fn" is NULL, any previously defined callback is discarded.
The callbacks used to provide output are: /* which == */
void begin( GLenum type ); /* GLU_TESS_BEGIN */
void edgeFlag( GLboolean flag ); /* GLU_TESS_EDGE_FLAG */
void vertex( void *data ); /* GLU_TESS_VERTEX */
void end( void ); /* GLU_TESS_END */
Any of the callbacks may be left undefined; if so, the corresponding
information will not be supplied during rendering.
The "begin" callback indicates the start of a primitive; type is one
of GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES (but see the
notes on "boundary extraction" below).
It is followed by any number of "vertex" callbacks, which supply the
vertices in the same order as expected by the corresponding glBegin()
call. After the last vertex of a given primitive, there is a callback
to "end".
If the "edgeFlag" callback is provided, no triangle fans or strips
will be used. When edgeFlag is called, if "flag" is GL_TRUE then each
vertex which follows begins an edge which lies on the polygon boundary
(ie. an edge which separates an interior region from an exterior one).
If "flag" is GL_FALSE, each vertex which follows begins an edge which lies
in the polygon interior. "edgeFlag" will be called before the first
call to "vertex".
Other Callbacks
---------------
void mesh( GLUmesh *mesh ); /* GLU_TESS_MESH */
- Returns an explicit mesh, represented using the quad-edge structure
(Guibas/Stolfi '85). Other implementations of this interface might
use a different mesh structure, so this is available only only as an
SGI extension. When the mesh is no longer needed, it should be freed
using
void gluDeleteMesh( GLUmesh *mesh );
There is a brief description of this data structure in the include
file "mesh.h". For the full details, see L. Guibas and J. Stolfi,
Primitives for the manipulation of general subdivisions and the
computation of Voronoi diagrams, ACM Transactions on Graphics,
4(2):74-123, April 1985. For an introduction, see the course notes
for CS348a, "Mathematical Foundations of Computer Graphics",
available at the Stanford bookstore (and taught during the fall
quarter).
void error( GLenum errno ); /* GLU_TESS_ERROR */
- errno is one of GLU_TESS_MISSING_BEGIN_POLYGON,
GLU_TESS_MISSING_END_POLYGON,
GLU_TESS_MISSING_BEGIN_CONTOUR,
GLU_TESS_MISSING_END_CONTOUR,
GLU_TESS_COORD_TOO_LARGE,
GLU_TESS_NEED_COMBINE_CALLBACK
The first four are obvious. The interface recovers from these
errors by inserting the missing call(s).
GLU_TESS_COORD_TOO_LARGE says that some vertex coordinate exceeded
the predefined constant GLU_TESS_MAX_COORD in absolute value, and
that the value has been clamped. (Coordinate values must be small
enough so that two can be multiplied together without overflow.)
GLU_TESS_NEED_COMBINE_CALLBACK says that the algorithm detected an
intersection between two edges in the input data, and the "combine"
callback (below) was not provided. No output will be generated.
void combine( GLUcoord coords[3], void *data[4], /* GLU_TESS_COMBINE */
GLUcoord weight[4], void **outData );
- When the algorithm detects an intersection, or wishes to merge
features, it needs to create a new vertex. The vertex is defined
as a linear combination of up to 4 existing vertices, referenced
by data[0..3]. The coefficients of the linear combination are
given by weight[0..3]; these weights always sum to 1.0. All vertex
pointers are valid even when some of the weights are zero.
"coords" gives the location of the new vertex.
The user must allocate another vertex, interpolate parameters
using "data" and "weights", and return the new vertex pointer in
"outData". This handle is supplied during rendering callbacks.
For example, if the polygon lies in an arbitrary plane in 3-space,
and we associate a color with each vertex, the combine callback might
look like this:
void myCombine( GLUcoord coords[3], VERTEX *d[4],
GLUcoord w[4], VERTEX **dataOut )
{
VERTEX *new = new_vertex();
new->x = coords[0];
new->y = coords[1];
new->z = coords[2];
new->r = w[0]*d[0]->r + w[1]*d[1]->r + w[2]*d[2]->r + w[3]*d[3]->r;
new->g = w[0]*d[0]->g + w[1]*d[1]->g + w[2]*d[2]->g + w[3]*d[3]->g;
new->b = w[0]*d[0]->b + w[1]*d[1]->b + w[2]*d[2]->b + w[3]*d[3]->b;
new->a = w[0]*d[0]->a + w[1]*d[1]->a + w[2]*d[2]->a + w[3]*d[3]->a;
*dataOut = new;
}
If the algorithm detects an intersection, then the "combine" callback
must be defined, and must write a non-NULL pointer into "dataOut".
Otherwise the GLU_TESS_NEED_COMBINE_CALLBACK error occurs, and no
output is generated. This is the only error that can occur during
tesselation and rendering.
Control over Tesselation
------------------------
void gluTessProperty( GLUtesselator *tess, GLenum which, GLUcoord value );
Properties defined:
- GLU_TESS_WINDING_RULE. Possible values:
GLU_TESS_WINDING_ODD
GLU_TESS_WINDING_NONZERO
GLU_TESS_WINDING_POSITIVE
GLU_TESS_WINDING_NEGATIVE
GLU_TESS_WINDING_ABS_GEQ_TWO
The input contours parition the plane into regions. A winding
rule determines which of these regions are inside the polygon.
For a single contour C, the winding number of a point x is simply
the signed number of revolutions we make around x as we travel
once around C (where CCW is positive). When there are several
contours, the individual winding numbers are summed. This
procedure associates a signed integer value with each point x in
the plane. Note that the winding number is the same for all
points in a single region.
The winding rule classifies a region as "inside" if its winding
number belongs to the chosen category (odd, nonzero, positive,
negative, or absolute value of at least two). The current GLU
tesselator implements the "odd" rule. The "nonzero" rule is another
common way to define the interior. The other three rules are
useful for polygon CSG operations (see below).
- GLU_TESS_BOUNDARY_ONLY. Values: TRUE (non-zero) or FALSE (zero).
If TRUE, returns a set of closed contours which separate the
polygon interior and exterior (rather than a tesselation).
Exterior contours are oriented CCW with respect to the normal,
interior contours are oriented CW. The GLU_TESS_BEGIN callback
uses the type GL_LINE_LOOP for each contour.
- GLU_TESS_TOLERANCE. Value: a real number between 0.0 and 1.0.
This specifies a tolerance for merging features to reduce the size
of the output. For example, two vertices which are very close to
each other might be replaced by a single vertex. The tolerance
is multiplied by the largest coordinate magnitude of any input vertex;
this specifies the maximum distance that any feature can move as the
result of a single merge operation. If a single feature takes part
in several merge operations, the total distance moved could be larger.
Feature merging is completely optional; the tolerance is only a hint.
The implementation is free to merge in some cases and not in others,
or to never merge features at all. The default tolerance is zero.
The current implementation merges vertices only if they are exactly
coincident, regardless of the current tolerance. A vertex is
spliced into an edge only if the implementation is unable to
distinguish which side of the edge the vertex lies on.
Two edges are merged only when both endpoints are identical.
void gluTessNormal( GLUtesselator *tess,
GLUcoord x, GLUcoord y, GLUcoord z )
- Lets the user supply the polygon normal, if known. All input data
is projected into a plane perpendicular to the normal before
tesselation. All output triangles are oriented CCW with
respect to the normal (CW orientation can be obtained by
reversing the sign of the supplied normal). For example, if
you know that all polygons lie in the x-y plane, call
"gluTessNormal(tess, 0.0, 0.0, 1.0)" before rendering any polygons.
- If the supplied normal is (0,0,0) (the default value), the
normal is determined as follows. The direction of the normal,
up to its sign, is found by fitting a plane to the vertices,
without regard to how the vertices are connected. It is
expected that the input data lies approximately in plane;
otherwise projection perpendicular to the computed normal may
substantially change the geometry. The sign of the normal is
chosen so that the sum of the signed areas of all input contours
is non-negative (where a CCW contour has positive area).
- The supplied normal persists until it is changed by another
call to gluTessNormal.
Backward compatibility with the GLU tesselator
----------------------------------------------
The preferred interface is the one described above. The following
routines are obsolete, and are provided only for backward compatibility:
typedef GLUtesselator GLUtriangulatorObj; /* obsolete name */
void gluBeginPolygon( GLUtesselator *tess );
void gluNextContour( GLUtesselator *tess, GLenum type );
void gluEndPolygon( GLUtesselator *tess );
"type" is one of GLU_EXTERIOR, GLU_INTERIOR, GLU_CCW, GLU_CW, or
GLU_UNKNOWN. It is ignored by the current GLU tesselator.
GLU_BEGIN, GLU_VERTEX, GLU_END, GLU_ERROR, and GLU_EDGE_FLAG are defined
as synonyms for GLU_TESS_BEGIN, GLU_TESS_VERTEX, GLU_TESS_END,
GLU_TESS_ERROR, and GLU_TESS_EDGE_FLAG.
Polygon CSG operations
----------------------
The features of the tesselator make it easy to find the union, difference,
or intersection of several polygons.
First, assume that each polygon is defined so that the winding number
is 0 for each exterior region, and 1 for each interior region. Under
this model, CCW contours define the outer boundary of the polygon, and
CW contours define holes. Contours may be nested, but a nested
contour must be oriented oppositely from the contour that contains it.
If the original polygons do not satisfy this description, they can be
converted to this form by first running the tesselator with the
GLU_TESS_BOUNDARY_ONLY property turned on. This returns a list of
contours satisfying the restriction above. By allocating two
tesselator objects, the callbacks from one tesselator can be fed
directly to the input of another.
Given two or more polygons of the form above, CSG operations can be
implemented as follows:
Union
Draw all the input contours as a single polygon. The winding number
of each resulting region is the number of original polygons
which cover it. The union can be extracted using the
GLU_TESS_WINDING_NONZERO or GLU_TESS_WINDING_POSITIVE winding rules.
Note that with the nonzero rule, we would get the same result if
all contour orientations were reversed.
Intersection (two polygons at a time only)
Draw a single polygon using the contours from both input polygons.
Extract the result using GLU_TESS_WINDING_ABS_GEQ_TWO. (Since this
winding rule looks at the absolute value, reversing all contour
orientations does not change the result.)
Difference
Suppose we want to compute A \ (B union C union D). Draw a single
polygon consisting of the unmodified contours from A, followed by
the contours of B,C,D with the vertex order reversed (this changes
the winding number of the interior regions to -1). To extract the
result, use the GLU_TESS_WINDING_POSITIVE rule.
If B,C,D are the result of a GLU_TESS_BOUNDARY_ONLY call, an
alternative to reversing the vertex order is to reverse the sign of
the supplied normal. For example in the x-y plane, call
gluTessNormal( tess, 0.0, 0.0, -1.0 ).
Performance
-----------
The tesselator is not intended for immediate-mode rendering; when
possible the output should be cached in a user structure or display
list. General polygon tesselation is an inherently difficult problem,
especially given the goal of extreme robustness.
The implementation makes an effort to output a small number of fans
and strips; this should improve the rendering performance when the
output is used in a display list.
Single-contour input polygons are first tested to see whether they can
be rendered as a triangle fan with respect to the first vertex (to
avoid running the full decomposition algorithm on convex polygons).
Non-convex polygons may be rendered by this "fast path" as well, if
the algorithm gets lucky in its choice of a starting vertex.
For best performance follow these guidelines:
- supply the polygon normal, if available, using gluTessNormal().
This represents about 10% of the computation time. For example,
if all polygons lie in the x-y plane, use gluTessNormal(tess,0,0,1).
- render many polygons using the same tesselator object, rather than
allocating a new tesselator for each one. (In a multi-threaded,
multi-processor environment you may get better performance using
several tesselators.)
Comparison with the GLU tesselator
----------------------------------
On polygons which make it through the "fast path", the tesselator is
3 to 5 times faster than the GLU tesselator.
On polygons which don't make it through the fast path (but which don't
have self-intersections or degeneracies), it is about 2 times slower.
On polygons with self-intersections or degeneraces, there is nothing
to compare against.
The new tesselator generates many more fans and strips, reducing the
number of vertices that need to be sent to the hardware.
Key to the statistics:
vert number of input vertices on all contours
cntr number of input contours
tri number of triangles in all output primitives
strip number of triangle strips
fan number of triangle fans
ind number of independent triangles
ms number of milliseconds for tesselation
(on a 150MHz R4400 Indy)
Convex polygon examples:
New: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.0459 ms
Old: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.149 ms
New: 4 vert, 1 cntr, 2 tri, 0 strip, 1 fan, 0 ind, 0.0459 ms
Old: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.161 ms
New: 36 vert, 1 cntr, 34 tri, 0 strip, 1 fan, 0 ind, 0.153 ms
Old: 36 vert, 1 cntr, 34 tri, 0 strip, 0 fan, 34 ind, 0.621 ms
Concave single-contour polygons:
New: 5 vert, 1 cntr, 3 tri, 0 strip, 1 fan, 0 ind, 0.052 ms
Old: 5 vert, 1 cntr, 3 tri, 0 strip, 0 fan, 3 ind, 0.252 ms
New: 19 vert, 1 cntr, 17 tri, 2 strip, 2 fan, 1 ind, 0.911 ms
Old: 19 vert, 1 cntr, 17 tri, 0 strip, 0 fan, 17 ind, 0.529 ms
New: 151 vert, 1 cntr, 149 tri, 13 strip, 18 fan, 3 ind, 6.82 ms
Old: 151 vert, 1 cntr, 149 tri, 0 strip, 3 fan, 143 ind, 2.7 ms
New: 574 vert, 1 cntr, 572 tri, 59 strip, 54 fan, 11 ind, 26.6 ms
Old: 574 vert, 1 cntr, 572 tri, 0 strip, 31 fan, 499 ind, 12.4 ms
Multiple contours, but no intersections:
New: 7 vert, 2 cntr, 7 tri, 1 strip, 0 fan, 0 ind, 0.527 ms
Old: 7 vert, 2 cntr, 7 tri, 0 strip, 0 fan, 7 ind, 0.274 ms
New: 81 vert, 6 cntr, 89 tri, 9 strip, 7 fan, 6 ind, 3.88 ms
Old: 81 vert, 6 cntr, 89 tri, 0 strip, 13 fan, 61 ind, 2.2 ms
New: 391 vert, 19 cntr, 413 tri, 37 strip, 32 fan, 26 ind, 20.2 ms
Old: 391 vert, 19 cntr, 413 tri, 0 strip, 25 fan, 363 ind, 8.68 ms
Self-intersecting and degenerate examples:
Bowtie: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.483 ms
Star: 5 vert, 1 cntr, 5 tri, 0 strip, 0 fan, 5 ind, 0.91 ms
Random: 24 vert, 7 cntr, 46 tri, 2 strip, 12 fan, 7 ind, 5.32 ms
Font: 333 vert, 2 cntr, 331 tri, 32 strip, 16 fan, 3 ind, 14.1 ms
: 167 vert, 35 cntr, 254 tri, 8 strip, 56 fan, 52 ind, 46.3 ms
: 78 vert, 1 cntr, 2675 tri, 148 strip, 207 fan, 180 ind, 243 ms
: 12480 vert, 2 cntr, 12478 tri, 736 strip,1275 fan, 5 ind, 1010 ms
+228
View File
@@ -0,0 +1,228 @@
/*
*/
This is only a very brief overview. There is quite a bit of
additional documentation in the source code itself.
Goals of robust tesselation
---------------------------
The tesselation algorithm is fundamentally a 2D algorithm. We
initially project all data into a plane; our goal is to robustly
tesselate the projected data. The same topological tesselation is
then applied to the input data.
Topologically, the output should always be a tesselation. If the
input is even slightly non-planar, then some triangles will
necessarily be back-facing when viewed from some angles, but the goal
is to minimize this effect.
The algorithm needs some capability of cleaning up the input data as
well as the numerical errors in its own calculations. One way to do
this is to specify a tolerance as defined above, and clean up the
input and output during the line sweep process. At the very least,
the algorithm must handle coincident vertices, vertices incident to an
edge, and coincident edges.
Phases of the algorithm
-----------------------
1. Find the polygon normal N.
2. Project the vertex data onto a plane. It does not need to be
perpendicular to the normal, eg. we can project onto the plane
perpendicular to the coordinate axis whose dot product with N
is largest.
3. Using a line-sweep algorithm, partition the plane into x-monotone
regions. Any vertical line intersects an x-monotone region in
at most one interval.
4. Triangulate the x-monotone regions.
5. Group the triangles into strips and fans.
Finding the normal vector
-------------------------
A common way to find a polygon normal is to compute the signed area
when the polygon is projected along the three coordinate axes. We
can't do this, since contours can have zero area without being
degenerate (eg. a bowtie).
We fit a plane to the vertex data, ignoring how they are connected
into contours. Ideally this would be a least-squares fit; however for
our purpose the accuracy of the normal is not important. Instead we
find three vertices which are widely separated, and compute the normal
to the triangle they form. The vertices are chosen so that the
triangle has an area at least 1/sqrt(3) times the largest area of any
triangle formed using the input vertices.
The contours do affect the orientation of the normal; after computing
the normal, we check that the sum of the signed contour areas is
non-negative, and reverse the normal if necessary.
Projecting the vertices
-----------------------
We project the vertices onto a plane perpendicular to one of the three
coordinate axes. This helps numerical accuracy by removing a
transformation step between the original input data and the data
processed by the algorithm. The projection also compresses the input
data; the 2D distance between vertices after projection may be smaller
than the original 2D distance. However by choosing the coordinate
axis whose dot product with the normal is greatest, the compression
factor is at most 1/sqrt(3).
Even though the *accuracy* of the normal is not that important (since
we are projecting perpendicular to a coordinate axis anyway), the
*robustness* of the computation is important. For example, if there
are many vertices which lie almost along a line, and one vertex V
which is well-separated from the line, then our normal computation
should involve V otherwise the results will be garbage.
The advantage of projecting perpendicular to the polygon normal is
that computed intersection points will be as close as possible to
their ideal locations. To get this behavior, define TRUE_PROJECT.
The Line Sweep
--------------
There are three data structures: the mesh, the event queue, and the
edge dictionary.
The mesh is a "quad-edge" data structure which records the topology of
the current decomposition; for details see the include file "mesh.h".
The event queue simply holds all vertices (both original and computed
ones), organized so that we can quickly extract the vertex with the
minimum x-coord (and among those, the one with the minimum y-coord).
The edge dictionary describes the current intersection of the sweep
line with the regions of the polygon. This is just an ordering of the
edges which intersect the sweep line, sorted by their current order of
intersection. For each pair of edges, we store some information about
the monotone region between them -- these are call "active regions"
(since they are crossed by the current sweep line).
The basic algorithm is to sweep from left to right, processing each
vertex. The processed portion of the mesh (left of the sweep line) is
a planar decomposition. As we cross each vertex, we update the mesh
and the edge dictionary, then we check any newly adjacent pairs of
edges to see if they intersect.
A vertex can have any number of edges. Vertices with many edges can
be created as vertices are merged and intersection points are
computed. For unprocessed vertices (right of the sweep line), these
edges are in no particular order around the vertex; for processed
vertices, the topological ordering should match the geometric ordering.
The vertex processing happens in two phases: first we process are the
left-going edges (all these edges are currently in the edge
dictionary). This involves:
- deleting the left-going edges from the dictionary;
- relinking the mesh if necessary, so that the order of these edges around
the event vertex matches the order in the dictionary;
- marking any terminated regions (regions which lie between two left-going
edges) as either "inside" or "outside" according to their winding number.
When there are no left-going edges, and the event vertex is in an
"interior" region, we need to add an edge (to split the region into
monotone pieces). To do this we simply join the event vertex to the
rightmost left endpoint of the upper or lower edge of the containing
region.
Then we process the right-going edges. This involves:
- inserting the edges in the edge dictionary;
- computing the winding number of any newly created active regions.
We can compute this incrementally using the winding of each edge
that we cross as we walk through the dictionary.
- relinking the mesh if necessary, so that the order of these edges around
the event vertex matches the order in the dictionary;
- checking any newly adjacent edges for intersection and/or merging.
If there are no right-going edges, again we need to add one to split
the containing region into monotone pieces. In our case it is most
convenient to add an edge to the leftmost right endpoint of either
containing edge; however we may need to change this later (see the
code for details).
Invariants
----------
These are the most important invariants maintained during the sweep.
We define a function VertLeq(v1,v2) which defines the order in which
vertices cross the sweep line, and a function EdgeLeq(e1,e2; loc)
which says whether e1 is below e2 at the sweep event location "loc".
This function is defined only at sweep event locations which lie
between the rightmost left endpoint of {e1,e2}, and the leftmost right
endpoint of {e1,e2}.
Invariants for the Edge Dictionary.
- Each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)
at any valid location of the sweep event.
- If EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2
share a common endpoint.
- For each e in the dictionary, e->Dst has been processed but not e->Org.
- Each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org)
where "event" is the current sweep line event.
- No edge e has zero length.
- No two edges have identical left and right endpoints.
Invariants for the Mesh (the processed portion).
- The portion of the mesh left of the sweep line is a planar graph,
ie. there is *some* way to embed it in the plane.
- No processed edge has zero length.
- No two processed vertices have identical coordinates.
- Each "inside" region is monotone, ie. can be broken into two chains
of monotonically increasing vertices according to VertLeq(v1,v2)
- a non-invariant: these chains may intersect (slightly) due to
numerical errors, but this does not affect the algorithm's operation.
Invariants for the Sweep.
- If a vertex has any left-going edges, then these must be in the edge
dictionary at the time the vertex is processed.
- If an edge is marked "fixUpperEdge" (it is a temporary edge introduced
by ConnectRightVertex), then it is the only right-going edge from
its associated vertex. (This says that these edges exist only
when it is necessary.)
Robustness
----------
The key to the robustness of the algorithm is maintaining the
invariants above, especially the correct ordering of the edge
dictionary. We achieve this by:
1. Writing the numerical computations for maximum precision rather
than maximum speed.
2. Making no assumptions at all about the results of the edge
intersection calculations -- for sufficiently degenerate inputs,
the computed location is not much better than a random number.
3. When numerical errors violate the invariants, restore them
by making *topological* changes when necessary (ie. relinking
the mesh structure).
Triangulation and Grouping
--------------------------
We finish the line sweep before doing any triangulation. This is
because even after a monotone region is complete, there can be further
changes to its vertex data because of further vertex merging.
After triangulating all monotone regions, we want to group the
triangles into fans and strips. We do this using a greedy approach.
The triangulation itself is not optimized to reduce the number of
primitives; we just try to get a reasonable decomposition of the
computed triangulation.
+100
View File
@@ -0,0 +1,100 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __dict_list_h_
#define __dict_list_h_
/* Use #define's so that another heap implementation can use this one */
#define DictKey DictListKey
#define Dict DictList
#define DictNode DictListNode
#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq)
#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict)
#define dictSearch(dict,key) __gl_dictListSearch(dict,key)
#define dictInsert(dict,key) __gl_dictListInsert(dict,key)
#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key)
#define dictDelete(dict,node) __gl_dictListDelete(dict,node)
#define dictKey(n) __gl_dictListKey(n)
#define dictSucc(n) __gl_dictListSucc(n)
#define dictPred(n) __gl_dictListPred(n)
#define dictMin(d) __gl_dictListMin(d)
#define dictMax(d) __gl_dictListMax(d)
typedef void *DictKey;
typedef struct Dict Dict;
typedef struct DictNode DictNode;
Dict *dictNewDict(
void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) );
void dictDeleteDict( Dict *dict );
/* Search returns the node with the smallest key greater than or equal
* to the given key. If there is no such key, returns a node whose
* key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc.
*/
DictNode *dictSearch( Dict *dict, DictKey key );
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
void dictDelete( Dict *dict, DictNode *node );
#define __gl_dictListKey(n) ((n)->key)
#define __gl_dictListSucc(n) ((n)->next)
#define __gl_dictListPred(n) ((n)->prev)
#define __gl_dictListMin(d) ((d)->head.next)
#define __gl_dictListMax(d) ((d)->head.prev)
#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
/*** Private data structures ***/
struct DictNode {
DictKey key;
DictNode *next;
DictNode *prev;
};
struct Dict {
DictNode head;
void *frame;
int (*leq)(void *frame, DictKey key1, DictKey key2);
};
#endif
+111
View File
@@ -0,0 +1,111 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include <stddef.h>
#include "dict-list.h"
#include "memalloc.h"
/* really __gl_dictListNewDict */
Dict *dictNewDict( void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) )
{
Dict *dict = (Dict *) memAlloc( sizeof( Dict ));
DictNode *head;
if (dict == NULL) return NULL;
head = &dict->head;
head->key = NULL;
head->next = head;
head->prev = head;
dict->frame = frame;
dict->leq = leq;
return dict;
}
/* really __gl_dictListDeleteDict */
void dictDeleteDict( Dict *dict )
{
DictNode *node, *next;
for( node = dict->head.next; node != &dict->head; node = next ) {
next = node->next;
memFree( node );
}
memFree( dict );
}
/* really __gl_dictListInsertBefore */
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key )
{
DictNode *newNode;
do {
node = node->prev;
} while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key));
newNode = (DictNode *) memAlloc( sizeof( DictNode ));
if (newNode == NULL) return NULL;
newNode->key = key;
newNode->next = node->next;
node->next->prev = newNode;
newNode->prev = node;
node->next = newNode;
return newNode;
}
/* really __gl_dictListDelete */
void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/
{
node->next->prev = node->prev;
node->prev->next = node->next;
memFree( node );
}
/* really __gl_dictListSearch */
DictNode *dictSearch( Dict *dict, DictKey key )
{
DictNode *node = &dict->head;
do {
node = node->next;
} while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key));
return node;
}
+100
View File
@@ -0,0 +1,100 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __dict_list_h_
#define __dict_list_h_
/* Use #define's so that another heap implementation can use this one */
#define DictKey DictListKey
#define Dict DictList
#define DictNode DictListNode
#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq)
#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict)
#define dictSearch(dict,key) __gl_dictListSearch(dict,key)
#define dictInsert(dict,key) __gl_dictListInsert(dict,key)
#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key)
#define dictDelete(dict,node) __gl_dictListDelete(dict,node)
#define dictKey(n) __gl_dictListKey(n)
#define dictSucc(n) __gl_dictListSucc(n)
#define dictPred(n) __gl_dictListPred(n)
#define dictMin(d) __gl_dictListMin(d)
#define dictMax(d) __gl_dictListMax(d)
typedef void *DictKey;
typedef struct Dict Dict;
typedef struct DictNode DictNode;
Dict *dictNewDict(
void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) );
void dictDeleteDict( Dict *dict );
/* Search returns the node with the smallest key greater than or equal
* to the given key. If there is no such key, returns a node whose
* key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc.
*/
DictNode *dictSearch( Dict *dict, DictKey key );
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
void dictDelete( Dict *dict, DictNode *node );
#define __gl_dictListKey(n) ((n)->key)
#define __gl_dictListSucc(n) ((n)->next)
#define __gl_dictListPred(n) ((n)->prev)
#define __gl_dictListMin(d) ((d)->head.next)
#define __gl_dictListMax(d) ((d)->head.prev)
#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
/*** Private data structures ***/
struct DictNode {
DictKey key;
DictNode *next;
DictNode *prev;
};
struct Dict {
DictNode head;
void *frame;
int (*leq)(void *frame, DictKey key1, DictKey key2);
};
#endif
+265
View File
@@ -0,0 +1,265 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "../prboom/SDL_opengl.h" // JDC
//#include "gluos.h"
#include <assert.h>
#include "mesh.h"
#include "geom.h"
int __gl_vertLeq( GLUvertex *u, GLUvertex *v )
{
/* Returns TRUE if u is lexicographically <= v. */
return VertLeq( u, v );
}
GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
* Returns v->t - (uw)(v->s), ie. the signed distance from uw to v.
* If uw is vertical (and thus passes thru v), the result is zero.
*
* The calculation is extremely accurate and stable, even when v
* is very close to u or w. In particular if we set v->t = 0 and
* let r be the negated result (this evaluates (uw)(v->s)), then
* r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t).
*/
GLdouble gapL, gapR;
assert( VertLeq( u, v ) && VertLeq( v, w ));
gapL = v->s - u->s;
gapR = w->s - v->s;
if( gapL + gapR > 0 ) {
if( gapL < gapR ) {
return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR));
} else {
return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR));
}
}
/* vertical line */
return 0;
}
GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Returns a number whose sign matches EdgeEval(u,v,w) but which
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
* as v is above, on, or below the edge uw.
*/
GLdouble gapL, gapR;
assert( VertLeq( u, v ) && VertLeq( v, w ));
gapL = v->s - u->s;
gapR = w->s - v->s;
if( gapL + gapR > 0 ) {
return (v->t - w->t) * gapL + (v->t - u->t) * gapR;
}
/* vertical line */
return 0;
}
/***********************************************************************
* Define versions of EdgeSign, EdgeEval with s and t transposed.
*/
GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w),
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
* Returns v->s - (uw)(v->t), ie. the signed distance from uw to v.
* If uw is vertical (and thus passes thru v), the result is zero.
*
* The calculation is extremely accurate and stable, even when v
* is very close to u or w. In particular if we set v->s = 0 and
* let r be the negated result (this evaluates (uw)(v->t)), then
* r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s).
*/
GLdouble gapL, gapR;
assert( TransLeq( u, v ) && TransLeq( v, w ));
gapL = v->t - u->t;
gapR = w->t - v->t;
if( gapL + gapR > 0 ) {
if( gapL < gapR ) {
return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR));
} else {
return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR));
}
}
/* vertical line */
return 0;
}
GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Returns a number whose sign matches TransEval(u,v,w) but which
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
* as v is above, on, or below the edge uw.
*/
GLdouble gapL, gapR;
assert( TransLeq( u, v ) && TransLeq( v, w ));
gapL = v->t - u->t;
gapR = w->t - v->t;
if( gapL + gapR > 0 ) {
return (v->s - w->s) * gapL + (v->s - u->s) * gapR;
}
/* vertical line */
return 0;
}
int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* For almost-degenerate situations, the results are not reliable.
* Unless the floating-point arithmetic can be performed without
* rounding errors, *any* implementation will give incorrect results
* on some degenerate inputs, so the client must have some way to
* handle this situation.
*/
return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0;
}
/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
* or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces
* this in the rare case that one argument is slightly negative.
* The implementation is extremely stable numerically.
* In particular it guarantees that the result r satisfies
* MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
* even when a and b differ greatly in magnitude.
*/
#define RealInterpolate(a,x,b,y) \
(a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \
((a <= b) ? ((b == 0) ? ((x+y) / 2) \
: (x + (y-x) * (a/(a+b)))) \
: (y + (x-y) * (b/(a+b)))))
#ifndef FOR_TRITE_TEST_PROGRAM
#define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y)
#else
/* Claim: the ONLY property the sweep algorithm relies on is that
* MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that.
*/
#include <stdlib.h>
extern int RandomInterpolate;
GLdouble Interpolate( GLdouble a, GLdouble x, GLdouble b, GLdouble y)
{
printf("*********************%d\n",RandomInterpolate);
if( RandomInterpolate ) {
a = 1.2 * drand48() - 0.1;
a = (a < 0) ? 0 : ((a > 1) ? 1 : a);
b = 1.0 - a;
}
return RealInterpolate(a,x,b,y);
}
#endif
#define Swap(a,b) if (1) { GLUvertex *t = a; a = b; b = t; } else
void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1,
GLUvertex *o2, GLUvertex *d2,
GLUvertex *v )
/* Given edges (o1,d1) and (o2,d2), compute their point of intersection.
* The computed point is guaranteed to lie in the intersection of the
* bounding rectangles defined by each edge.
*/
{
GLdouble z1, z2;
/* This is certainly not the most efficient way to find the intersection
* of two line segments, but it is very numerically stable.
*
* Strategy: find the two middle vertices in the VertLeq ordering,
* and interpolate the intersection s-value from these. Then repeat
* using the TransLeq ordering to find the intersection t-value.
*/
if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); }
if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); }
if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
if( ! VertLeq( o2, d1 )) {
/* Technically, no intersection -- do our best */
v->s = (o2->s + d1->s) / 2;
} else if( VertLeq( d1, d2 )) {
/* Interpolate between o2 and d1 */
z1 = EdgeEval( o1, o2, d1 );
z2 = EdgeEval( o2, d1, d2 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->s = Interpolate( z1, o2->s, z2, d1->s );
} else {
/* Interpolate between o2 and d2 */
z1 = EdgeSign( o1, o2, d1 );
z2 = -EdgeSign( o1, d2, d1 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->s = Interpolate( z1, o2->s, z2, d2->s );
}
/* Now repeat the process for t */
if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); }
if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); }
if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
if( ! TransLeq( o2, d1 )) {
/* Technically, no intersection -- do our best */
v->t = (o2->t + d1->t) / 2;
} else if( TransLeq( d1, d2 )) {
/* Interpolate between o2 and d1 */
z1 = TransEval( o1, o2, d1 );
z2 = TransEval( o2, d1, d2 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->t = Interpolate( z1, o2->t, z2, d1->t );
} else {
/* Interpolate between o2 and d2 */
z1 = TransSign( o1, o2, d1 );
z2 = -TransSign( o1, d2, d1 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->t = Interpolate( z1, o2->t, z2, d2->t );
}
}
+84
View File
@@ -0,0 +1,84 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __geom_h_
#define __geom_h_
#include "mesh.h"
#ifdef NO_BRANCH_CONDITIONS
/* MIPS architecture has special instructions to evaluate boolean
* conditions -- more efficient than branching, IF you can get the
* compiler to generate the right instructions (SGI compiler doesn't)
*/
#define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t))
#define VertLeq(u,v) (((u)->s < (v)->s) | \
((u)->s == (v)->s & (u)->t <= (v)->t))
#else
#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t)
#define VertLeq(u,v) (((u)->s < (v)->s) || \
((u)->s == (v)->s && (u)->t <= (v)->t))
#endif
#define EdgeEval(u,v,w) __gl_edgeEval(u,v,w)
#define EdgeSign(u,v,w) __gl_edgeSign(u,v,w)
/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */
#define TransLeq(u,v) (((u)->t < (v)->t) || \
((u)->t == (v)->t && (u)->s <= (v)->s))
#define TransEval(u,v,w) __gl_transEval(u,v,w)
#define TransSign(u,v,w) __gl_transSign(u,v,w)
#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org )
#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst )
#undef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t))
#define VertCCW(u,v,w) __gl_vertCCW(u,v,w)
int __gl_vertLeq( GLUvertex *u, GLUvertex *v );
GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w );
int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w );
void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1,
GLUvertex *o2, GLUvertex *d2,
GLUvertex *v );
#endif
+55
View File
@@ -0,0 +1,55 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "memalloc.h"
#include "string.h"
int __gl_memInit( size_t maxFast )
{
#ifndef NO_MALLOPT
/* mallopt( M_MXFAST, maxFast );*/
#ifdef MEMORY_DEBUG
mallopt( M_DEBUG, 1 );
#endif
#endif
return 1;
}
#ifdef MEMORY_DEBUG
void *__gl_memAlloc( size_t n )
{
return memset( malloc( n ), 0xa5, n );
}
#endif
+54
View File
@@ -0,0 +1,54 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __memalloc_simple_h_
#define __memalloc_simple_h_
#include <stdlib.h>
#define memRealloc realloc
#define memFree free
#define memInit __gl_memInit
/*extern void __gl_memInit( size_t );*/
extern int __gl_memInit( size_t );
#ifndef MEMORY_DEBUG
#define memAlloc malloc
#else
#define memAlloc __gl_memAlloc
extern void * __gl_memAlloc( size_t );
#endif
#endif
+790
View File
@@ -0,0 +1,790 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "../prboom/SDL_opengl.h" // JDC
//#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include "mesh.h"
#include "memalloc.h"
#define TRUE 1
#define FALSE 0
static GLUvertex *allocVertex()
{
return (GLUvertex *)memAlloc( sizeof( GLUvertex ));
}
static GLUface *allocFace()
{
return (GLUface *)memAlloc( sizeof( GLUface ));
}
/************************ Utility Routines ************************/
/* Allocate and free half-edges in pairs for efficiency.
* The *only* place that should use this fact is allocation/free.
*/
typedef struct { GLUhalfEdge e, eSym; } EdgePair;
/* MakeEdge creates a new pair of half-edges which form their own loop.
* No vertex or face structures are allocated, but these must be assigned
* before the current edge operation is completed.
*/
static GLUhalfEdge *MakeEdge( GLUhalfEdge *eNext )
{
GLUhalfEdge *e;
GLUhalfEdge *eSym;
GLUhalfEdge *ePrev;
EdgePair *pair = (EdgePair *)memAlloc( sizeof( EdgePair ));
if (pair == NULL) return NULL;
e = &pair->e;
eSym = &pair->eSym;
/* Make sure eNext points to the first edge of the edge pair */
if( eNext->Sym < eNext ) { eNext = eNext->Sym; }
/* Insert in circular doubly-linked list before eNext.
* Note that the prev pointer is stored in Sym->next.
*/
ePrev = eNext->Sym->next;
eSym->next = ePrev;
ePrev->Sym->next = e;
e->next = eNext;
eNext->Sym->next = eSym;
e->Sym = eSym;
e->Onext = e;
e->Lnext = eSym;
e->Org = NULL;
e->Lface = NULL;
e->winding = 0;
e->activeRegion = NULL;
eSym->Sym = e;
eSym->Onext = eSym;
eSym->Lnext = e;
eSym->Org = NULL;
eSym->Lface = NULL;
eSym->winding = 0;
eSym->activeRegion = NULL;
return e;
}
/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the
* CS348a notes (see mesh.h). Basically it modifies the mesh so that
* a->Onext and b->Onext are exchanged. This can have various effects
* depending on whether a and b belong to different face or vertex rings.
* For more explanation see __gl_meshSplice() below.
*/
static void Splice( GLUhalfEdge *a, GLUhalfEdge *b )
{
GLUhalfEdge *aOnext = a->Onext;
GLUhalfEdge *bOnext = b->Onext;
aOnext->Sym->Lnext = b;
bOnext->Sym->Lnext = a;
a->Onext = bOnext;
b->Onext = aOnext;
}
/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the
* origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
* a place to insert the new vertex in the global vertex list. We insert
* the new vertex *before* vNext so that algorithms which walk the vertex
* list will not see the newly created vertices.
*/
static void MakeVertex( GLUvertex *newVertex,
GLUhalfEdge *eOrig, GLUvertex *vNext )
{
GLUhalfEdge *e;
GLUvertex *vPrev;
GLUvertex *vNew = newVertex;
assert(vNew != NULL);
/* insert in circular doubly-linked list before vNext */
vPrev = vNext->prev;
vNew->prev = vPrev;
vPrev->next = vNew;
vNew->next = vNext;
vNext->prev = vNew;
vNew->anEdge = eOrig;
vNew->data = NULL;
/* leave coords, s, t undefined */
/* fix other edges on this vertex loop */
e = eOrig;
do {
e->Org = vNew;
e = e->Onext;
} while( e != eOrig );
}
/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left
* face of all edges in the face loop to which eOrig belongs. "fNext" gives
* a place to insert the new face in the global face list. We insert
* the new face *before* fNext so that algorithms which walk the face
* list will not see the newly created faces.
*/
static void MakeFace( GLUface *newFace, GLUhalfEdge *eOrig, GLUface *fNext )
{
GLUhalfEdge *e;
GLUface *fPrev;
GLUface *fNew = newFace;
assert(fNew != NULL);
/* insert in circular doubly-linked list before fNext */
fPrev = fNext->prev;
fNew->prev = fPrev;
fPrev->next = fNew;
fNew->next = fNext;
fNext->prev = fNew;
fNew->anEdge = eOrig;
fNew->data = NULL;
fNew->trail = NULL;
fNew->marked = FALSE;
/* The new face is marked "inside" if the old one was. This is a
* convenience for the common case where a face has been split in two.
*/
fNew->inside = fNext->inside;
/* fix other edges on this face loop */
e = eOrig;
do {
e->Lface = fNew;
e = e->Lnext;
} while( e != eOrig );
}
/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym),
* and removes from the global edge list.
*/
static void KillEdge( GLUhalfEdge *eDel )
{
GLUhalfEdge *ePrev, *eNext;
/* Half-edges are allocated in pairs, see EdgePair above */
if( eDel->Sym < eDel ) { eDel = eDel->Sym; }
/* delete from circular doubly-linked list */
eNext = eDel->next;
ePrev = eDel->Sym->next;
eNext->Sym->next = ePrev;
ePrev->Sym->next = eNext;
memFree( eDel );
}
/* KillVertex( vDel ) destroys a vertex and removes it from the global
* vertex list. It updates the vertex loop to point to a given new vertex.
*/
static void KillVertex( GLUvertex *vDel, GLUvertex *newOrg )
{
GLUhalfEdge *e, *eStart = vDel->anEdge;
GLUvertex *vPrev, *vNext;
/* change the origin of all affected edges */
e = eStart;
do {
e->Org = newOrg;
e = e->Onext;
} while( e != eStart );
/* delete from circular doubly-linked list */
vPrev = vDel->prev;
vNext = vDel->next;
vNext->prev = vPrev;
vPrev->next = vNext;
memFree( vDel );
}
/* KillFace( fDel ) destroys a face and removes it from the global face
* list. It updates the face loop to point to a given new face.
*/
static void KillFace( GLUface *fDel, GLUface *newLface )
{
GLUhalfEdge *e, *eStart = fDel->anEdge;
GLUface *fPrev, *fNext;
/* change the left face of all affected edges */
e = eStart;
do {
e->Lface = newLface;
e = e->Lnext;
} while( e != eStart );
/* delete from circular doubly-linked list */
fPrev = fDel->prev;
fNext = fDel->next;
fNext->prev = fPrev;
fPrev->next = fNext;
memFree( fDel );
}
/****************** Basic Edge Operations **********************/
/* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face).
* The loop consists of the two new half-edges.
*/
GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh )
{
GLUvertex *newVertex1= allocVertex();
GLUvertex *newVertex2= allocVertex();
GLUface *newFace= allocFace();
GLUhalfEdge *e;
/* if any one is null then all get freed */
if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) {
if (newVertex1 != NULL) memFree(newVertex1);
if (newVertex2 != NULL) memFree(newVertex2);
if (newFace != NULL) memFree(newFace);
return NULL;
}
e = MakeEdge( &mesh->eHead );
if (e == NULL) return NULL;
MakeVertex( newVertex1, e, &mesh->vHead );
MakeVertex( newVertex2, e->Sym, &mesh->vHead );
MakeFace( newFace, e, &mesh->fHead );
return e;
}
/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
* mesh connectivity and topology. It changes the mesh so that
* eOrg->Onext <- OLD( eDst->Onext )
* eDst->Onext <- OLD( eOrg->Onext )
* where OLD(...) means the value before the meshSplice operation.
*
* This can have two effects on the vertex structure:
* - if eOrg->Org != eDst->Org, the two vertices are merged together
* - if eOrg->Org == eDst->Org, the origin is split into two vertices
* In both cases, eDst->Org is changed and eOrg->Org is untouched.
*
* Similarly (and independently) for the face structure,
* - if eOrg->Lface == eDst->Lface, one loop is split into two
* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
*
* Some special cases:
* If eDst == eOrg, the operation has no effect.
* If eDst == eOrg->Lnext, the new face will have a single edge.
* If eDst == eOrg->Lprev, the old face will have a single edge.
* If eDst == eOrg->Onext, the new vertex will have a single edge.
* If eDst == eOrg->Oprev, the old vertex will have a single edge.
*/
int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst )
{
int joiningLoops = FALSE;
int joiningVertices = FALSE;
if( eOrg == eDst ) return 1;
if( eDst->Org != eOrg->Org ) {
/* We are merging two disjoint vertices -- destroy eDst->Org */
joiningVertices = TRUE;
KillVertex( eDst->Org, eOrg->Org );
}
if( eDst->Lface != eOrg->Lface ) {
/* We are connecting two disjoint loops -- destroy eDst->Lface */
joiningLoops = TRUE;
KillFace( eDst->Lface, eOrg->Lface );
}
/* Change the edge structure */
Splice( eDst, eOrg );
if( ! joiningVertices ) {
GLUvertex *newVertex= allocVertex();
if (newVertex == NULL) return 0;
/* We split one vertex into two -- the new vertex is eDst->Org.
* Make sure the old vertex points to a valid half-edge.
*/
MakeVertex( newVertex, eDst, eOrg->Org );
eOrg->Org->anEdge = eOrg;
}
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return 0;
/* We split one loop into two -- the new loop is eDst->Lface.
* Make sure the old face points to a valid half-edge.
*/
MakeFace( newFace, eDst, eOrg->Lface );
eOrg->Lface->anEdge = eOrg;
}
return 1;
}
/* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases:
* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
* the newly created loop will contain eDel->Dst. If the deletion of eDel
* would create isolated vertices, those are deleted as well.
*
* This function could be implemented as two calls to __gl_meshSplice
* plus a few calls to memFree, but this would allocate and delete
* unnecessary vertices and faces.
*/
int __gl_meshDelete( GLUhalfEdge *eDel )
{
GLUhalfEdge *eDelSym = eDel->Sym;
int joiningLoops = FALSE;
/* First step: disconnect the origin vertex eDel->Org. We make all
* changes to get a consistent mesh in this "intermediate" state.
*/
if( eDel->Lface != eDel->Rface ) {
/* We are joining two loops into one -- remove the left face */
joiningLoops = TRUE;
KillFace( eDel->Lface, eDel->Rface );
}
if( eDel->Onext == eDel ) {
KillVertex( eDel->Org, NULL );
} else {
/* Make sure that eDel->Org and eDel->Rface point to valid half-edges */
eDel->Rface->anEdge = eDel->Oprev;
eDel->Org->anEdge = eDel->Onext;
Splice( eDel, eDel->Oprev );
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return 0;
/* We are splitting one loop into two -- create a new loop for eDel. */
MakeFace( newFace, eDel, eDel->Lface );
}
}
/* Claim: the mesh is now in a consistent state, except that eDel->Org
* may have been deleted. Now we disconnect eDel->Dst.
*/
if( eDelSym->Onext == eDelSym ) {
KillVertex( eDelSym->Org, NULL );
KillFace( eDelSym->Lface, NULL );
} else {
/* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */
eDel->Lface->anEdge = eDelSym->Oprev;
eDelSym->Org->anEdge = eDelSym->Onext;
Splice( eDelSym, eDelSym->Oprev );
}
/* Any isolated vertices or faces have already been freed. */
KillEdge( eDel );
return 1;
}
/******************** Other Edge Operations **********************/
/* All these routines can be implemented with the basic edge
* operations above. They are provided for convenience and efficiency.
*/
/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
* eOrg and eNew will have the same left face.
*/
GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg )
{
GLUhalfEdge *eNewSym;
GLUhalfEdge *eNew = MakeEdge( eOrg );
if (eNew == NULL) return NULL;
eNewSym = eNew->Sym;
/* Connect the new edge appropriately */
Splice( eNew, eOrg->Lnext );
/* Set the vertex and face information */
eNew->Org = eOrg->Dst;
{
GLUvertex *newVertex= allocVertex();
if (newVertex == NULL) return NULL;
MakeVertex( newVertex, eNewSym, eNew->Org );
}
eNew->Lface = eNewSym->Lface = eOrg->Lface;
return eNew;
}
/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
* eOrg and eNew will have the same left face.
*/
GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg )
{
GLUhalfEdge *eNew;
GLUhalfEdge *tempHalfEdge= __gl_meshAddEdgeVertex( eOrg );
if (tempHalfEdge == NULL) return NULL;
eNew = tempHalfEdge->Sym;
/* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */
Splice( eOrg->Sym, eOrg->Sym->Oprev );
Splice( eOrg->Sym, eNew );
/* Set the vertex and face information */
eOrg->Dst = eNew->Org;
eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */
eNew->Rface = eOrg->Rface;
eNew->winding = eOrg->winding; /* copy old winding information */
eNew->Sym->winding = eOrg->Sym->winding;
return eNew;
}
/* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
* to eDst->Org, and returns the corresponding half-edge eNew.
* If eOrg->Lface == eDst->Lface, this splits one loop into two,
* and the newly created loop is eNew->Lface. Otherwise, two disjoint
* loops are merged into one, and the loop eDst->Lface is destroyed.
*
* If (eOrg == eDst), the new face will have only two edges.
* If (eOrg->Lnext == eDst), the old face is reduced to a single edge.
* If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges.
*/
GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst )
{
GLUhalfEdge *eNewSym;
int joiningLoops = FALSE;
GLUhalfEdge *eNew = MakeEdge( eOrg );
if (eNew == NULL) return NULL;
eNewSym = eNew->Sym;
if( eDst->Lface != eOrg->Lface ) {
/* We are connecting two disjoint loops -- destroy eDst->Lface */
joiningLoops = TRUE;
KillFace( eDst->Lface, eOrg->Lface );
}
/* Connect the new edge appropriately */
Splice( eNew, eOrg->Lnext );
Splice( eNewSym, eDst );
/* Set the vertex and face information */
eNew->Org = eOrg->Dst;
eNewSym->Org = eDst->Org;
eNew->Lface = eNewSym->Lface = eOrg->Lface;
/* Make sure the old face points to a valid half-edge */
eOrg->Lface->anEdge = eNewSym;
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return NULL;
/* We split one loop into two -- the new loop is eNew->Lface */
MakeFace( newFace, eNew, eOrg->Lface );
}
return eNew;
}
/******************** Other Operations **********************/
/* __gl_meshZapFace( fZap ) destroys a face and removes it from the
* global face list. All edges of fZap will have a NULL pointer as their
* left face. Any edges which also have a NULL pointer as their right face
* are deleted entirely (along with any isolated vertices this produces).
* An entire mesh can be deleted by zapping its faces, one at a time,
* in any order. Zapped faces cannot be used in further mesh operations!
*/
void __gl_meshZapFace( GLUface *fZap )
{
GLUhalfEdge *eStart = fZap->anEdge;
GLUhalfEdge *e, *eNext, *eSym;
GLUface *fPrev, *fNext;
/* walk around face, deleting edges whose right face is also NULL */
eNext = eStart->Lnext;
do {
e = eNext;
eNext = e->Lnext;
e->Lface = NULL;
if( e->Rface == NULL ) {
/* delete the edge -- see __gl_MeshDelete above */
if( e->Onext == e ) {
KillVertex( e->Org, NULL );
} else {
/* Make sure that e->Org points to a valid half-edge */
e->Org->anEdge = e->Onext;
Splice( e, e->Oprev );
}
eSym = e->Sym;
if( eSym->Onext == eSym ) {
KillVertex( eSym->Org, NULL );
} else {
/* Make sure that eSym->Org points to a valid half-edge */
eSym->Org->anEdge = eSym->Onext;
Splice( eSym, eSym->Oprev );
}
KillEdge( e );
}
} while( e != eStart );
/* delete from circular doubly-linked list */
fPrev = fZap->prev;
fNext = fZap->next;
fNext->prev = fPrev;
fPrev->next = fNext;
memFree( fZap );
}
/* __gl_meshNewMesh() creates a new mesh with no edges, no vertices,
* and no loops (what we usually call a "face").
*/
GLUmesh *__gl_meshNewMesh( void )
{
GLUvertex *v;
GLUface *f;
GLUhalfEdge *e;
GLUhalfEdge *eSym;
GLUmesh *mesh = (GLUmesh *)memAlloc( sizeof( GLUmesh ));
if (mesh == NULL) {
return NULL;
}
v = &mesh->vHead;
f = &mesh->fHead;
e = &mesh->eHead;
eSym = &mesh->eHeadSym;
v->next = v->prev = v;
v->anEdge = NULL;
v->data = NULL;
f->next = f->prev = f;
f->anEdge = NULL;
f->data = NULL;
f->trail = NULL;
f->marked = FALSE;
f->inside = FALSE;
e->next = e;
e->Sym = eSym;
e->Onext = NULL;
e->Lnext = NULL;
e->Org = NULL;
e->Lface = NULL;
e->winding = 0;
e->activeRegion = NULL;
eSym->next = eSym;
eSym->Sym = e;
eSym->Onext = NULL;
eSym->Lnext = NULL;
eSym->Org = NULL;
eSym->Lface = NULL;
eSym->winding = 0;
eSym->activeRegion = NULL;
return mesh;
}
/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
* both meshes, and returns the new mesh (the old meshes are destroyed).
*/
GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 )
{
GLUface *f1 = &mesh1->fHead;
GLUvertex *v1 = &mesh1->vHead;
GLUhalfEdge *e1 = &mesh1->eHead;
GLUface *f2 = &mesh2->fHead;
GLUvertex *v2 = &mesh2->vHead;
GLUhalfEdge *e2 = &mesh2->eHead;
/* Add the faces, vertices, and edges of mesh2 to those of mesh1 */
if( f2->next != f2 ) {
f1->prev->next = f2->next;
f2->next->prev = f1->prev;
f2->prev->next = f1;
f1->prev = f2->prev;
}
if( v2->next != v2 ) {
v1->prev->next = v2->next;
v2->next->prev = v1->prev;
v2->prev->next = v1;
v1->prev = v2->prev;
}
if( e2->next != e2 ) {
e1->Sym->next->Sym->next = e2->next;
e2->next->Sym->next = e1->Sym->next;
e2->Sym->next->Sym->next = e1;
e1->Sym->next = e2->Sym->next;
}
memFree( mesh2 );
return mesh1;
}
#ifdef DELETE_BY_ZAPPING
/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*/
void __gl_meshDeleteMesh( GLUmesh *mesh )
{
GLUface *fHead = &mesh->fHead;
while( fHead->next != fHead ) {
__gl_meshZapFace( fHead->next );
}
assert( mesh->vHead.next == &mesh->vHead );
memFree( mesh );
}
#else
/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*/
void __gl_meshDeleteMesh( GLUmesh *mesh )
{
GLUface *f, *fNext;
GLUvertex *v, *vNext;
GLUhalfEdge *e, *eNext;
for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) {
fNext = f->next;
memFree( f );
}
for( v = mesh->vHead.next; v != &mesh->vHead; v = vNext ) {
vNext = v->next;
memFree( v );
}
for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
/* One call frees both e and e->Sym (see EdgePair above) */
eNext = e->next;
memFree( e );
}
memFree( mesh );
}
#endif
#ifndef NDEBUG
/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
*/
void __gl_meshCheckMesh( GLUmesh *mesh )
{
GLUface *fHead = &mesh->fHead;
GLUvertex *vHead = &mesh->vHead;
GLUhalfEdge *eHead = &mesh->eHead;
GLUface *f, *fPrev;
GLUvertex *v, *vPrev;
GLUhalfEdge *e, *ePrev;
fPrev = fHead;
for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) {
assert( f->prev == fPrev );
e = f->anEdge;
do {
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
assert( e->Lface == f );
e = e->Lnext;
} while( e != f->anEdge );
}
assert( f->prev == fPrev && f->anEdge == NULL && f->data == NULL );
vPrev = vHead;
for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) {
assert( v->prev == vPrev );
e = v->anEdge;
do {
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
assert( e->Org == v );
e = e->Onext;
} while( e != v->anEdge );
}
assert( v->prev == vPrev && v->anEdge == NULL && v->data == NULL );
ePrev = eHead;
for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) {
assert( e->Sym->next == ePrev->Sym );
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Org != NULL );
assert( e->Dst != NULL );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
}
assert( e->Sym->next == ePrev->Sym
&& e->Sym == &mesh->eHeadSym
&& e->Sym->Sym == e
&& e->Org == NULL && e->Dst == NULL
&& e->Lface == NULL && e->Rface == NULL );
}
#endif
+266
View File
@@ -0,0 +1,266 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __mesh_h_
#define __mesh_h_
// JDC #include <GL/glu.h>
typedef struct GLUmesh GLUmesh;
typedef struct GLUvertex GLUvertex;
typedef struct GLUface GLUface;
typedef struct GLUhalfEdge GLUhalfEdge;
typedef struct ActiveRegion ActiveRegion; /* Internal data */
/* The mesh structure is similar in spirit, notation, and operations
* to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives
* for the manipulation of general subdivisions and the computation of
* Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985).
* For a simplified description, see the course notes for CS348a,
* "Mathematical Foundations of Computer Graphics", available at the
* Stanford bookstore (and taught during the fall quarter).
* The implementation also borrows a tiny subset of the graph-based approach
* use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction
* to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988).
*
* The fundamental data structure is the "half-edge". Two half-edges
* go together to make an edge, but they point in opposite directions.
* Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym),
* its origin vertex (Org), the face on its left side (Lface), and the
* adjacent half-edges in the CCW direction around the origin vertex
* (Onext) and around the left face (Lnext). There is also a "next"
* pointer for the global edge list (see below).
*
* The notation used for mesh navigation:
* Sym = the mate of a half-edge (same edge, but opposite direction)
* Onext = edge CCW around origin vertex (keep same origin)
* Dnext = edge CCW around destination vertex (keep same dest)
* Lnext = edge CCW around left face (dest becomes new origin)
* Rnext = edge CCW around right face (origin becomes new dest)
*
* "prev" means to substitute CW for CCW in the definitions above.
*
* The mesh keeps global lists of all vertices, faces, and edges,
* stored as doubly-linked circular lists with a dummy header node.
* The mesh stores pointers to these dummy headers (vHead, fHead, eHead).
*
* The circular edge list is special; since half-edges always occur
* in pairs (e and e->Sym), each half-edge stores a pointer in only
* one direction. Starting at eHead and following the e->next pointers
* will visit each *edge* once (ie. e or e->Sym, but not both).
* e->Sym stores a pointer in the opposite direction, thus it is
* always true that e->Sym->next->Sym->next == e.
*
* Each vertex has a pointer to next and previous vertices in the
* circular list, and a pointer to a half-edge with this vertex as
* the origin (NULL if this is the dummy header). There is also a
* field "data" for client data.
*
* Each face has a pointer to the next and previous faces in the
* circular list, and a pointer to a half-edge with this face as
* the left face (NULL if this is the dummy header). There is also
* a field "data" for client data.
*
* Note that what we call a "face" is really a loop; faces may consist
* of more than one loop (ie. not simply connected), but there is no
* record of this in the data structure. The mesh may consist of
* several disconnected regions, so it may not be possible to visit
* the entire mesh by starting at a half-edge and traversing the edge
* structure.
*
* The mesh does NOT support isolated vertices; a vertex is deleted along
* with its last edge. Similarly when two faces are merged, one of the
* faces is deleted (see __gl_meshDelete below). For mesh operations,
* all face (loop) and vertex pointers must not be NULL. However, once
* mesh manipulation is finished, __gl_MeshZapFace can be used to delete
* faces of the mesh, one at a time. All external faces can be "zapped"
* before the mesh is returned to the client; then a NULL face indicates
* a region which is not part of the output polygon.
*/
struct GLUvertex {
GLUvertex *next; /* next vertex (never NULL) */
GLUvertex *prev; /* previous vertex (never NULL) */
GLUhalfEdge *anEdge; /* a half-edge with this origin */
void *data; /* client's data */
/* Internal data (keep hidden) */
GLdouble coords[3]; /* vertex location in 3D */
GLdouble s, t; /* projection onto the sweep plane */
long pqHandle; /* to allow deletion from priority queue */
};
struct GLUface {
GLUface *next; /* next face (never NULL) */
GLUface *prev; /* previous face (never NULL) */
GLUhalfEdge *anEdge; /* a half edge with this left face */
void *data; /* room for client's data */
/* Internal data (keep hidden) */
GLUface *trail; /* "stack" for conversion to strips */
GLboolean marked; /* flag for conversion to strips */
GLboolean inside; /* this face is in the polygon interior */
};
struct GLUhalfEdge {
GLUhalfEdge *next; /* doubly-linked list (prev==Sym->next) */
GLUhalfEdge *Sym; /* same edge, opposite direction */
GLUhalfEdge *Onext; /* next edge CCW around origin */
GLUhalfEdge *Lnext; /* next edge CCW around left face */
GLUvertex *Org; /* origin vertex (Overtex too long) */
GLUface *Lface; /* left face */
/* Internal data (keep hidden) */
ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */
int winding; /* change in winding number when crossing
from the right face to the left face */
};
#define Rface Sym->Lface
#define Dst Sym->Org
#define Oprev Sym->Lnext
#define Lprev Onext->Sym
#define Dprev Lnext->Sym
#define Rprev Sym->Onext
#define Dnext Rprev->Sym /* 3 pointers */
#define Rnext Oprev->Sym /* 3 pointers */
struct GLUmesh {
GLUvertex vHead; /* dummy header for vertex list */
GLUface fHead; /* dummy header for face list */
GLUhalfEdge eHead; /* dummy header for edge list */
GLUhalfEdge eHeadSym; /* and its symmetric counterpart */
};
/* The mesh operations below have three motivations: completeness,
* convenience, and efficiency. The basic mesh operations are MakeEdge,
* Splice, and Delete. All the other edge operations can be implemented
* in terms of these. The other operations are provided for convenience
* and/or efficiency.
*
* When a face is split or a vertex is added, they are inserted into the
* global list *before* the existing vertex or face (ie. e->Org or e->Lface).
* This makes it easier to process all vertices or faces in the global lists
* without worrying about processing the same data twice. As a convenience,
* when a face is split, the "inside" flag is copied from the old face.
* Other internal data (v->data, v->activeRegion, f->data, f->marked,
* f->trail, e->winding) is set to zero.
*
* ********************** Basic Edge Operations **************************
*
* __gl_meshMakeEdge( mesh ) creates one edge, two vertices, and a loop.
* The loop (face) consists of the two new half-edges.
*
* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
* mesh connectivity and topology. It changes the mesh so that
* eOrg->Onext <- OLD( eDst->Onext )
* eDst->Onext <- OLD( eOrg->Onext )
* where OLD(...) means the value before the meshSplice operation.
*
* This can have two effects on the vertex structure:
* - if eOrg->Org != eDst->Org, the two vertices are merged together
* - if eOrg->Org == eDst->Org, the origin is split into two vertices
* In both cases, eDst->Org is changed and eOrg->Org is untouched.
*
* Similarly (and independently) for the face structure,
* - if eOrg->Lface == eDst->Lface, one loop is split into two
* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
*
* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases:
* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
* the newly created loop will contain eDel->Dst. If the deletion of eDel
* would create isolated vertices, those are deleted as well.
*
* ********************** Other Edge Operations **************************
*
* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
* eOrg and eNew will have the same left face.
*
* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
* eOrg and eNew will have the same left face.
*
* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
* to eDst->Org, and returns the corresponding half-edge eNew.
* If eOrg->Lface == eDst->Lface, this splits one loop into two,
* and the newly created loop is eNew->Lface. Otherwise, two disjoint
* loops are merged into one, and the loop eDst->Lface is destroyed.
*
* ************************ Other Operations *****************************
*
* __gl_meshNewMesh() creates a new mesh with no edges, no vertices,
* and no loops (what we usually call a "face").
*
* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
* both meshes, and returns the new mesh (the old meshes are destroyed).
*
* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*
* __gl_meshZapFace( fZap ) destroys a face and removes it from the
* global face list. All edges of fZap will have a NULL pointer as their
* left face. Any edges which also have a NULL pointer as their right face
* are deleted entirely (along with any isolated vertices this produces).
* An entire mesh can be deleted by zapping its faces, one at a time,
* in any order. Zapped faces cannot be used in further mesh operations!
*
* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
*/
GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh );
int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst );
int __gl_meshDelete( GLUhalfEdge *eDel );
GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg );
GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg );
GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst );
GLUmesh *__gl_meshNewMesh( void );
GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 );
void __gl_meshDeleteMesh( GLUmesh *mesh );
void __gl_meshZapFace( GLUface *fZap );
#ifdef NDEBUG
#define __gl_meshCheckMesh( mesh )
#else
void __gl_meshCheckMesh( GLUmesh *mesh );
#endif
#endif
+254
View File
@@ -0,0 +1,254 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "../prboom/SDL_opengl.h" // JDC
//#include "gluos.h"
#include "mesh.h"
#include "tess.h"
#include "normal.h"
#include <math.h>
#include <assert.h>
#define TRUE 1
#define FALSE 0
#define Dot(u,v) (u[0]*v[0] + u[1]*v[1] + u[2]*v[2])
#if 0
static void Normalize( GLdouble v[3] )
{
GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
assert( len > 0 );
len = sqrt( len );
v[0] /= len;
v[1] /= len;
v[2] /= len;
}
#endif
#undef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
static int LongAxis( GLdouble v[3] )
{
int i = 0;
if( ABS(v[1]) > ABS(v[0]) ) { i = 1; }
if( ABS(v[2]) > ABS(v[i]) ) { i = 2; }
return i;
}
static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] )
{
GLUvertex *v, *v1, *v2;
GLdouble c, tLen2, maxLen2;
GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3];
GLUvertex *maxVert[3], *minVert[3];
GLUvertex *vHead = &tess->mesh->vHead;
int i;
maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD;
minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD;
for( v = vHead->next; v != vHead; v = v->next ) {
for( i = 0; i < 3; ++i ) {
c = v->coords[i];
if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; }
if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; }
}
}
/* Find two vertices separated by at least 1/sqrt(3) of the maximum
* distance between any two vertices
*/
i = 0;
if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; }
if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; }
if( minVal[i] >= maxVal[i] ) {
/* All vertices are the same -- normal doesn't matter */
norm[0] = 0; norm[1] = 0; norm[2] = 1;
return;
}
/* Look for a third vertex which forms the triangle with maximum area
* (Length of normal == twice the triangle area)
*/
maxLen2 = 0;
v1 = minVert[i];
v2 = maxVert[i];
d1[0] = v1->coords[0] - v2->coords[0];
d1[1] = v1->coords[1] - v2->coords[1];
d1[2] = v1->coords[2] - v2->coords[2];
for( v = vHead->next; v != vHead; v = v->next ) {
d2[0] = v->coords[0] - v2->coords[0];
d2[1] = v->coords[1] - v2->coords[1];
d2[2] = v->coords[2] - v2->coords[2];
tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1];
tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2];
tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0];
tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2];
if( tLen2 > maxLen2 ) {
maxLen2 = tLen2;
norm[0] = tNorm[0];
norm[1] = tNorm[1];
norm[2] = tNorm[2];
}
}
if( maxLen2 <= 0 ) {
/* All points lie on a single line -- any decent normal will do */
norm[0] = norm[1] = norm[2] = 0;
norm[LongAxis(d1)] = 1;
}
}
static void CheckOrientation( GLUtesselator *tess )
{
GLdouble area;
GLUface *f, *fHead = &tess->mesh->fHead;
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLUhalfEdge *e;
/* When we compute the normal automatically, we choose the orientation
* so that the the sum of the signed areas of all contours is non-negative.
*/
area = 0;
for( f = fHead->next; f != fHead; f = f->next ) {
e = f->anEdge;
if( e->winding <= 0 ) continue;
do {
area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t);
e = e->Lnext;
} while( e != f->anEdge );
}
if( area < 0 ) {
/* Reverse the orientation by flipping all the t-coordinates */
for( v = vHead->next; v != vHead; v = v->next ) {
v->t = - v->t;
}
tess->tUnit[0] = - tess->tUnit[0];
tess->tUnit[1] = - tess->tUnit[1];
tess->tUnit[2] = - tess->tUnit[2];
}
}
#ifdef FOR_TRITE_TEST_PROGRAM
#include <stdlib.h>
extern int RandomSweep;
#define S_UNIT_X (RandomSweep ? (2*drand48()-1) : 1.0)
#define S_UNIT_Y (RandomSweep ? (2*drand48()-1) : 0.0)
#else
#if defined(SLANTED_SWEEP)
/* The "feature merging" is not intended to be complete. There are
* special cases where edges are nearly parallel to the sweep line
* which are not implemented. The algorithm should still behave
* robustly (ie. produce a reasonable tesselation) in the presence
* of such edges, however it may miss features which could have been
* merged. We could minimize this effect by choosing the sweep line
* direction to be something unusual (ie. not parallel to one of the
* coordinate axes).
*/
#define S_UNIT_X 0.50941539564955385 /* Pre-normalized */
#define S_UNIT_Y 0.86052074622010633
#else
#define S_UNIT_X 1.0
#define S_UNIT_Y 0.0
#endif
#endif
/* Determine the polygon normal and project vertices onto the plane
* of the polygon.
*/
void __gl_projectPolygon( GLUtesselator *tess )
{
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLdouble norm[3];
GLdouble *sUnit, *tUnit;
int i, computedNormal = FALSE;
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
ComputeNormal( tess, norm );
computedNormal = TRUE;
}
sUnit = tess->sUnit;
tUnit = tess->tUnit;
i = LongAxis( norm );
#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
/* Choose the initial sUnit vector to be approximately perpendicular
* to the normal.
*/
Normalize( norm );
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
/* Now make it exactly perpendicular */
w = Dot( sUnit, norm );
sUnit[0] -= w * norm[0];
sUnit[1] -= w * norm[1];
sUnit[2] -= w * norm[2];
Normalize( sUnit );
/* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */
tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
Normalize( tUnit );
#else
/* Project perpendicular to a coordinate axis -- better numerically */
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
tUnit[i] = 0;
tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y;
tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X;
#endif
/* Project the vertices onto the sweep plane */
for( v = vHead->next; v != vHead; v = v->next ) {
v->s = Dot( v->coords, sUnit );
v->t = Dot( v->coords, tUnit );
}
if( computedNormal ) {
CheckOrientation( tess );
}
}
+45
View File
@@ -0,0 +1,45 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __normal_h_
#define __normal_h_
#include "tess.h"
/* __gl_projectPolygon( tess ) determines the polygon normal
* and project vertices onto the plane of the polygon.
*/
void __gl_projectPolygon( GLUtesselator *tess );
#endif
+252
View File
@@ -0,0 +1,252 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include <stddef.h>
#include <assert.h>
#include "priorityq-heap.h"
#include "memalloc.h"
#define INIT_SIZE 32
#define TRUE 1
#define FALSE 0
#ifdef FOR_TRITE_TEST_PROGRAM
#define LEQ(x,y) (*pq->leq)(x,y)
#else
/* Violates modularity, but a little faster */
#include "geom.h"
#define LEQ(x,y) VertLeq((GLUvertex *)x, (GLUvertex *)y)
#endif
/* really __gl_pqHeapNewPriorityQ */
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) )
{
PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ ));
if (pq == NULL) return NULL;
pq->size = 0;
pq->max = INIT_SIZE;
pq->nodes = (PQnode *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->nodes[0]) );
if (pq->nodes == NULL) {
memFree(pq);
return NULL;
}
pq->handles = (PQhandleElem *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->handles[0]) );
if (pq->handles == NULL) {
memFree(pq->nodes);
memFree(pq);
return NULL;
}
pq->initialized = FALSE;
pq->freeList = 0;
pq->leq = leq;
pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */
pq->handles[1].key = NULL;
return pq;
}
/* really __gl_pqHeapDeletePriorityQ */
void pqDeletePriorityQ( PriorityQ *pq )
{
memFree( pq->handles );
memFree( pq->nodes );
memFree( pq );
}
static void FloatDown( PriorityQ *pq, long curr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hCurr, hChild;
long child;
hCurr = n[curr].handle;
for( ;; ) {
child = curr << 1;
if( child < pq->size && LEQ( h[n[child+1].handle].key,
h[n[child].handle].key )) {
++child;
}
assert(child <= pq->max);
hChild = n[child].handle;
if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) {
n[curr].handle = hCurr;
h[hCurr].node = curr;
break;
}
n[curr].handle = hChild;
h[hChild].node = curr;
curr = child;
}
}
static void FloatUp( PriorityQ *pq, long curr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hCurr, hParent;
long parent;
hCurr = n[curr].handle;
for( ;; ) {
parent = curr >> 1;
hParent = n[parent].handle;
if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) {
n[curr].handle = hCurr;
h[hCurr].node = curr;
break;
}
n[curr].handle = hParent;
h[hParent].node = curr;
curr = parent;
}
}
/* really __gl_pqHeapInit */
void pqInit( PriorityQ *pq )
{
long i;
/* This method of building a heap is O(n), rather than O(n lg n). */
for( i = pq->size; i >= 1; --i ) {
FloatDown( pq, i );
}
pq->initialized = TRUE;
}
/* really __gl_pqHeapInsert */
/* returns LONG_MAX iff out of memory */
PQhandle pqInsert( PriorityQ *pq, PQkey keyNew )
{
long curr;
PQhandle free;
curr = ++ pq->size;
if( (curr*2) > pq->max ) {
PQnode *saveNodes= pq->nodes;
PQhandleElem *saveHandles= pq->handles;
/* If the heap overflows, double its size. */
pq->max <<= 1;
pq->nodes = (PQnode *)memRealloc( pq->nodes,
(size_t)
((pq->max + 1) * sizeof( pq->nodes[0] )));
if (pq->nodes == NULL) {
pq->nodes = saveNodes; /* restore ptr to free upon return */
return LONG_MAX;
}
pq->handles = (PQhandleElem *)memRealloc( pq->handles,
(size_t)
((pq->max + 1) *
sizeof( pq->handles[0] )));
if (pq->handles == NULL) {
pq->handles = saveHandles; /* restore ptr to free upon return */
return LONG_MAX;
}
}
if( pq->freeList == 0 ) {
free = curr;
} else {
free = pq->freeList;
pq->freeList = pq->handles[free].node;
}
pq->nodes[curr].handle = free;
pq->handles[free].node = curr;
pq->handles[free].key = keyNew;
if( pq->initialized ) {
FloatUp( pq, curr );
}
assert(free != LONG_MAX);
return free;
}
/* really __gl_pqHeapExtractMin */
PQkey pqExtractMin( PriorityQ *pq )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hMin = n[1].handle;
PQkey min = h[hMin].key;
if( pq->size > 0 ) {
n[1].handle = n[pq->size].handle;
h[n[1].handle].node = 1;
h[hMin].key = NULL;
h[hMin].node = pq->freeList;
pq->freeList = hMin;
if( -- pq->size > 0 ) {
FloatDown( pq, 1 );
}
}
return min;
}
/* really __gl_pqHeapDelete */
void pqDelete( PriorityQ *pq, PQhandle hCurr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
long curr;
assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL );
curr = h[hCurr].node;
n[curr].handle = n[pq->size].handle;
h[n[curr].handle].node = curr;
if( curr <= -- pq->size ) {
if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) {
FloatDown( pq, curr );
} else {
FloatUp( pq, curr );
}
}
h[hCurr].key = NULL;
h[hCurr].node = pq->freeList;
pq->freeList = hCurr;
}
+107
View File
@@ -0,0 +1,107 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_heap_h_
#define __priorityq_heap_h_
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQHeapKey
#define PQhandle PQHeapHandle
#define PriorityQ PriorityQHeap
#define pqNewPriorityQ(leq) __gl_pqHeapNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqHeapDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqHeapInit(pq)
#define pqInsert(pq,key) __gl_pqHeapInsert(pq,key)
#define pqMinimum(pq) __gl_pqHeapMinimum(pq)
#define pqExtractMin(pq) __gl_pqHeapExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqHeapDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqHeapIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef void *PQkey;
typedef long PQhandle;
typedef struct PriorityQ PriorityQ;
typedef struct { PQhandle handle; } PQnode;
typedef struct { PQkey key; PQhandle node; } PQhandleElem;
struct PriorityQ {
PQnode *nodes;
PQhandleElem *handles;
long size, max;
PQhandle freeList;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
void pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
#define __gl_pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key)
#define __gl_pqHeapIsEmpty(pq) ((pq)->size == 0)
#endif
+117
View File
@@ -0,0 +1,117 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_sort_h_
#define __priorityq_sort_h_
#include "priorityq-heap.h"
#undef PQkey
#undef PQhandle
#undef PriorityQ
#undef pqNewPriorityQ
#undef pqDeletePriorityQ
#undef pqInit
#undef pqInsert
#undef pqMinimum
#undef pqExtractMin
#undef pqDelete
#undef pqIsEmpty
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQSortKey
#define PQhandle PQSortHandle
#define PriorityQ PriorityQSort
#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqSortInit(pq)
#define pqInsert(pq,key) __gl_pqSortInsert(pq,key)
#define pqMinimum(pq) __gl_pqSortMinimum(pq)
#define pqExtractMin(pq) __gl_pqSortExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef PQHeapKey PQkey;
typedef PQHeapHandle PQhandle;
typedef struct PriorityQ PriorityQ;
struct PriorityQ {
PriorityQHeap *heap;
PQkey *keys;
PQkey **order;
PQhandle size, max;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
int pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
PQkey pqMinimum( PriorityQ *pq );
int pqIsEmpty( PriorityQ *pq );
#endif
+264
View File
@@ -0,0 +1,264 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
typedef double GLdouble;
typedef unsigned char GLboolean;
//#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include <limits.h> /* LONG_MAX */
#include "memalloc.h"
/* Include all the code for the regular heap-based queue here. */
#include "priorityq-heap.c"
/* Now redefine all the function names to map to their "Sort" versions. */
#include "priorityq-sort.h"
/* really __gl_pqSortNewPriorityQ */
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) )
{
PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ ));
if (pq == NULL) return NULL;
pq->heap = __gl_pqHeapNewPriorityQ( leq );
if (pq->heap == NULL) {
memFree(pq);
return NULL;
}
pq->keys = (PQHeapKey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) );
if (pq->keys == NULL) {
__gl_pqHeapDeletePriorityQ(pq->heap);
memFree(pq);
return NULL;
}
pq->size = 0;
pq->max = INIT_SIZE;
pq->initialized = FALSE;
pq->leq = leq;
return pq;
}
/* really __gl_pqSortDeletePriorityQ */
void pqDeletePriorityQ( PriorityQ *pq )
{
assert(pq != NULL);
if (pq->heap != NULL) __gl_pqHeapDeletePriorityQ( pq->heap );
if (pq->order != NULL) memFree( pq->order );
if (pq->keys != NULL) memFree( pq->keys );
memFree( pq );
}
#define LT(x,y) (! LEQ(y,x))
#define GT(x,y) (! LEQ(x,y))
#define Swap(a,b) if(1){PQkey *tmp = *a; *a = *b; *b = tmp;}else
/* really __gl_pqSortInit */
int pqInit( PriorityQ *pq )
{
PQkey **p, **r, **i, **j, *piv;
struct { PQkey **p, **r; } Stack[50], *top = Stack;
unsigned long seed = 2016473283;
/* Create an array of indirect pointers to the keys, so that we
* the handles we have returned are still valid.
*/
/*
pq->order = (PQHeapKey **)memAlloc( (size_t)
(pq->size * sizeof(pq->order[0])) );
*/
pq->order = (PQHeapKey **)memAlloc( (size_t)
((pq->size+1) * sizeof(pq->order[0])) );
/* the previous line is a patch to compensate for the fact that IBM */
/* machines return a null on a malloc of zero bytes (unlike SGI), */
/* so we have to put in this defense to guard against a memory */
/* fault four lines down. from fossum@austin.ibm.com. */
if (pq->order == NULL) return 0;
p = pq->order;
r = p + pq->size - 1;
for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) {
*i = piv;
}
/* Sort the indirect pointers in descending order,
* using randomized Quicksort
*/
top->p = p; top->r = r; ++top;
while( --top >= Stack ) {
p = top->p;
r = top->r;
while( r > p + 10 ) {
seed = seed * 1539415821 + 1;
i = p + seed % (r - p + 1);
piv = *i;
*i = *p;
*p = piv;
i = p - 1;
j = r + 1;
do {
do { ++i; } while( GT( **i, *piv ));
do { --j; } while( LT( **j, *piv ));
Swap( i, j );
} while( i < j );
Swap( i, j ); /* Undo last swap */
if( i - p < r - j ) {
top->p = j+1; top->r = r; ++top;
r = i-1;
} else {
top->p = p; top->r = i-1; ++top;
p = j+1;
}
}
/* Insertion sort small lists */
for( i = p+1; i <= r; ++i ) {
piv = *i;
for( j = i; j > p && LT( **(j-1), *piv ); --j ) {
*j = *(j-1);
}
*j = piv;
}
}
pq->max = pq->size;
pq->initialized = TRUE;
__gl_pqHeapInit( pq->heap ); /* always succeeds */
#ifndef NDEBUG
p = pq->order;
r = p + pq->size - 1;
for( i = p; i < r; ++i ) {
assert( LEQ( **(i+1), **i ));
}
#endif
return 1;
}
/* really __gl_pqSortInsert */
/* returns LONG_MAX iff out of memory */
PQhandle pqInsert( PriorityQ *pq, PQkey keyNew )
{
long curr;
if( pq->initialized ) {
return __gl_pqHeapInsert( pq->heap, keyNew );
}
curr = pq->size;
if( ++ pq->size >= pq->max ) {
PQkey *saveKey= pq->keys;
/* If the heap overflows, double its size. */
pq->max <<= 1;
pq->keys = (PQHeapKey *)memRealloc( pq->keys,
(size_t)
(pq->max * sizeof( pq->keys[0] )));
if (pq->keys == NULL) {
pq->keys = saveKey; /* restore ptr to free upon return */
return LONG_MAX;
}
}
assert(curr != LONG_MAX);
pq->keys[curr] = keyNew;
/* Negative handles index the sorted array. */
return -(curr+1);
}
/* really __gl_pqSortExtractMin */
PQkey pqExtractMin( PriorityQ *pq )
{
PQkey sortMin, heapMin;
if( pq->size == 0 ) {
return __gl_pqHeapExtractMin( pq->heap );
}
sortMin = *(pq->order[pq->size-1]);
if( ! __gl_pqHeapIsEmpty( pq->heap )) {
heapMin = __gl_pqHeapMinimum( pq->heap );
if( LEQ( heapMin, sortMin )) {
return __gl_pqHeapExtractMin( pq->heap );
}
}
do {
-- pq->size;
} while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL );
return sortMin;
}
/* really __gl_pqSortMinimum */
PQkey pqMinimum( PriorityQ *pq )
{
PQkey sortMin, heapMin;
if( pq->size == 0 ) {
return __gl_pqHeapMinimum( pq->heap );
}
sortMin = *(pq->order[pq->size-1]);
if( ! __gl_pqHeapIsEmpty( pq->heap )) {
heapMin = __gl_pqHeapMinimum( pq->heap );
if( LEQ( heapMin, sortMin )) {
return heapMin;
}
}
return sortMin;
}
/* really __gl_pqSortIsEmpty */
int pqIsEmpty( PriorityQ *pq )
{
return (pq->size == 0) && __gl_pqHeapIsEmpty( pq->heap );
}
/* really __gl_pqSortDelete */
void pqDelete( PriorityQ *pq, PQhandle curr )
{
if( curr >= 0 ) {
__gl_pqHeapDelete( pq->heap, curr );
return;
}
curr = -(curr+1);
assert( curr < pq->max && pq->keys[curr] != NULL );
pq->keys[curr] = NULL;
while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) {
-- pq->size;
}
}
+117
View File
@@ -0,0 +1,117 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_sort_h_
#define __priorityq_sort_h_
#include "priorityq-heap.h"
#undef PQkey
#undef PQhandle
#undef PriorityQ
#undef pqNewPriorityQ
#undef pqDeletePriorityQ
#undef pqInit
#undef pqInsert
#undef pqMinimum
#undef pqExtractMin
#undef pqDelete
#undef pqIsEmpty
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQSortKey
#define PQhandle PQSortHandle
#define PriorityQ PriorityQSort
#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqSortInit(pq)
#define pqInsert(pq,key) __gl_pqSortInsert(pq,key)
#define pqMinimum(pq) __gl_pqSortMinimum(pq)
#define pqExtractMin(pq) __gl_pqSortExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef PQHeapKey PQkey;
typedef PQHeapHandle PQhandle;
typedef struct PriorityQ PriorityQ;
struct PriorityQ {
PriorityQHeap *heap;
PQkey *keys;
PQkey **order;
PQhandle size, max;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
int pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
PQkey pqMinimum( PriorityQ *pq );
int pqIsEmpty( PriorityQ *pq );
#endif
+499
View File
@@ -0,0 +1,499 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "../prboom/SDL_opengl.h" // JDC
//#include "gluos.h"
#include <assert.h>
#include <stddef.h>
#include "mesh.h"
#include "tess.h"
#include "render.h"
#define TRUE 1
#define FALSE 0
/* This structure remembers the information we need about a primitive
* to be able to render it later, once we have determined which
* primitive is able to use the most triangles.
*/
struct FaceCount {
long size; /* number of triangles used */
GLUhalfEdge *eStart; /* edge where this primitive starts */
void (*render)(GLUtesselator *, GLUhalfEdge *, long);
/* routine to render this primitive */
};
static struct FaceCount MaximumFan( GLUhalfEdge *eOrig );
static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig );
static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart,
long size );
static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig );
static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head );
/************************ Strips and Fans decomposition ******************/
/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
* fans, strips, and separate triangles. A substantial effort is made
* to use as few rendering primitives as possible (ie. to make the fans
* and strips as large as possible).
*
* The rendering output is provided as callbacks (see the api).
*/
void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh )
{
GLUface *f;
/* Make a list of separate triangles so we can render them all at once */
tess->lonelyTriList = NULL;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
f->marked = FALSE;
}
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
/* We examine all faces in an arbitrary order. Whenever we find
* an unprocessed face F, we output a group of faces including F
* whose size is maximum.
*/
if( f->inside && ! f->marked ) {
RenderMaximumFaceGroup( tess, f );
assert( f->marked );
}
}
if( tess->lonelyTriList != NULL ) {
RenderLonelyTriangles( tess, tess->lonelyTriList );
tess->lonelyTriList = NULL;
}
}
static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig )
{
/* We want to find the largest triangle fan or strip of unmarked faces
* which includes the given face fOrig. There are 3 possible fans
* passing through fOrig (one centered at each vertex), and 3 possible
* strips (one for each CCW permutation of the vertices). Our strategy
* is to try all of these, and take the primitive which uses the most
* triangles (a greedy approach).
*/
GLUhalfEdge *e = fOrig->anEdge;
struct FaceCount max, newFace;
max.size = 1;
max.eStart = e;
max.render = &RenderTriangle;
if( ! tess->flagBoundary ) {
newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
}
(*(max.render))( tess, max.eStart, max.size );
}
/* Macros which keep track of faces we have marked temporarily, and allow
* us to backtrack when necessary. With triangle fans, this is not
* really necessary, since the only awkward case is a loop of triangles
* around a single origin vertex. However with strips the situation is
* more complicated, and we need a general tracking method like the
* one here.
*/
#define Marked(f) (! (f)->inside || (f)->marked)
#define AddToTrail(f,t) ((f)->trail = (t), (t) = (f), (f)->marked = TRUE)
#define FreeTrail(t) if( 1 ) { \
while( (t) != NULL ) { \
(t)->marked = FALSE; t = (t)->trail; \
} \
} else /* absorb trailing semicolon */
static struct FaceCount MaximumFan( GLUhalfEdge *eOrig )
{
/* eOrig->Lface is the face we want to render. We want to find the size
* of a maximal fan around eOrig->Org. To do this we just walk around
* the origin vertex as far as possible in both directions.
*/
struct FaceCount newFace = { 0, NULL, &RenderFan };
GLUface *trail = NULL;
GLUhalfEdge *e;
for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) {
AddToTrail( e->Lface, trail );
++newFace.size;
}
for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) {
AddToTrail( e->Rface, trail );
++newFace.size;
}
newFace.eStart = e;
/*LINTED*/
FreeTrail( trail );
return newFace;
}
#define IsEven(n) (((n) & 1) == 0)
static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig )
{
/* Here we are looking for a maximal strip that contains the vertices
* eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the
* reverse, such that all triangles are oriented CCW).
*
* Again we walk forward and backward as far as possible. However for
* strips there is a twist: to get CCW orientations, there must be
* an *even* number of triangles in the strip on one side of eOrig.
* We walk the strip starting on a side with an even number of triangles;
* if both side have an odd number, we are forced to shorten one side.
*/
struct FaceCount newFace = { 0, NULL, &RenderStrip };
long headSize = 0, tailSize = 0;
GLUface *trail = NULL;
GLUhalfEdge *e, *eTail, *eHead;
for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) {
AddToTrail( e->Lface, trail );
++tailSize;
e = e->Dprev;
if( Marked( e->Lface )) break;
AddToTrail( e->Lface, trail );
}
eTail = e;
for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) {
AddToTrail( e->Rface, trail );
++headSize;
e = e->Oprev;
if( Marked( e->Rface )) break;
AddToTrail( e->Rface, trail );
}
eHead = e;
newFace.size = tailSize + headSize;
if( IsEven( tailSize )) {
newFace.eStart = eTail->Sym;
} else if( IsEven( headSize )) {
newFace.eStart = eHead;
} else {
/* Both sides have odd length, we must shorten one of them. In fact,
* we must start from eHead to guarantee inclusion of eOrig->Lface.
*/
--newFace.size;
newFace.eStart = eHead->Onext;
}
/*LINTED*/
FreeTrail( trail );
return newFace;
}
static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Just add the triangle to a triangle list, so we can render all
* the separate triangles at once.
*/
assert( size == 1 );
AddToTrail( e->Lface, tess->lonelyTriList );
}
static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f )
{
/* Now we render all the separate triangles which could not be
* grouped into a triangle fan or strip.
*/
GLUhalfEdge *e;
int newState;
int edgeState = -1; /* force edge state output for first vertex */
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES );
for( ; f != NULL; f = f->trail ) {
/* Loop once for each edge (there will always be 3 edges) */
e = f->anEdge;
do {
if( tess->flagBoundary ) {
/* Set the "edge state" to TRUE just before we output the
* first vertex of each edge on the polygon boundary.
*/
newState = ! e->Rface->inside;
if( edgeState != newState ) {
edgeState = newState;
CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState );
}
}
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
e = e->Lnext;
} while( e != f->anEdge );
}
CALL_END_OR_END_DATA();
}
static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Render as many CCW triangles as possible in a fan starting from
* edge "e". The fan *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN );
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
while( ! Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
}
assert( size == 0 );
CALL_END_OR_END_DATA();
}
static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Render as many CCW triangles as possible in a strip starting from
* edge "e". The strip *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP );
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
while( ! Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Dprev;
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
if( Marked( e->Lface )) break;
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
}
assert( size == 0 );
CALL_END_OR_END_DATA();
}
/************************ Boundary contour decomposition ******************/
/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one
* contour for each face marked "inside". The rendering output is
* provided as callbacks (see the api).
*/
void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh )
{
GLUface *f;
GLUhalfEdge *e;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
if( f->inside ) {
CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP );
e = f->anEdge;
do {
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
e = e->Lnext;
} while( e != f->anEdge );
CALL_END_OR_END_DATA();
}
}
}
/************************ Quick-and-dirty decomposition ******************/
#define SIGN_INCONSISTENT 2
static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
/*
* If check==FALSE, we compute the polygon normal and place it in norm[].
* If check==TRUE, we check that each triangle in the fan from v0 has a
* consistent orientation with respect to norm[]. If triangles are
* consistently oriented CCW, return 1; if CW, return -1; if all triangles
* are degenerate return 0; otherwise (no consistent orientation) return
* SIGN_INCONSISTENT.
*/
{
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble dot, xc, yc, zc, xp, yp, zp, n[3];
int sign = 0;
/* Find the polygon normal. It is important to get a reasonable
* normal even when the polygon is self-intersecting (eg. a bowtie).
* Otherwise, the computed normal could be very tiny, but perpendicular
* to the true plane of the polygon due to numerical noise. Then all
* the triangles would appear to be degenerate and we would incorrectly
* decompose the polygon as a fan (or simply not render it at all).
*
* We use a sum-of-triangles normal algorithm rather than the more
* efficient sum-of-trapezoids method (used in CheckOrientation()
* in normal.c). This lets us explicitly reverse the signed area
* of some triangles to get a reasonable normal in the self-intersecting
* case.
*/
if( ! check ) {
norm[0] = norm[1] = norm[2] = 0.0;
}
vc = v0 + 1;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
while( ++vc < vn ) {
xp = xc; yp = yc; zp = zc;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
/* Compute (vp - v0) cross (vc - v0) */
n[0] = yp*zc - zp*yc;
n[1] = zp*xc - xp*zc;
n[2] = xp*yc - yp*xc;
dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2];
if( ! check ) {
/* Reverse the contribution of back-facing triangles to get
* a reasonable normal for self-intersecting polygons (see above)
*/
if( dot >= 0 ) {
norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2];
} else {
norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2];
}
} else if( dot != 0 ) {
/* Check the new orientation for consistency with previous triangles */
if( dot > 0 ) {
if( sign < 0 ) return SIGN_INCONSISTENT;
sign = 1;
} else {
if( sign > 0 ) return SIGN_INCONSISTENT;
sign = -1;
}
}
}
return sign;
}
/* __gl_renderCache( tess ) takes a single contour and tries to render it
* as a triangle fan. This handles convex polygons, as well as some
* non-convex polygons if we get lucky.
*
* Returns TRUE if the polygon was successfully rendered. The rendering
* output is provided as callbacks (see the api).
*/
GLboolean __gl_renderCache( GLUtesselator *tess )
{
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble norm[3];
int sign;
if( tess->cacheCount < 3 ) {
/* Degenerate contour -- no output */
return TRUE;
}
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
ComputeNormal( tess, norm, FALSE );
}
sign = ComputeNormal( tess, norm, TRUE );
if( sign == SIGN_INCONSISTENT ) {
/* Fan triangles did not have a consistent orientation */
return FALSE;
}
if( sign == 0 ) {
/* All triangles were degenerate */
return TRUE;
}
/* Make sure we do the right thing for each winding rule */
switch( tess->windingRule ) {
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
break;
case GLU_TESS_WINDING_POSITIVE:
if( sign < 0 ) return TRUE;
break;
case GLU_TESS_WINDING_NEGATIVE:
if( sign > 0 ) return TRUE;
break;
case GLU_TESS_WINDING_ABS_GEQ_TWO:
return TRUE;
}
CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP
: (tess->cacheCount > 3) ? GL_TRIANGLE_FAN
: GL_TRIANGLES );
CALL_VERTEX_OR_VERTEX_DATA( v0->data );
if( sign > 0 ) {
for( vc = v0+1; vc < vn; ++vc ) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data );
}
} else {
for( vc = vn-1; vc > v0; --vc ) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data );
}
}
CALL_END_OR_END_DATA();
return TRUE;
}
+52
View File
@@ -0,0 +1,52 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __render_h_
#define __render_h_
#include "mesh.h"
/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
* fans, strips, and separate triangles. A substantial effort is made
* to use as few rendering primitives as possible (ie. to make the fans
* and strips as large as possible).
*
* The rendering output is provided as callbacks (see the api).
*/
void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh );
void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh );
GLboolean __gl_renderCache( GLUtesselator *tess );
#endif
+1358
View File
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __sweep_h_
#define __sweep_h_
#include "mesh.h"
/* __gl_computeInterior( tess ) computes the planar arrangement specified
* by the given contours, and further subdivides this arrangement
* into regions. Each region is marked "inside" if it belongs
* to the polygon, according to the rule given by tess->windingRule.
* Each interior region is guaranteed be monotone.
*/
int __gl_computeInterior( GLUtesselator *tess );
/* The following is here *only* for access by debugging routines */
#include "dict.h"
/* For each pair of adjacent edges crossing the sweep line, there is
* an ActiveRegion to represent the region between them. The active
* regions are kept in sorted order in a dynamic dictionary. As the
* sweep line crosses each vertex, we update the affected regions.
*/
struct ActiveRegion {
GLUhalfEdge *eUp; /* upper edge, directed right to left */
DictNode *nodeUp; /* dictionary node corresponding to eUp */
int windingNumber; /* used to determine which regions are
* inside the polygon */
GLboolean inside; /* is this region inside the polygon? */
GLboolean sentinel; /* marks fake edges at t = +/-infinity */
GLboolean dirty; /* marks regions where the upper or lower
* edge has changed, but we haven't checked
* whether they intersect yet */
GLboolean fixUpperEdge; /* marks temporary edges introduced when
* we process a "right vertex" (one without
* any edges leaving to the right) */
};
#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp)))
#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp)))
#endif
+629
View File
@@ -0,0 +1,629 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "../prboom/SDL_opengl.h" // JDC
//#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include <setjmp.h>
#include "memalloc.h"
#include "tess.h"
#include "mesh.h"
#include "normal.h"
#include "sweep.h"
#include "tessmono.h"
#include "render.h"
#define GLU_TESS_DEFAULT_TOLERANCE 0.0
#define GLU_TESS_MESH 100112 /* void (*)(GLUmesh *mesh) */
#define TRUE 1
#define FALSE 0
/*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {}
/*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {}
/*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {}
/*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {}
/*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {}
/*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **dataOut ) {}
/*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3],
void *data[4],
GLfloat weight[4],
void **outData,
void *polygonData ) {}
/* Half-edges are allocated in pairs (see mesh.c) */
typedef struct { GLUhalfEdge e, eSym; } EdgePair;
#undef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \
MAX(sizeof(GLUvertex),sizeof(GLUface))))
GLUtesselator * GLAPIENTRY
gluNewTess( void )
{
GLUtesselator *tess;
/* Only initialize fields which can be changed by the api. Other fields
* are initialized where they are used.
*/
if (memInit( MAX_FAST_ALLOC ) == 0) {
return 0; /* out of memory */
}
tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator ));
if (tess == NULL) {
return 0; /* out of memory */
}
tess->state = T_DORMANT;
tess->normal[0] = 0;
tess->normal[1] = 0;
tess->normal[2] = 0;
tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE;
tess->windingRule = GLU_TESS_WINDING_ODD;
tess->flagBoundary = FALSE;
tess->boundaryOnly = FALSE;
tess->callBegin = &noBegin;
tess->callEdgeFlag = &noEdgeFlag;
tess->callVertex = &noVertex;
tess->callEnd = &noEnd;
tess->callError = &noError;
tess->callCombine = &noCombine;
tess->callMesh = &noMesh;
tess->callBeginData= &__gl_noBeginData;
tess->callEdgeFlagData= &__gl_noEdgeFlagData;
tess->callVertexData= &__gl_noVertexData;
tess->callEndData= &__gl_noEndData;
tess->callErrorData= &__gl_noErrorData;
tess->callCombineData= &__gl_noCombineData;
tess->polygonData= NULL;
return tess;
}
static void MakeDormant( GLUtesselator *tess )
{
/* Return the tessellator to its original dormant state. */
if( tess->mesh != NULL ) {
__gl_meshDeleteMesh( tess->mesh );
}
tess->state = T_DORMANT;
tess->lastEdge = NULL;
tess->mesh = NULL;
}
#define RequireState( tess, s ) if( tess->state != s ) GotoState(tess,s)
static void GotoState( GLUtesselator *tess, enum TessState newState )
{
while( tess->state != newState ) {
/* We change the current state one level at a time, to get to
* the desired state.
*/
if( tess->state < newState ) {
switch( tess->state ) {
case T_DORMANT:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON );
gluTessBeginPolygon( tess, NULL );
break;
case T_IN_POLYGON:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR );
gluTessBeginContour( tess );
break;
default:
;
}
} else {
switch( tess->state ) {
case T_IN_CONTOUR:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR );
gluTessEndContour( tess );
break;
case T_IN_POLYGON:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON );
/* gluTessEndPolygon( tess ) is too much work! */
MakeDormant( tess );
break;
default:
;
}
}
}
}
void GLAPIENTRY
gluDeleteTess( GLUtesselator *tess )
{
RequireState( tess, T_DORMANT );
memFree( tess );
}
void GLAPIENTRY
gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value )
{
GLenum windingRule;
switch( which ) {
case GLU_TESS_TOLERANCE:
if( value < 0.0 || value > 1.0 ) break;
tess->relTolerance = value;
return;
case GLU_TESS_WINDING_RULE:
windingRule = (GLenum) value;
if( windingRule != value ) break; /* not an integer */
switch( windingRule ) {
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
case GLU_TESS_WINDING_POSITIVE:
case GLU_TESS_WINDING_NEGATIVE:
case GLU_TESS_WINDING_ABS_GEQ_TWO:
tess->windingRule = windingRule;
return;
default:
break;
}
case GLU_TESS_BOUNDARY_ONLY:
tess->boundaryOnly = (value != 0);
return;
default:
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
return;
}
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE );
}
/* Returns tessellator property */
void GLAPIENTRY
gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value )
{
switch (which) {
case GLU_TESS_TOLERANCE:
/* tolerance should be in range [0..1] */
assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0);
*value= tess->relTolerance;
break;
case GLU_TESS_WINDING_RULE:
assert(tess->windingRule == GLU_TESS_WINDING_ODD ||
tess->windingRule == GLU_TESS_WINDING_NONZERO ||
tess->windingRule == GLU_TESS_WINDING_POSITIVE ||
tess->windingRule == GLU_TESS_WINDING_NEGATIVE ||
tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO);
*value= tess->windingRule;
break;
case GLU_TESS_BOUNDARY_ONLY:
assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE);
*value= tess->boundaryOnly;
break;
default:
*value= 0.0;
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
break;
}
} /* gluGetTessProperty() */
void GLAPIENTRY
gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z )
{
tess->normal[0] = x;
tess->normal[1] = y;
tess->normal[2] = z;
}
void GLAPIENTRY
gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn)
{
switch( which ) {
case GLU_TESS_BEGIN:
tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn;
return;
case GLU_TESS_BEGIN_DATA:
tess->callBeginData = (fn == NULL) ?
&__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
return;
case GLU_TESS_EDGE_FLAG:
tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag :
(void (GLAPIENTRY *)(GLboolean)) fn;
/* If the client wants boundary edges to be flagged,
* we render everything as separate triangles (no strips or fans).
*/
tess->flagBoundary = (fn != NULL);
return;
case GLU_TESS_EDGE_FLAG_DATA:
tess->callEdgeFlagData= (fn == NULL) ?
&__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn;
/* If the client wants boundary edges to be flagged,
* we render everything as separate triangles (no strips or fans).
*/
tess->flagBoundary = (fn != NULL);
return;
case GLU_TESS_VERTEX:
tess->callVertex = (fn == NULL) ? &noVertex :
(void (GLAPIENTRY *)(void *)) fn;
return;
case GLU_TESS_VERTEX_DATA:
tess->callVertexData = (fn == NULL) ?
&__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn;
return;
case GLU_TESS_END:
tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn;
return;
case GLU_TESS_END_DATA:
tess->callEndData = (fn == NULL) ? &__gl_noEndData :
(void (GLAPIENTRY *)(void *)) fn;
return;
case GLU_TESS_ERROR:
tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn;
return;
case GLU_TESS_ERROR_DATA:
tess->callErrorData = (fn == NULL) ?
&__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
return;
case GLU_TESS_COMBINE:
tess->callCombine = (fn == NULL) ? &noCombine :
(void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn;
return;
case GLU_TESS_COMBINE_DATA:
tess->callCombineData = (fn == NULL) ? &__gl_noCombineData :
(void (GLAPIENTRY *)(GLdouble [3],
void *[4],
GLfloat [4],
void **,
void *)) fn;
return;
case GLU_TESS_MESH:
tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn;
return;
default:
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
return;
}
}
static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
GLUhalfEdge *e;
e = tess->lastEdge;
if( e == NULL ) {
/* Make a self-loop (one vertex, one edge). */
e = __gl_meshMakeEdge( tess->mesh );
if (e == NULL) return 0;
if ( !__gl_meshSplice( e, e->Sym ) ) return 0;
} else {
/* Create a new vertex and edge which immediately follow e
* in the ordering around the left face.
*/
if (__gl_meshSplitEdge( e ) == NULL) return 0;
e = e->Lnext;
}
/* The new vertex is now e->Org. */
e->Org->data = data;
e->Org->coords[0] = coords[0];
e->Org->coords[1] = coords[1];
e->Org->coords[2] = coords[2];
/* The winding of an edge says how the winding number changes as we
* cross from the edge''s right face to its left face. We add the
* vertices in such an order that a CCW contour will add +1 to
* the winding number of the region inside the contour.
*/
e->winding = 1;
e->Sym->winding = -1;
tess->lastEdge = e;
return 1;
}
static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
CachedVertex *v = &tess->cache[tess->cacheCount];
v->data = data;
v->coords[0] = coords[0];
v->coords[1] = coords[1];
v->coords[2] = coords[2];
++tess->cacheCount;
}
static int EmptyCache( GLUtesselator *tess )
{
CachedVertex *v = tess->cache;
CachedVertex *vLast;
tess->mesh = __gl_meshNewMesh();
if (tess->mesh == NULL) return 0;
for( vLast = v + tess->cacheCount; v < vLast; ++v ) {
if ( !AddVertex( tess, v->coords, v->data ) ) return 0;
}
tess->cacheCount = 0;
tess->emptyCache = FALSE;
return 1;
}
void GLAPIENTRY
gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
int i, tooLarge = FALSE;
GLdouble x, clamped[3];
RequireState( tess, T_IN_CONTOUR );
if( tess->emptyCache ) {
if ( !EmptyCache( tess ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
tess->lastEdge = NULL;
}
for( i = 0; i < 3; ++i ) {
x = coords[i];
if( x < - GLU_TESS_MAX_COORD ) {
x = - GLU_TESS_MAX_COORD;
tooLarge = TRUE;
}
if( x > GLU_TESS_MAX_COORD ) {
x = GLU_TESS_MAX_COORD;
tooLarge = TRUE;
}
clamped[i] = x;
}
if( tooLarge ) {
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE );
}
if( tess->mesh == NULL ) {
if( tess->cacheCount < TESS_MAX_CACHE ) {
CacheVertex( tess, clamped, data );
return;
}
if ( !EmptyCache( tess ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
}
if ( !AddVertex( tess, clamped, data ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
}
}
void GLAPIENTRY
gluTessBeginPolygon( GLUtesselator *tess, void *data )
{
RequireState( tess, T_DORMANT );
tess->state = T_IN_POLYGON;
tess->cacheCount = 0;
tess->emptyCache = FALSE;
tess->mesh = NULL;
tess->polygonData= data;
}
void GLAPIENTRY
gluTessBeginContour( GLUtesselator *tess )
{
RequireState( tess, T_IN_POLYGON );
tess->state = T_IN_CONTOUR;
tess->lastEdge = NULL;
if( tess->cacheCount > 0 ) {
/* Just set a flag so we don't get confused by empty contours
* -- these can be generated accidentally with the obsolete
* NextContour() interface.
*/
tess->emptyCache = TRUE;
}
}
void GLAPIENTRY
gluTessEndContour( GLUtesselator *tess )
{
RequireState( tess, T_IN_CONTOUR );
tess->state = T_IN_POLYGON;
}
void GLAPIENTRY
gluTessEndPolygon( GLUtesselator *tess )
{
GLUmesh *mesh;
if (setjmp(tess->env) != 0) {
/* come back here if out of memory */
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
RequireState( tess, T_IN_POLYGON );
tess->state = T_DORMANT;
if( tess->mesh == NULL ) {
if( ! tess->flagBoundary && tess->callMesh == &noMesh ) {
/* Try some special code to make the easy cases go quickly
* (eg. convex polygons). This code does NOT handle multiple contours,
* intersections, edge flags, and of course it does not generate
* an explicit mesh either.
*/
if( __gl_renderCache( tess )) {
tess->polygonData= NULL;
return;
}
}
if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/
}
/* Determine the polygon normal and project vertices onto the plane
* of the polygon.
*/
__gl_projectPolygon( tess );
/* __gl_computeInterior( tess ) computes the planar arrangement specified
* by the given contours, and further subdivides this arrangement
* into regions. Each region is marked "inside" if it belongs
* to the polygon, according to the rule given by tess->windingRule.
* Each interior region is guaranteed be monotone.
*/
if ( !__gl_computeInterior( tess ) ) {
longjmp(tess->env,1); /* could've used a label */
}
mesh = tess->mesh;
if( ! tess->fatalError ) {
int rc = 1;
/* If the user wants only the boundary contours, we throw away all edges
* except those which separate the interior from the exterior.
* Otherwise we tessellate all the regions marked "inside".
*/
if( tess->boundaryOnly ) {
rc = __gl_meshSetWindingNumber( mesh, 1, TRUE );
} else {
rc = __gl_meshTessellateInterior( mesh );
}
if (rc == 0) longjmp(tess->env,1); /* could've used a label */
__gl_meshCheckMesh( mesh );
if( tess->callBegin != &noBegin || tess->callEnd != &noEnd
|| tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag
|| tess->callBeginData != &__gl_noBeginData
|| tess->callEndData != &__gl_noEndData
|| tess->callVertexData != &__gl_noVertexData
|| tess->callEdgeFlagData != &__gl_noEdgeFlagData )
{
if( tess->boundaryOnly ) {
__gl_renderBoundary( tess, mesh ); /* output boundary contours */
} else {
__gl_renderMesh( tess, mesh ); /* output strips and fans */
}
}
if( tess->callMesh != &noMesh ) {
/* Throw away the exterior faces, so that all faces are interior.
* This way the user doesn't have to check the "inside" flag,
* and we don't need to even reveal its existence. It also leaves
* the freedom for an implementation to not generate the exterior
* faces in the first place.
*/
__gl_meshDiscardExterior( mesh );
(*tess->callMesh)( mesh ); /* user wants the mesh itself */
tess->mesh = NULL;
tess->polygonData= NULL;
return;
}
}
__gl_meshDeleteMesh( mesh );
tess->polygonData= NULL;
tess->mesh = NULL;
}
/*XXXblythe unused function*/
#if 0
void GLAPIENTRY
gluDeleteMesh( GLUmesh *mesh )
{
__gl_meshDeleteMesh( mesh );
}
#endif
/*******************************************************/
/* Obsolete calls -- for backward compatibility */
void GLAPIENTRY
gluBeginPolygon( GLUtesselator *tess )
{
gluTessBeginPolygon( tess, NULL );
gluTessBeginContour( tess );
}
/*ARGSUSED*/
void GLAPIENTRY
gluNextContour( GLUtesselator *tess, GLenum type )
{
gluTessEndContour( tess );
gluTessBeginContour( tess );
}
void GLAPIENTRY
gluEndPolygon( GLUtesselator *tess )
{
gluTessEndContour( tess );
gluTessEndPolygon( tess );
}
+166
View File
@@ -0,0 +1,166 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __tess_h_
#define __tess_h_
#include "../prboom/SDL_opengl.h" // JDC
// JDC #include <GL/glu.h>
#include <setjmp.h>
#include "mesh.h"
#include "dict.h"
#include "priorityq.h"
/* The begin/end calls must be properly nested. We keep track of
* the current state to enforce the ordering.
*/
enum TessState { T_DORMANT, T_IN_POLYGON, T_IN_CONTOUR };
/* We cache vertex data for single-contour polygons so that we can
* try a quick-and-dirty decomposition first.
*/
#define TESS_MAX_CACHE 100
typedef struct CachedVertex {
GLdouble coords[3];
void *data;
} CachedVertex;
struct GLUtesselator {
/*** state needed for collecting the input data ***/
enum TessState state; /* what begin/end calls have we seen? */
GLUhalfEdge *lastEdge; /* lastEdge->Org is the most recent vertex */
GLUmesh *mesh; /* stores the input contours, and eventually
the tessellation itself */
void (GLAPIENTRY *callError)( GLenum errnum );
/*** state needed for projecting onto the sweep plane ***/
GLdouble normal[3]; /* user-specified normal (if provided) */
GLdouble sUnit[3]; /* unit vector in s-direction (debugging) */
GLdouble tUnit[3]; /* unit vector in t-direction (debugging) */
/*** state needed for the line sweep ***/
GLdouble relTolerance; /* tolerance for merging features */
GLenum windingRule; /* rule for determining polygon interior */
GLboolean fatalError; /* fatal error: needed combine callback */
Dict *dict; /* edge dictionary for sweep line */
PriorityQ *pq; /* priority queue of vertex events */
GLUvertex *event; /* current sweep event being processed */
void (GLAPIENTRY *callCombine)( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData );
/*** state needed for rendering callbacks (see render.c) ***/
GLboolean flagBoundary; /* mark boundary edges (use EdgeFlag) */
GLboolean boundaryOnly; /* Extract contours, not triangles */
GLUface *lonelyTriList;
/* list of triangles which could not be rendered as strips or fans */
void (GLAPIENTRY *callBegin)( GLenum type );
void (GLAPIENTRY *callEdgeFlag)( GLboolean boundaryEdge );
void (GLAPIENTRY *callVertex)( void *data );
void (GLAPIENTRY *callEnd)( void );
void (GLAPIENTRY *callMesh)( GLUmesh *mesh );
/*** state needed to cache single-contour polygons for renderCache() */
GLboolean emptyCache; /* empty cache on next vertex() call */
int cacheCount; /* number of cached vertices */
CachedVertex cache[TESS_MAX_CACHE]; /* the vertex data */
/*** rendering callbacks that also pass polygon data ***/
void (GLAPIENTRY *callBeginData)( GLenum type, void *polygonData );
void (GLAPIENTRY *callEdgeFlagData)( GLboolean boundaryEdge,
void *polygonData );
void (GLAPIENTRY *callVertexData)( void *data, void *polygonData );
void (GLAPIENTRY *callEndData)( void *polygonData );
void (GLAPIENTRY *callErrorData)( GLenum errnum, void *polygonData );
void (GLAPIENTRY *callCombineData)( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData,
void *polygonData );
jmp_buf env; /* place to jump to when memAllocs fail */
void *polygonData; /* client data for current polygon */
};
void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData );
void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData );
void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData );
void GLAPIENTRY __gl_noEndData( void *polygonData );
void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData );
void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData,
void *polygonData );
#define CALL_BEGIN_OR_BEGIN_DATA(a) \
if (tess->callBeginData != &__gl_noBeginData) \
(*tess->callBeginData)((a),tess->polygonData); \
else (*tess->callBegin)((a));
#define CALL_VERTEX_OR_VERTEX_DATA(a) \
if (tess->callVertexData != &__gl_noVertexData) \
(*tess->callVertexData)((a),tess->polygonData); \
else (*tess->callVertex)((a));
#define CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA(a) \
if (tess->callEdgeFlagData != &__gl_noEdgeFlagData) \
(*tess->callEdgeFlagData)((a),tess->polygonData); \
else (*tess->callEdgeFlag)((a));
#define CALL_END_OR_END_DATA() \
if (tess->callEndData != &__gl_noEndData) \
(*tess->callEndData)(tess->polygonData); \
else (*tess->callEnd)();
#define CALL_COMBINE_OR_COMBINE_DATA(a,b,c,d) \
if (tess->callCombineData != &__gl_noCombineData) \
(*tess->callCombineData)((a),(b),(c),(d),tess->polygonData); \
else (*tess->callCombine)((a),(b),(c),(d));
#define CALL_ERROR_OR_ERROR_DATA(a) \
if (tess->callErrorData != &__gl_noErrorData) \
(*tess->callErrorData)((a),tess->polygonData); \
else (*tess->callError)((a));
#endif
+202
View File
@@ -0,0 +1,202 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "../prboom/SDL_opengl.h" // JDC
//#include "gluos.h"
#include <stdlib.h>
#include "geom.h"
#include "mesh.h"
#include "tessmono.h"
#include <assert.h>
#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \
eDst->Sym->winding += eSrc->Sym->winding)
/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region
* (what else would it do??) The region must consist of a single
* loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this
* case means that any vertical line intersects the interior of the
* region in a single interval.
*
* Tessellation consists of adding interior edges (actually pairs of
* half-edges), to split the region into non-overlapping triangles.
*
* The basic idea is explained in Preparata and Shamos (which I don''t
* have handy right now), although their implementation is more
* complicated than this one. The are two edge chains, an upper chain
* and a lower chain. We process all vertices from both chains in order,
* from right to left.
*
* The algorithm ensures that the following invariant holds after each
* vertex is processed: the untessellated region consists of two
* chains, where one chain (say the upper) is a single edge, and
* the other chain is concave. The left vertex of the single edge
* is always to the left of all vertices in the concave chain.
*
* Each step consists of adding the rightmost unprocessed vertex to one
* of the two chains, and forming a fan of triangles from the rightmost
* of two chain endpoints. Determining whether we can add each triangle
* to the fan is a simple orientation test. By making the fan as large
* as possible, we restore the invariant (check it yourself).
*/
int __gl_meshTessellateMonoRegion( GLUface *face )
{
GLUhalfEdge *up, *lo;
/* All edges are oriented CCW around the boundary of the region.
* First, find the half-edge whose origin vertex is rightmost.
* Since the sweep goes from left to right, face->anEdge should
* be close to the edge we want.
*/
up = face->anEdge;
assert( up->Lnext != up && up->Lnext->Lnext != up );
for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev )
;
for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext )
;
lo = up->Lprev;
while( up->Lnext != lo ) {
if( VertLeq( up->Dst, lo->Org )) {
/* up->Dst is on the left. It is safe to form triangles from lo->Org.
* The EdgeGoesLeft test guarantees progress even when some triangles
* are CW, given that the upper and lower chains are truly monotone.
*/
while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext )
|| EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) {
GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo );
if (tempHalfEdge == NULL) return 0;
lo = tempHalfEdge->Sym;
}
lo = lo->Lprev;
} else {
/* lo->Org is on the left. We can make CCW triangles from up->Dst. */
while( lo->Lnext != up && (EdgeGoesRight( up->Lprev )
|| EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) {
GLUhalfEdge *tempHalfEdge= __gl_meshConnect( up, up->Lprev );
if (tempHalfEdge == NULL) return 0;
up = tempHalfEdge->Sym;
}
up = up->Lnext;
}
}
/* Now lo->Org == up->Dst == the leftmost vertex. The remaining region
* can be tessellated in a fan from this leftmost vertex.
*/
assert( lo->Lnext != up );
while( lo->Lnext->Lnext != up ) {
GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo );
if (tempHalfEdge == NULL) return 0;
lo = tempHalfEdge->Sym;
}
return 1;
}
/* __gl_meshTessellateInterior( mesh ) tessellates each region of
* the mesh which is marked "inside" the polygon. Each such region
* must be monotone.
*/
int __gl_meshTessellateInterior( GLUmesh *mesh )
{
GLUface *f, *next;
/*LINTED*/
for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
/* Make sure we don''t try to tessellate the new triangles. */
next = f->next;
if( f->inside ) {
if ( !__gl_meshTessellateMonoRegion( f ) ) return 0;
}
}
return 1;
}
/* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces
* which are not marked "inside" the polygon. Since further mesh operations
* on NULL faces are not allowed, the main purpose is to clean up the
* mesh so that exterior loops are not represented in the data structure.
*/
void __gl_meshDiscardExterior( GLUmesh *mesh )
{
GLUface *f, *next;
/*LINTED*/
for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
/* Since f will be destroyed, save its next pointer. */
next = f->next;
if( ! f->inside ) {
__gl_meshZapFace( f );
}
}
}
#define MARKED_FOR_DELETION 0x7fffffff
/* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the
* winding numbers on all edges so that regions marked "inside" the
* polygon have a winding number of "value", and regions outside
* have a winding number of 0.
*
* If keepOnlyBoundary is TRUE, it also deletes all edges which do not
* separate an interior region from an exterior one.
*/
int __gl_meshSetWindingNumber( GLUmesh *mesh, int value,
GLboolean keepOnlyBoundary )
{
GLUhalfEdge *e, *eNext;
for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
eNext = e->next;
if( e->Rface->inside != e->Lface->inside ) {
/* This is a boundary edge (one side is interior, one is exterior). */
e->winding = (e->Lface->inside) ? value : -value;
} else {
/* Both regions are interior, or both are exterior. */
if( ! keepOnlyBoundary ) {
e->winding = 0;
} else {
if ( !__gl_meshDelete( e ) ) return 0;
}
}
}
return 1;
}
+71
View File
@@ -0,0 +1,71 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __tessmono_h_
#define __tessmono_h_
/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region
* (what else would it do??) The region must consist of a single
* loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this
* case means that any vertical line intersects the interior of the
* region in a single interval.
*
* Tessellation consists of adding interior edges (actually pairs of
* half-edges), to split the region into non-overlapping triangles.
*
* __gl_meshTessellateInterior( mesh ) tessellates each region of
* the mesh which is marked "inside" the polygon. Each such region
* must be monotone.
*
* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces
* which are not marked "inside" the polygon. Since further mesh operations
* on NULL faces are not allowed, the main purpose is to clean up the
* mesh so that exterior loops are not represented in the data structure.
*
* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the
* winding numbers on all edges so that regions marked "inside" the
* polygon have a winding number of "value", and regions outside
* have a winding number of 0.
*
* If keepOnlyBoundary is TRUE, it also deletes all edges which do not
* separate an interior region from an exterior one.
*/
int __gl_meshTessellateMonoRegion( GLUface *face );
int __gl_meshTessellateInterior( GLUmesh *mesh );
void __gl_meshDiscardExterior( GLUmesh *mesh );
int __gl_meshSetWindingNumber( GLUmesh *mesh, int value,
GLboolean keepOnlyBoundary );
#endif
+72
View File
@@ -0,0 +1,72 @@
#
# automake Makefile.am for the PrBoom source directory
#
#
# Process this file with automake to produce Makefile.in
#
#
SUBDIRS = SDL POSIX MAC
gamesdir=$(prefix)/games
games_PROGRAMS = prboom prboom-game-server
CFLAGS = @CFLAGS@ @SDL_CFLAGS@
prboom_game_server_SOURCES = d_server.c protocol.h
prboom_game_server_LDADD = POSIX/libposixdoom.a SDL/i_network.o @NET_LIBS@ @SDL_LIBS@
COMMON_SRC = \
am_map.c g_game.c p_maputl.h r_plane.h \
am_map.h g_game.h p_mobj.c r_demo.c r_segs.c \
hu_lib.c lprintf.c p_mobj.h r_demo.h r_segs.h \
hu_lib.h lprintf.h p_plats.c r_sky.c \
d_deh.c hu_stuff.c m_argv.c p_pspr.c r_sky.h \
d_deh.h hu_stuff.h m_argv.h p_pspr.h r_state.h \
d_englsh.h i_joy.h m_bbox.c p_saveg.c r_things.c \
d_event.h m_bbox.h p_saveg.h r_things.h \
d_items.c i_network.h m_cheat.c p_setup.c s_sound.c \
d_items.h i_sound.h m_cheat.h p_setup.h s_sound.h \
d_main.c i_system.h m_fixed.h p_sight.c sounds.c \
d_main.h i_video.h m_menu.c p_spec.c sounds.h \
info.c m_menu.h p_spec.h st_lib.c \
d_net.h info.h m_misc.c p_switch.c st_lib.h \
d_player.h m_misc.h p_telept.c st_stuff.c \
m_random.c p_tick.c st_stuff.h i_main.h \
d_think.h m_random.h p_tick.h tables.c \
d_ticcmd.h m_swap.h p_user.c tables.h \
doomdata.h p_ceilng.c p_user.h v_video.c \
doomdef.c p_doors.c protocol.h v_video.h \
doomdef.h p_enemy.c r_bsp.c version.c \
doomstat.c p_enemy.h r_bsp.h version.h \
doomstat.h p_floor.c r_data.c w_wad.c \
doomtype.h p_genlin.c r_data.h w_wad.h \
dstrings.c p_inter.c r_defs.h wi_stuff.c \
dstrings.h p_inter.h r_draw.c wi_stuff.h \
f_finale.c p_lights.c r_draw.h z_bmalloc.c \
f_finale.h p_map.c r_main.c z_bmalloc.h \
f_wipe.c p_map.h r_main.h z_zone.c \
f_wipe.h p_maputl.c r_plane.c z_zone.h \
md5.c md5.h p_checksum.h p_checksum.c \
r_patch.c r_patch.h r_fps.c r_fps.h \
r_filter.c r_filter.h
NET_CLIENT_SRC = d_client.c
if BUILD_GL
USE_GL_SRC = gl_intern.h gl_main.c gl_struct.h gl_texture.c
else
USE_GL_SRC =
endif
if WAD_MMAP
WAD_SRC = w_mmap.c
else
WAD_SRC = w_memcache.c
endif
prboom_SOURCES = mmus2mid.c mmus2mid.h $(COMMON_SRC) $(NET_CLIENT_SRC) $(USE_GL_SRC) $(WAD_SRC)
prboom_LDADD = SDL/libsdldoom.a @MIXER_LIBS@ @NET_LIBS@ @SDL_LIBS@ @GL_LIBS@ @MATH_LIB@
EXTRA_DIST = \
r_drawcolumn.inl r_drawflush.inl r_drawspan.inl r_drawcolpipeline.inl
+211
View File
@@ -0,0 +1,211 @@
/*
* SDL_opengl.h
* doom
*
* Created by John Carmack on 4/13/09.
* Copyright 2009 idSoftware. All rights reserved.
*
* iPhone glue to get the prBoom code compiling
* Replaces SDL_opengl.h
*/
#ifndef __SDL_OPENGL_H__
#define __SDL_OPENGL_H__
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#define GLAPIENTRY
// this needs to be added before each projection matrix
int iphoneRotateForLandscape();
// no colorTable in ES
typedef void (* PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *table);
static GLubyte *gluErrorString( int err ) { return (GLubyte *)"GLU error"; }
static void *SDL_GL_GetProcAddress( const char *proc ) { return 0; }
static void SDL_GL_SwapBuffers() {}
// we need to emulate immediate mode gl for ES
void glBegin( GLenum prim );
void glVertex3f( GLfloat x, GLfloat y, GLfloat z );
void glVertex3fv( GLfloat *xyz );
void glVertex2f( GLfloat x, GLfloat y );
void glVertex2i( GLint x, GLint y );
void glTexCoord2i( GLint s, GLint t );
void glTexCoord2f( GLfloat s, GLfloat t );
void glTexCoord2fv( GLfloat *st );
void glEnd();
// Doom just uses state color for all draw calls, setting it once
// before a glBegin, rather than setting it each vertex, so we
// don't need to emulate the color functions.
//#defne VERTEX_COLOR
#ifdef VERTEX_COLOR
void glColor4ub( GLubyte r, GLubyte g, GLubyte b, GLubyte a );
void glColor4f( GLfloat r, GLfloat g, GLfloat b, GLfloat a );
void glColor4fv( GLfloat *rgba );
void glColor3f( GLfloat r, GLfloat g, GLfloat b );
#endif
// GLES only defines glColor4ub and glColor4f, so define the others in terms of that
#define glColor4fv(x) glColor4f(x[0],x[1],x[2],x[3])
#define glColor4ubv(x) glColor4ub(x[0],x[1],x[2],x[3])
#define glColor3f(r,g,b) glColor4f(r,g,b,1)
// The width and height need to be flipped for iPhone landscape mode,
// so redefine these functions to something that can do the work behind
// the scenes.
void landscapeViewport( GLint x, GLint y, GLsizei width, GLsizei height );
void landscapeScissor( GLint x, GLint y, GLsizei width, GLsizei height );
#define glViewport landscapeViewport
#define glScissor landscapeScissor
// ES made matching fixed and floating versions of some functions
#define glClearDepth glClearDepthf
#define glOrtho glOrthof
#define glFogi glFogx
// no GLdouble in ES, but needed for glu tesselator
typedef double GLdouble;
// ES doesn't have the messy clamp-to-half-border mode
#define GL_CLAMP GL_CLAMP_TO_EDGE
// this is the internal format used by the prBoom gl code
// ES doesn't allow format conversions between external and internal,
// so we need to manually convert to 5551 before doing glTexSubImage
#define GL_RGB5_A1 GL_RGBA
#define GL_RGBA8 GL_RGBA
#define GL_RGBA4 GL_RGBA
#define GL_RGBA2 GL_RGBA
// not available in ES, so prBoom's skies must be implemeted differently
static void glTexGenfv( int a, int b, void *c ) { };
static void glTexGenf( int a, int b, int c ) { };
// texGen enums not present in ES
#define GL_S 0x2000
#define GL_T 0x2001
#define GL_R 0x2002
#define GL_Q 0x2003
#define GL_OBJECT_LINEAR 0x2401
#define GL_OBJECT_PLANE 0x2501
#define GL_EYE_LINEAR 0x2400
#define GL_EYE_PLANE 0x2502
#define GL_TEXTURE_GEN_MODE 0x2500
#define GL_TEXTURE_GEN_S 0x0C60
#define GL_TEXTURE_GEN_T 0x0C61
#define GL_TEXTURE_GEN_R 0x0C62
#define GL_TEXTURE_GEN_Q 0x0C63
// other extensions not present in ES
// Whlle the iPhone exports the extension for paletted
// textures, it isn't actually supported in hardware, so
// they are expanded internally on glTexImage2D, making their
// use completely counterproductive.
#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB
#define GL_COLOR_INDEX 0x1900
#define GL_COLOR_INDEX8_EXT 0x80E5
//===========================
// all this for the glu tesselator, used by prBoom to make drawable sector geometry
//===========================
#include "../libtess/tess.h"
/* TessCallback */
#define GLU_TESS_BEGIN 100100
#define GLU_BEGIN 100100
#define GLU_TESS_VERTEX 100101
#define GLU_VERTEX 100101
#define GLU_TESS_END 100102
#define GLU_END 100102
#define GLU_TESS_ERROR 100103
#define GLU_TESS_EDGE_FLAG 100104
#define GLU_EDGE_FLAG 100104
#define GLU_TESS_COMBINE 100105
#define GLU_TESS_BEGIN_DATA 100106
#define GLU_TESS_VERTEX_DATA 100107
#define GLU_TESS_END_DATA 100108
#define GLU_TESS_ERROR_DATA 100109
#define GLU_TESS_EDGE_FLAG_DATA 100110
#define GLU_TESS_COMBINE_DATA 100111
/* TessContour */
#define GLU_CW 100120
#define GLU_CCW 100121
#define GLU_INTERIOR 100122
#define GLU_EXTERIOR 100123
#define GLU_UNKNOWN 100124
/* TessProperty */
#define GLU_TESS_WINDING_RULE 100140
#define GLU_TESS_BOUNDARY_ONLY 100141
#define GLU_TESS_TOLERANCE 100142
/* TessError */
#define GLU_TESS_ERROR1 100151
#define GLU_TESS_ERROR2 100152
#define GLU_TESS_ERROR3 100153
#define GLU_TESS_ERROR4 100154
#define GLU_TESS_ERROR5 100155
#define GLU_TESS_ERROR6 100156
#define GLU_TESS_ERROR7 100157
#define GLU_TESS_ERROR8 100158
#define GLU_TESS_MISSING_BEGIN_POLYGON 100151
#define GLU_TESS_MISSING_BEGIN_CONTOUR 100152
#define GLU_TESS_MISSING_END_POLYGON 100153
#define GLU_TESS_MISSING_END_CONTOUR 100154
#define GLU_TESS_COORD_TOO_LARGE 100155
#define GLU_TESS_NEED_COMBINE_CALLBACK 100156
/* TessWinding */
#define GLU_TESS_WINDING_ODD 100130
#define GLU_TESS_WINDING_NONZERO 100131
#define GLU_TESS_WINDING_POSITIVE 100132
#define GLU_TESS_WINDING_NEGATIVE 100133
#define GLU_TESS_WINDING_ABS_GEQ_TWO 100134
/* ErrorCode */
#define GLU_INVALID_ENUM 100900
#define GLU_INVALID_VALUE 100901
#define GLU_OUT_OF_MEMORY 100902
#define GLU_INCOMPATIBLE_GL_VERSION 100903
#define GLU_INVALID_OPERATION 100904
#define GLAPI
#define GLAPIENTRYP
typedef struct GLUtesselator GLUtesselator;
typedef GLUtesselator GLUtesselatorObj;
typedef GLUtesselator GLUtriangulatorObj;
#define GLU_TESS_MAX_COORD 1.0e150
/* Internal convenience typedefs */
typedef void (GLAPIENTRYP _GLUfuncptr)();
GLAPI void GLAPIENTRY gluTessBeginContour (GLUtesselator* tess);
GLAPI void GLAPIENTRY gluTessBeginPolygon (GLUtesselator* tess, GLvoid* data);
GLAPI void GLAPIENTRY gluTessCallback (GLUtesselator* tess, GLenum which, _GLUfuncptr CallBackFunc);
GLAPI void GLAPIENTRY gluTessEndContour (GLUtesselator* tess);
GLAPI void GLAPIENTRY gluTessEndPolygon (GLUtesselator* tess);
GLAPI void GLAPIENTRY gluTessNormal (GLUtesselator* tess, GLdouble valueX, GLdouble valueY, GLdouble valueZ);
GLAPI void GLAPIENTRY gluTessProperty (GLUtesselator* tess, GLenum which, GLdouble data);
GLAPI void GLAPIENTRY gluTessVertex (GLUtesselator* tess, GLdouble *location, GLvoid* data);
GLUtesselator * GLAPIENTRY gluNewTess( void );
void GLAPIENTRY gluDeleteTess( GLUtesselator *tess );
#endif
+1589
View File
File diff suppressed because it is too large Load Diff
+111
View File
@@ -0,0 +1,111 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* AutoMap module.
*
*-----------------------------------------------------------------------------*/
#ifndef __AMMAP_H__
#define __AMMAP_H__
#include "d_event.h"
#define MAPBITS 12
#define FRACTOMAPBITS (FRACBITS-MAPBITS)
// Used by ST StatusBar stuff.
#define AM_MSGHEADER (('a'<<24)+('m'<<16))
#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8))
#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8))
// Called by main loop.
boolean AM_Responder (event_t* ev);
// Called by main loop.
void AM_Ticker (void);
// Called by main loop,
// called instead of view drawer if automap active.
void AM_Drawer (void);
// Called to force the automap to quit
// if the level is completed while it is up.
void AM_Stop (void);
// killough 2/22/98: for saving automap information in savegame:
extern void AM_Start(void);
//jff 4/16/98 make externally available
extern void AM_clearMarks(void);
typedef struct
{
fixed_t x,y;
} mpoint_t;
extern mpoint_t *markpoints;
extern int markpointnum, markpointnum_max;
// end changes -- killough 2/22/98
// killough 5/2/98: moved from m_misc.c
//jff 1/7/98 automap colors added
extern int mapcolor_back; // map background
extern int mapcolor_grid; // grid lines color
extern int mapcolor_wall; // normal 1s wall color
extern int mapcolor_fchg; // line at floor height change color
extern int mapcolor_cchg; // line at ceiling height change color
extern int mapcolor_clsd; // line at sector with floor=ceiling color
extern int mapcolor_rkey; // red key color
extern int mapcolor_bkey; // blue key color
extern int mapcolor_ykey; // yellow key color
extern int mapcolor_rdor; // red door color (diff from keys to allow option)
extern int mapcolor_bdor; // blue door color (of enabling one not other)
extern int mapcolor_ydor; // yellow door color
extern int mapcolor_tele; // teleporter line color
extern int mapcolor_secr; // secret sector boundary color
//jff 4/23/98
extern int mapcolor_exit; // exit line
extern int mapcolor_unsn; // computer map unseen line color
extern int mapcolor_flat; // line with no floor/ceiling changes
extern int mapcolor_sprt; // general sprite color
extern int mapcolor_item; // item sprite color
extern int mapcolor_enemy; // enemy sprite color
extern int mapcolor_frnd; // friendly sprite color
extern int mapcolor_hair; // crosshair color
extern int mapcolor_sngl; // single player arrow color
extern int mapcolor_plyr[4]; // colors for players in multiplayer
extern int mapcolor_me; // consoleplayer's chosen colour
//jff 3/9/98
extern int map_secret_after; // secrets do not appear til after bagged
#endif
+112
View File
@@ -0,0 +1,112 @@
/**/
#define PACKAGE "prboom"
#define VERSION "2.5.0"
#ifdef DEBUG
/* Define to enable internal range checking */
#define RANGECHECK 1
/* Define this to see real-time memory allocation
* statistics, and enable extra debugging features
*/
#define INSTRUMENTED 1
/* Uncomment this to exhaustively run memory checks
* while the game is running (this is EXTREMELY slow).
* Only useful if INSTRUMENTED is also defined.
*/
#define CHECKHEAP 1
/* Uncomment this to cause heap dumps to be generated.
* Only useful if INSTRUMENTED is also defined.
*/
#define HEAPDUMP 1
/* Uncomment this to perform id checks on zone blocks,
* to detect corrupted and illegally freed blocks
*/
#define ZONEIDCHECK 1
/* CPhipps - some debugging macros for the new wad lump handling code */
/* Defining this causes quick checks which only impose an overhead if a
* posible error is detected. */
#define SIMPLECHECKS 1
/* Defining this causes time stamps to be created each time a lump is locked, and
* lumps locked for long periods of time are reported */
#define TIMEDIAG 1
#endif // DEBUG
#define DOGS 1
#define MONITOR_VISIBILITY 1
/*#define DISABLE_LUMP_CACHING*/
/**/
#define USE_SDL 1
/*#define HAVE_MIXER 1*/
#define HAVE_NET 1
#define USE_SDL_NET 1
/**/
#define HIGHRES 1
#define GL_DOOM 1
#define USE_GLU_TESS 1
#define USE_GLU_IMAGESCALE 1
#define USE_GLU_MIPMAP 1
#define DISABLE_DOUBLEBUFFER
/**/
#define STDC_HEADERS 1
#define stricmp strcasecmp
#define strnicmp strncasecmp
#define HAVE_INET_ATON 1
#define HAVE_INET_NTOP 1
#define HAVE_INET_PTON 1
#define HAVE_SETSOCKOPT 1
#define HAVE_SNPRINTF 1
#define HAVE_VSNPRINTF 1
#define HAVE_MKSTEMPS 1
#define HAVE_IPv6 1
#define HAVE_UNISTD_H
#define HAVE_SYS_WAIT_H
#define HAVE_GETOPT
/* causes a duplicate define warning
#define HAVE_NETINET_IN_H
*/
#define SYS_SIGLIST_DECLARED
/**/
#ifdef __BIG_ENDIAN__
#define WORDS_BIGENDIAN
#endif
#ifdef __i386__
#define I386_ASM 1
#endif
#define PACKEDATTR __attribute__((packed))
#define MACOSX
#define HAVE_LIBKERN_OSBYTEORDER_H
#define HAVE_OWN_MUSIC
#define UPDATE_MUSIC
#define SCREENSHOT_DIR I_DoomExeDir()
#define HEAPDUMP_DIR I_DoomExeDir()
//------ JDC config changes for iPhone ------------
#undef I386_ASM
#undef USE_SDL
#undef USE_SDL_NET
#undef HAVE_NET
#undef DOGS // not sure why this needs to be removed, info.c complains
#undef USE_GLU_IMAGESCALE
#undef USE_GLU_MIPMAP
+539
View File
@@ -0,0 +1,539 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Network client. Passes information to/from server, staying
* synchronised.
* Contains the main wait loop, waiting for network input or
* time before doing the next tic.
* Rewritten for LxDoom, but based around bits of the old code.
*
*-----------------------------------------------------------------------------
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef USE_SDL_NET
#include "SDL.h"
#endif
#include "doomtype.h"
#include "doomstat.h"
#include "d_net.h"
#include "z_zone.h"
#include "d_main.h"
#include "g_game.h"
#include "m_menu.h"
#include "p_checksum.h"
#include "protocol.h"
#include "i_network.h"
#include "i_system.h"
#include "i_main.h"
#include "i_video.h"
#include "m_argv.h"
#include "r_fps.h"
#include "lprintf.h"
static boolean server;
static int remotetic; // Tic expected from the remote
static int remotesend; // Tic expected by the remote
ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
static ticcmd_t* localcmds;
static unsigned numqueuedpackets;
static packet_header_t** queuedpacket;
int maketic;
int ticdup = 1;
static int xtratics = 0;
int wanted_player_number;
static boolean isExtraDDisplay = false;
static void D_QuitNetGame (void);
#ifndef HAVE_NET
doomcom_t* doomcom;
#endif
#ifdef HAVE_NET
void D_InitNetGame (void)
{
int i;
int numplayers = 1;
i = M_CheckParm("-net");
if (i && i < myargc-1) i++;
if (!(netgame = server = !!i)) {
playeringame[consoleplayer = 0] = true;
// e6y
// for play, recording or playback using "single-player coop" mode.
// Equivalent to using prboom_server with -N 1
netgame = M_CheckParm("-solo-net") || M_CheckParm("-net1");
} else {
// Get game info from server
packet_header_t *packet = Z_Malloc(1000, PU_STATIC, NULL);
struct setup_packet_s *sinfo = (void*)(packet+1);
struct { packet_header_t head; short pn; } PACKEDATTR initpacket;
I_InitNetwork();
udp_socket = I_Socket(0);
I_ConnectToServer(myargv[i]);
do
{
do {
// Send init packet
initpacket.pn = doom_htons(wanted_player_number);
packet_set(&initpacket.head, PKT_INIT, 0);
I_SendPacket(&initpacket.head, sizeof(initpacket));
I_WaitForPacket(5000);
} while (!I_GetPacket(packet, 1000));
if (packet->type == PKT_DOWN) I_Error("Server aborted the game");
} while (packet->type != PKT_SETUP);
// Once we have been accepted by the server, we should tell it when we leave
atexit(D_QuitNetGame);
// Get info from the setup packet
consoleplayer = sinfo->yourplayer;
compatibility_level = sinfo->complevel;
G_Compatibility();
startskill = sinfo->skill;
deathmatch = sinfo->deathmatch;
startmap = sinfo->level;
startepisode = sinfo->episode;
ticdup = sinfo->ticdup;
xtratics = sinfo->extratic;
G_ReadOptions(sinfo->game_options);
lprintf(LO_INFO, "\tjoined game as player %d/%d; %d WADs specified\n",
consoleplayer+1, numplayers = sinfo->players, sinfo->numwads);
{
char *p = sinfo->wadnames;
int i = sinfo->numwads;
while (i--) {
D_AddFile(p, source_net);
p += strlen(p) + 1;
}
}
Z_Free(packet);
}
localcmds = netcmds[displayplayer = consoleplayer];
for (i=0; i<numplayers; i++)
playeringame[i] = true;
for (; i<MAXPLAYERS; i++)
playeringame[i] = false;
if (!playeringame[consoleplayer]) I_Error("D_InitNetGame: consoleplayer not in game");
}
#else
void D_InitNetGame (void)
{
int i;
doomcom = Z_Malloc(sizeof *doomcom, PU_STATIC, NULL);
doomcom->consoleplayer = 0;
doomcom->numnodes = 0; doomcom->numplayers = 1;
localcmds = netcmds[consoleplayer];
netgame = (M_CheckParm("-solo-net") != 0) || (M_CheckParm("-net1") != 0);
for (i=0; i<doomcom->numplayers; i++)
playeringame[i] = true;
for (; i<MAXPLAYERS; i++)
playeringame[i] = false;
consoleplayer = displayplayer = doomcom->consoleplayer;
}
#endif // HAVE_NET
#ifdef HAVE_NET
void D_CheckNetGame(void)
{
packet_header_t *packet = Z_Malloc(sizeof(packet_header_t)+1, PU_STATIC, NULL);
if (server) {
lprintf(LO_INFO, "D_CheckNetGame: waiting for server to signal game start\n");
do {
while (!I_GetPacket(packet, sizeof(packet_header_t)+1)) {
packet_set(packet, PKT_GO, 0);
*(byte*)(packet+1) = consoleplayer;
I_SendPacket(packet, sizeof(packet_header_t)+1);
I_uSleep(100000);
}
} while (packet->type != PKT_GO);
}
Z_Free(packet);
}
boolean D_NetGetWad(const char* name)
{
#if defined(HAVE_WAIT_H)
size_t psize = sizeof(packet_header_t) + strlen(name) + 500;
packet_header_t *packet;
boolean done = false;
if (!server || strchr(name, '/')) return false; // If it contains path info, reject
do {
// Send WAD request to remote
packet = Z_Malloc(psize, PU_STATIC, NULL);
packet_set(packet, PKT_WAD, 0);
*(byte*)(packet+1) = consoleplayer;
strcpy(1+(byte*)(packet+1), name);
I_SendPacket(packet, sizeof(packet_header_t) + strlen(name) + 2);
I_uSleep(10000);
} while (!I_GetPacket(packet, psize) || (packet->type != PKT_WAD));
Z_Free(packet);
if (!strcasecmp((void*)(packet+1), name)) {
pid_t pid;
int rv;
byte *p = (byte*)(packet+1) + strlen(name) + 1;
/* Automatic wad file retrieval using wget (supports http and ftp, using URLs)
* Unix systems have all these commands handy, this kind of thing is easy
* Any windo$e port will have some awkward work replacing these.
*/
/* cph - caution here. This is data from an untrusted source.
* Don't pass it via a shell. */
if ((pid = fork()) == -1)
perror("fork");
else if (!pid) {
/* Child chains to wget, does the download */
execlp("wget", "wget", p, NULL);
}
/* This is the parent, i.e. main LxDoom process */
wait(&rv);
if (!(done = !access(name, R_OK))) {
if (!strcmp(p+strlen(p)-4, ".zip")) {
p = strrchr(p, '/')+1;
if ((pid = fork()) == -1)
perror("fork");
else if (!pid) {
/* Child executes decompressor */
execlp("unzip", "unzip", p, name, NULL);
}
/* Parent waits for the file */
wait(&rv);
done = !!access(name, R_OK);
}
/* Add more decompression protocols here as desired */
}
Z_Free(buffer);
}
return done;
#else /* HAVE_WAIT_H */
return false;
#endif
}
void NetUpdate(void)
{
static int lastmadetic;
if (isExtraDDisplay)
return;
if (server) { // Receive network packets
size_t recvlen;
packet_header_t *packet = Z_Malloc(10000, PU_STATIC, NULL);
while ((recvlen = I_GetPacket(packet, 10000))) {
switch(packet->type) {
case PKT_TICS:
{
byte *p = (void*)(packet+1);
int tics = *p++;
unsigned long ptic = doom_ntohl(packet->tic);
if (ptic > (unsigned)remotetic) { // Missed some
packet_set(packet, PKT_RETRANS, remotetic);
*(byte*)(packet+1) = consoleplayer;
I_SendPacket(packet, sizeof(*packet)+1);
} else {
if (ptic + tics <= (unsigned)remotetic) break; // Will not improve things
remotetic = ptic;
while (tics--) {
int players = *p++;
while (players--) {
int n = *p++;
RawToTic(&netcmds[n][remotetic%BACKUPTICS], p);
p += sizeof(ticcmd_t);
}
remotetic++;
}
}
}
break;
case PKT_RETRANS: // Resend request
remotesend = doom_ntohl(packet->tic);
break;
case PKT_DOWN: // Server downed
{
int j;
for (j=0; j<MAXPLAYERS; j++)
if (j != consoleplayer) playeringame[j] = false;
server = false;
doom_printf("Server is down\nAll other players are no longer in the game\n");
}
break;
case PKT_EXTRA: // Misc stuff
case PKT_QUIT: // Player quit
// Queue packet to be processed when its tic time is reached
queuedpacket = Z_Realloc(queuedpacket, ++numqueuedpackets * sizeof *queuedpacket,
PU_STATIC, NULL);
queuedpacket[numqueuedpackets-1] = Z_Malloc(recvlen, PU_STATIC, NULL);
memcpy(queuedpacket[numqueuedpackets-1], packet, recvlen);
break;
case PKT_BACKOFF:
/* cph 2003-09-18 -
* The server sends this when we have got ahead of the other clients. We should
* stall the input side on this client, to allow other clients to catch up.
*/
lastmadetic++;
break;
default: // Other packet, unrecognised or redundant
break;
}
}
Z_Free(packet);
}
{ // Build new ticcmds
int newtics = I_GetTime() - lastmadetic;
newtics = (newtics > 0 ? newtics : 0);
lastmadetic += newtics;
if (ffmap) newtics++;
while (newtics--) {
I_StartTic();
if (maketic - gametic > BACKUPTICS/2) break;
G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]);
maketic++;
}
if (server && maketic > remotesend) { // Send the tics to the server
int sendtics;
remotesend -= xtratics;
if (remotesend < 0) remotesend = 0;
sendtics = maketic - remotesend;
{
size_t pkt_size = sizeof(packet_header_t) + 2 + sendtics * sizeof(ticcmd_t);
packet_header_t *packet = Z_Malloc(pkt_size, PU_STATIC, NULL);
packet_set(packet, PKT_TICC, maketic - sendtics);
*(byte*)(packet+1) = sendtics;
*(((byte*)(packet+1))+1) = consoleplayer;
{
void *tic = ((byte*)(packet+1)) +2;
while (sendtics--) {
TicToRaw(tic, &localcmds[remotesend++%BACKUPTICS]);
tic = (byte *)tic + sizeof(ticcmd_t);
}
}
I_SendPacket(packet, pkt_size);
Z_Free(packet);
}
}
}
}
#else
void D_BuildNewTiccmds(void)
{
static int lastmadetic;
int newtics = I_GetTime() - lastmadetic;
lastmadetic += newtics;
while (newtics--)
{
I_StartTic();
if (maketic - gametic > BACKUPTICS/2) break;
G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]);
maketic++;
}
}
#endif
#ifdef HAVE_NET
/* cph - data passed to this must be in the Doom (little-) endian */
void D_NetSendMisc(netmisctype_t type, size_t len, void* data)
{
if (server) {
size_t size = sizeof(packet_header_t) + 3*sizeof(int) + len;
packet_header_t *packet = Z_Malloc(size, PU_STATIC, NULL);
int *p = (void*)(packet+1);
packet_set(packet, PKT_EXTRA, gametic);
*p++ = LONG(type); *p++ = LONG(consoleplayer); *p++ = LONG(len);
memcpy(p, data, len);
I_SendPacket(packet, size);
Z_Free(packet);
}
}
static void CheckQueuedPackets(void)
{
int i;
for (i=0; (unsigned)i<numqueuedpackets; i++)
if (doom_ntohl(queuedpacket[i]->tic) <= gametic)
switch (queuedpacket[i]->type) {
case PKT_QUIT: // Player quit the game
{
int pn = *(byte*)(queuedpacket[i]+1);
playeringame[pn] = false;
doom_printf("Player %d left the game\n", pn);
}
break;
case PKT_EXTRA:
{
int *p = (int*)(queuedpacket[i]+1);
size_t len = LONG(*(p+2));
switch (LONG(*p)) {
case nm_plcolour:
G_ChangedPlayerColour(LONG(*(p+1)), LONG(*(p+3)));
break;
case nm_savegamename:
if (len < SAVEDESCLEN) {
memcpy(savedescription, p+3, len);
// Force terminating 0 in case
savedescription[len] = 0;
}
break;
}
}
break;
default: // Should not be queued
break;
}
{ // Requeue remaining packets
int newnum = 0;
packet_header_t **newqueue = NULL;
for (i=0; (unsigned)i<numqueuedpackets; i++)
if (doom_ntohl(queuedpacket[i]->tic) > gametic) {
newqueue = Z_Realloc(newqueue, ++newnum * sizeof *newqueue,
PU_STATIC, NULL);
newqueue[newnum-1] = queuedpacket[i];
} else Z_Free(queuedpacket[i]);
Z_Free(queuedpacket);
numqueuedpackets = newnum; queuedpacket = newqueue;
}
}
#endif // HAVE_NET
void TryRunTics (void)
{
int runtics;
int entertime = I_GetTime();
// Wait for tics to run
while (1) {
#ifdef HAVE_NET
NetUpdate();
#else
D_BuildNewTiccmds();
#endif
runtics = (server ? remotetic : maketic) - gametic;
if (!runtics) {
if (!movement_smooth) {
#ifdef HAVE_NET
if (server)
I_WaitForPacket(ms_to_next_tick);
else
#endif
I_uSleep(ms_to_next_tick*1000);
}
if (I_GetTime() - entertime > 10) {
#ifdef HAVE_NET
if (server) {
char buf[sizeof(packet_header_t)+1];
remotesend--;
packet_set((packet_header_t *)buf, PKT_RETRANS, remotetic);
buf[sizeof(buf)-1] = consoleplayer;
I_SendPacket((packet_header_t *)buf, sizeof buf);
}
#endif
M_Ticker(); return;
}
//if ((displaytime) < (tic_vars.next-SDL_GetTicks()))
{
WasRenderedInTryRunTics = true;
if (V_GetMode() == VID_MODEGL ?
movement_smooth :
movement_smooth && gamestate==wipegamestate)
{
isExtraDDisplay = true;
D_Display();
isExtraDDisplay = false;
}
}
} else break;
}
while (runtics--) {
#ifdef HAVE_NET
if (server) CheckQueuedPackets();
#endif
if (advancedemo)
D_DoAdvanceDemo ();
M_Ticker ();
I_GetTime_SaveMS();
G_Ticker ();
P_Checksum(gametic);
gametic++;
#ifdef HAVE_NET
NetUpdate(); // Keep sending our tics to avoid stalling remote nodes
#endif
}
}
#ifdef HAVE_NET
static void D_QuitNetGame (void)
{
byte buf[1 + sizeof(packet_header_t)];
packet_header_t *packet = (void*)buf;
int i;
if (!server) return;
buf[sizeof(packet_header_t)] = consoleplayer;
packet_set(packet, PKT_QUIT, gametic);
for (i=0; i<4; i++) {
I_SendPacket(packet, 1 + sizeof(packet_header_t));
I_uSleep(10000);
}
}
#endif
+3111
View File
File diff suppressed because it is too large Load Diff
+1118
View File
File diff suppressed because it is too large Load Diff
+707
View File
@@ -0,0 +1,707 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Printed strings for translation.
* English language support (default).
* See dstrings.h for suggestions about foreign language BEX support
*
*-----------------------------------------------------------------------------*/
#ifndef __D_ENGLSH__
#define __D_ENGLSH__
/* d_main.c */
#define D_DEVSTR "Development mode ON.\n"
#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n"
/* m_menu.c */
#define PRESSKEY "press a key."
#define PRESSYN "press y or n."
#define QUITMSG "are you sure you want to\nquit this great game?"
#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY
#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY
#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY
#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY
#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN
#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n"PRESSYN
#define NEWGAME \
"you can't start a new game\n"\
"while in a network game.\n\n"PRESSKEY
#define NIGHTMARE \
"are you sure? this skill level\n"\
"isn't even remotely fair.\n\n"PRESSYN
#define SWSTRING \
"this is the shareware version of doom.\n\n"\
"you need to order the entire trilogy.\n\n"PRESSKEY
#define MSGOFF "Messages OFF"
#define MSGON "Messages ON"
#define NETEND "you can't end a netgame!\n\n"PRESSKEY
#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN
#define RESTARTLEVEL "restart the level?\n\n"PRESSYN
#define DOSY "(press y to quit)"
#define DETAILHI "High detail"
#define DETAILLO "Low detail"
#define GAMMALVL0 "Gamma correction OFF"
#define GAMMALVL1 "Gamma correction level 1"
#define GAMMALVL2 "Gamma correction level 2"
#define GAMMALVL3 "Gamma correction level 3"
#define GAMMALVL4 "Gamma correction level 4"
#define EMPTYSTRING "empty slot"
/* p_inter.c */
#define GOTARMOR "Picked up the armor."
#define GOTMEGA "Picked up the MegaArmor!"
#define GOTHTHBONUS "Picked up a health bonus."
#define GOTARMBONUS "Picked up an armor bonus."
#define GOTSTIM "Picked up a stimpack."
#define GOTMEDINEED "Picked up a medikit that you REALLY need!"
#define GOTMEDIKIT "Picked up a medikit."
#define GOTSUPER "Supercharge!"
#define GOTBLUECARD "Picked up a blue keycard."
#define GOTYELWCARD "Picked up a yellow keycard."
#define GOTREDCARD "Picked up a red keycard."
#define GOTBLUESKUL "Picked up a blue skull key."
#define GOTYELWSKUL "Picked up a yellow skull key."
#define GOTREDSKULL "Picked up a red skull key."
#define GOTINVUL "Invulnerability!"
#define GOTBERSERK "Berserk!"
#define GOTINVIS "Partial Invisibility"
#define GOTSUIT "Radiation Shielding Suit"
#define GOTMAP "Computer Area Map"
#define GOTVISOR "Light Amplification Visor"
#define GOTMSPHERE "MegaSphere!"
#define GOTCLIP "Picked up a clip."
#define GOTCLIPBOX "Picked up a box of bullets."
#define GOTROCKET "Picked up a rocket."
#define GOTROCKBOX "Picked up a box of rockets."
#define GOTCELL "Picked up an energy cell."
#define GOTCELLBOX "Picked up an energy cell pack."
#define GOTSHELLS "Picked up shotgun shells." // JDC: removed number 4 so baby mode isn't incorrect
#define GOTSHELLBOX "Picked up a box of shotgun shells."
#define GOTBACKPACK "Picked up a backpack full of ammo!"
#define GOTBFG9000 "You got the BFG9000! Oh, yes."
#define GOTCHAINGUN "You got the chaingun!"
#define GOTCHAINSAW "A chainsaw! Find some meat!"
#define GOTLAUNCHER "You got the rocket launcher!"
#define GOTPLASMA "You got the plasma gun!"
#define GOTSHOTGUN "You got the shotgun!"
#define GOTSHOTGUN2 "You got the super shotgun!"
/* p_doors.c */ // JDC: added punctuation to these
#define PD_BLUEO "You need a blue key to activate this object."
#define PD_REDO "You need a red key to activate this object."
#define PD_YELLOWO "You need a yellow key to activate this object."
#define PD_BLUEK "You need a blue key to open this door."
#define PD_REDK "You need a red key to open this door."
#define PD_YELLOWK "You need a yellow key to open this door."
/* jff 02/05/98 Create messages specific to card and skull keys */
#define PD_BLUEC "You need a blue card to open this door."
#define PD_REDC "You need a red card to open this door."
#define PD_YELLOWC "You need a yellow card to open this door."
#define PD_BLUES "You need a blue skull to open this door."
#define PD_REDS "You need a red skull to open this door."
#define PD_YELLOWS "You need a yellow skull to open this door."
#define PD_ANY "Any key will open this door."
#define PD_ALL3 "You need all three keys to open this door."
#define PD_ALL6 "You need all six keys to open this door."
/* g_game.c */
#define GGSAVED "Game saved." // JDC: capitalized per testing report
/* hu_stuff.c */
#define HUSTR_MSGU "[Message unsent]"
#define HUSTR_E1M1 "E1M1: Hangar"
#define HUSTR_E1M2 "E1M2: Nuclear Plant"
#define HUSTR_E1M3 "E1M3: Toxin Refinery"
#define HUSTR_E1M4 "E1M4: Command Control"
#define HUSTR_E1M5 "E1M5: Phobos Lab"
#define HUSTR_E1M6 "E1M6: Central Processing"
#define HUSTR_E1M7 "E1M7: Computer Station"
#define HUSTR_E1M8 "E1M8: Phobos Anomaly"
#define HUSTR_E1M9 "E1M9: Military Base"
#define HUSTR_E2M1 "E2M1: Deimos Anomaly"
#define HUSTR_E2M2 "E2M2: Containment Area"
#define HUSTR_E2M3 "E2M3: Refinery"
#define HUSTR_E2M4 "E2M4: Deimos Lab"
#define HUSTR_E2M5 "E2M5: Command Center"
#define HUSTR_E2M6 "E2M6: Halls of the Damned"
#define HUSTR_E2M7 "E2M7: Spawning Vats"
#define HUSTR_E2M8 "E2M8: Tower of Babel"
#define HUSTR_E2M9 "E2M9: Fortress of Mystery"
#define HUSTR_E3M1 "E3M1: Hell Keep"
#define HUSTR_E3M2 "E3M2: Slough of Despair"
#define HUSTR_E3M3 "E3M3: Pandemonium"
#define HUSTR_E3M4 "E3M4: House of Pain"
#define HUSTR_E3M5 "E3M5: Unholy Cathedral"
#define HUSTR_E3M6 "E3M6: Mt. Erebus"
#define HUSTR_E3M7 "E3M7: Limbo"
#define HUSTR_E3M8 "E3M8: Dis"
#define HUSTR_E3M9 "E3M9: Warrens"
#define HUSTR_E4M1 "E4M1: Hell Beneath"
#define HUSTR_E4M2 "E4M2: Perfect Hatred"
#define HUSTR_E4M3 "E4M3: Sever The Wicked"
#define HUSTR_E4M4 "E4M4: Unruly Evil"
#define HUSTR_E4M5 "E4M5: They Will Repent"
#define HUSTR_E4M6 "E4M6: Against Thee Wickedly"
#define HUSTR_E4M7 "E4M7: And Hell Followed"
#define HUSTR_E4M8 "E4M8: Unto The Cruel"
#define HUSTR_E4M9 "E4M9: Fear"
#define HUSTR_1 "level 1: entryway"
#define HUSTR_2 "level 2: underhalls"
#define HUSTR_3 "level 3: the gantlet"
#define HUSTR_4 "level 4: the focus"
#define HUSTR_5 "level 5: the waste tunnels"
#define HUSTR_6 "level 6: the crusher"
#define HUSTR_7 "level 7: dead simple"
#define HUSTR_8 "level 8: tricks and traps"
#define HUSTR_9 "level 9: the pit"
#define HUSTR_10 "level 10: refueling base"
#define HUSTR_11 "level 11: 'o' of destruction!"
#define HUSTR_12 "level 12: the factory"
#define HUSTR_13 "level 13: downtown"
#define HUSTR_14 "level 14: the inmost dens"
#define HUSTR_15 "level 15: industrial zone"
#define HUSTR_16 "level 16: suburbs"
#define HUSTR_17 "level 17: tenements"
#define HUSTR_18 "level 18: the courtyard"
#define HUSTR_19 "level 19: the citadel"
#define HUSTR_20 "level 20: gotcha!"
#define HUSTR_21 "level 21: nirvana"
#define HUSTR_22 "level 22: the catacombs"
#define HUSTR_23 "level 23: barrels o' fun"
#define HUSTR_24 "level 24: the chasm"
#define HUSTR_25 "level 25: bloodfalls"
#define HUSTR_26 "level 26: the abandoned mines"
#define HUSTR_27 "level 27: monster condo"
#define HUSTR_28 "level 28: the spirit world"
#define HUSTR_29 "level 29: the living end"
#define HUSTR_30 "level 30: icon of sin"
#define HUSTR_31 "level 31: wolfenstein"
#define HUSTR_32 "level 32: grosse"
#define PHUSTR_1 "level 1: congo"
#define PHUSTR_2 "level 2: well of souls"
#define PHUSTR_3 "level 3: aztec"
#define PHUSTR_4 "level 4: caged"
#define PHUSTR_5 "level 5: ghost town"
#define PHUSTR_6 "level 6: baron's lair"
#define PHUSTR_7 "level 7: caughtyard"
#define PHUSTR_8 "level 8: realm"
#define PHUSTR_9 "level 9: abattoire"
#define PHUSTR_10 "level 10: onslaught"
#define PHUSTR_11 "level 11: hunted"
#define PHUSTR_12 "level 12: speed"
#define PHUSTR_13 "level 13: the crypt"
#define PHUSTR_14 "level 14: genesis"
#define PHUSTR_15 "level 15: the twilight"
#define PHUSTR_16 "level 16: the omen"
#define PHUSTR_17 "level 17: compound"
#define PHUSTR_18 "level 18: neurosphere"
#define PHUSTR_19 "level 19: nme"
#define PHUSTR_20 "level 20: the death domain"
#define PHUSTR_21 "level 21: slayer"
#define PHUSTR_22 "level 22: impossible mission"
#define PHUSTR_23 "level 23: tombstone"
#define PHUSTR_24 "level 24: the final frontier"
#define PHUSTR_25 "level 25: the temple of darkness"
#define PHUSTR_26 "level 26: bunker"
#define PHUSTR_27 "level 27: anti-christ"
#define PHUSTR_28 "level 28: the sewers"
#define PHUSTR_29 "level 29: odyssey of noises"
#define PHUSTR_30 "level 30: the gateway of hell"
#define PHUSTR_31 "level 31: cyberden"
#define PHUSTR_32 "level 32: go 2 it"
#define THUSTR_1 "level 1: system control"
#define THUSTR_2 "level 2: human bbq"
#define THUSTR_3 "level 3: power control"
#define THUSTR_4 "level 4: wormhole"
#define THUSTR_5 "level 5: hanger"
#define THUSTR_6 "level 6: open season"
#define THUSTR_7 "level 7: prison"
#define THUSTR_8 "level 8: metal"
#define THUSTR_9 "level 9: stronghold"
#define THUSTR_10 "level 10: redemption"
#define THUSTR_11 "level 11: storage facility"
#define THUSTR_12 "level 12: crater"
#define THUSTR_13 "level 13: nukage processing"
#define THUSTR_14 "level 14: steel works"
#define THUSTR_15 "level 15: dead zone"
#define THUSTR_16 "level 16: deepest reaches"
#define THUSTR_17 "level 17: processing area"
#define THUSTR_18 "level 18: mill"
#define THUSTR_19 "level 19: shipping/respawning"
#define THUSTR_20 "level 20: central processing"
#define THUSTR_21 "level 21: administration center"
#define THUSTR_22 "level 22: habitat"
#define THUSTR_23 "level 23: lunar mining project"
#define THUSTR_24 "level 24: quarry"
#define THUSTR_25 "level 25: baron's den"
#define THUSTR_26 "level 26: ballistyx"
#define THUSTR_27 "level 27: mount pain"
#define THUSTR_28 "level 28: heck"
#define THUSTR_29 "level 29: river styx"
#define THUSTR_30 "level 30: last call"
#define THUSTR_31 "level 31: pharaoh"
#define THUSTR_32 "level 32: caribbean"
#define HUSTR_CHATMACRO1 "I'm ready to kick butt!"
#define HUSTR_CHATMACRO2 "I'm OK."
#define HUSTR_CHATMACRO3 "I'm not looking too good!"
#define HUSTR_CHATMACRO4 "Help!"
#define HUSTR_CHATMACRO5 "You suck!"
#define HUSTR_CHATMACRO6 "Next time, scumbag..."
#define HUSTR_CHATMACRO7 "Come here!"
#define HUSTR_CHATMACRO8 "I'll take care of it."
#define HUSTR_CHATMACRO9 "Yes"
#define HUSTR_CHATMACRO0 "No"
#define HUSTR_TALKTOSELF1 "You mumble to yourself"
#define HUSTR_TALKTOSELF2 "Who's there?"
#define HUSTR_TALKTOSELF3 "You scare yourself"
#define HUSTR_TALKTOSELF4 "You start to rave"
#define HUSTR_TALKTOSELF5 "You've lost it..."
#define HUSTR_MESSAGESENT "[Message Sent]"
/* The following should NOT be changed unless it seems
* just AWFULLY necessary */
#define HUSTR_PLRGREEN "Player 1: "
#define HUSTR_PLRINDIGO "Player 2: "
#define HUSTR_PLRBROWN "Player 3: "
#define HUSTR_PLRRED "Player 4: "
#define HUSTR_KEYGREEN 'g'
#define HUSTR_KEYINDIGO 'i'
#define HUSTR_KEYBROWN 'b'
#define HUSTR_KEYRED 'r'
/* am_map.c */
#define AMSTR_FOLLOWON "Follow Mode ON"
#define AMSTR_FOLLOWOFF "Follow Mode OFF"
#define AMSTR_GRIDON "Grid ON"
#define AMSTR_GRIDOFF "Grid OFF"
#define AMSTR_MARKEDSPOT "Marked Spot"
#define AMSTR_MARKSCLEARED "All Marks Cleared"
#define AMSTR_ROTATEON "Rotate Mode ON"
#define AMSTR_ROTATEOFF "Rotate Mode OFF"
#define AMSTR_OVERLAYON "Overlay Mode ON"
#define AMSTR_OVERLAYOFF "Overlay Mode OFF"
/* st_stuff.c */
#define STSTR_MUS "Music Change"
#define STSTR_NOMUS "IMPOSSIBLE SELECTION"
#define STSTR_DQDON "Degreelessness Mode On"
#define STSTR_DQDOFF "Degreelessness Mode Off"
#define STSTR_KFAADDED "Very Happy Ammo Added"
#define STSTR_FAADDED "Ammo (no keys) Added"
#define STSTR_NCON "No Clipping Mode ON"
#define STSTR_NCOFF "No Clipping Mode OFF"
#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp"
#define STSTR_BEHOLDX "Power-up Toggled"
#define STSTR_CHOPPERS "... doesn't suck - GM"
#define STSTR_CLEV "Changing Level..."
#define STSTR_COMPON "Compatibility Mode On" /* phares */
#define STSTR_COMPOFF "Compatibility Mode Off" /* phares */
/* f_finale.c */
#define E1TEXT \
"Once you beat the big badasses and\n"\
"clean out the moon base you're supposed\n"\
"to win, aren't you? Aren't you? Where's\n"\
"your fat reward and ticket home? What\n"\
"the hell is this? It's not supposed to\n"\
"end this way!\n"\
"\n" \
"It stinks like rotten meat, but looks\n"\
"like the lost Deimos base. Looks like\n"\
"you're stuck on The Shores of Hell.\n"\
"The only way out is through.\n"\
"\n"\
"To continue the DOOM experience, play\n"\
"The Shores of Hell and its amazing\n"\
"sequel, Inferno!\n"
#define E2TEXT \
"You've done it! The hideous cyber-\n"\
"demon lord that ruled the lost Deimos\n"\
"moon base has been slain and you\n"\
"are triumphant! But ... where are\n"\
"you? You clamber to the edge of the\n"\
"moon and look down to see the awful\n"\
"truth.\n" \
"\n"\
"Deimos floats above Hell itself!\n"\
"You've never heard of anyone escaping\n"\
"from Hell, but you'll make the bastards\n"\
"sorry they ever heard of you! Quickly,\n"\
"you rappel down to the surface of\n"\
"Hell.\n"\
"\n" \
"Now, it's on to the final chapter of\n"\
"DOOM! -- Inferno."
#define E3TEXT \
"The loathsome spiderdemon that\n"\
"masterminded the invasion of the moon\n"\
"bases and caused so much death has had\n"\
"its ass kicked for all time.\n"\
"\n"\
"A hidden doorway opens and you enter.\n"\
"You've proven too tough for Hell to\n"\
"contain, and now Hell at last plays\n"\
"fair -- for you emerge from the door\n"\
"to see the green fields of Earth!\n"\
"Home at last.\n" \
"\n"\
"You wonder what's been happening on\n"\
"Earth while you were battling evil\n"\
"unleashed. It's good that no Hell-\n"\
"spawn could have come through that\n"\
"door with you ..."
#define E4TEXT \
"the spider mastermind must have sent forth\n"\
"its legions of hellspawn before your\n"\
"final confrontation with that terrible\n"\
"beast from hell. but you stepped forward\n"\
"and brought forth eternal damnation and\n"\
"suffering upon the horde as a true hero\n"\
"would in the face of something so evil.\n"\
"\n"\
"besides, someone was gonna pay for what\n"\
"happened to daisy, your pet rabbit.\n"\
"\n"\
"but now, you see spread before you more\n"\
"potential pain and gibbitude as a nation\n"\
"of demons run amok among our cities.\n"\
"\n"\
"next stop, hell on earth!"
/* after level 6, put this: */
#define C1TEXT \
"YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \
"STARPORT. BUT SOMETHING IS WRONG. THE\n" \
"MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \
"WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \
"IS BEING SUBVERTED BY THEIR PRESENCE.\n" \
"\n"\
"AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \
"FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \
"YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \
"OF THE STARBASE AND FIND THE CONTROLLING\n" \
"SWITCH WHICH HOLDS EARTH'S POPULATION\n" \
"HOSTAGE."
/* After level 11, put this: */
#define C2TEXT \
"YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \
"HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"\
"THE NIGHTMARE. NOW YOU ARE THE ONLY\n"\
"HUMAN LEFT ON THE FACE OF THE PLANET.\n"\
"CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"\
"AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"\
"YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"\
"THAT YOU HAVE SAVED YOUR SPECIES.\n"\
"\n"\
"BUT THEN, EARTH CONTROL BEAMS DOWN A\n"\
"MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"\
"THE SOURCE OF THE ALIEN INVASION. IF YOU\n"\
"GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"\
"ENTRY. THE ALIEN BASE IS IN THE HEART OF\n"\
"YOUR OWN HOME CITY, NOT FAR FROM THE\n"\
"STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"\
"UP AND RETURN TO THE FRAY."
/* After level 20, put this: */
#define C3TEXT \
"YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"\
"SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"\
"YOU SEE NO WAY TO DESTROY THE CREATURES'\n"\
"ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"\
"TEETH AND PLUNGE THROUGH IT.\n"\
"\n"\
"THERE MUST BE A WAY TO CLOSE IT ON THE\n"\
"OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"\
"GOT TO GO THROUGH HELL TO GET TO IT?"
/* After level 29, put this: */
#define C4TEXT \
"THE HORRENDOUS VISAGE OF THE BIGGEST\n"\
"DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"\
"YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"\
"HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"\
"UP AND DIES, ITS THRASHING LIMBS\n"\
"DEVASTATING UNTOLD MILES OF HELL'S\n"\
"SURFACE.\n"\
"\n"\
"YOU'VE DONE IT. THE INVASION IS OVER.\n"\
"EARTH IS SAVED. HELL IS A WRECK. YOU\n"\
"WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"\
"DIE, NOW. WIPING THE SWEAT FROM YOUR\n"\
"FOREHEAD YOU BEGIN THE LONG TREK BACK\n"\
"HOME. REBUILDING EARTH OUGHT TO BE A\n"\
"LOT MORE FUN THAN RUINING IT WAS.\n"
/* Before level 31, put this: */
#define C5TEXT \
"CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"\
"LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"\
"HUMANS, RATHER THAN DEMONS. YOU WONDER\n"\
"WHO THE INMATES OF THIS CORNER OF HELL\n"\
"WILL BE."
/* Before level 32, put this: */
#define C6TEXT \
"CONGRATULATIONS, YOU'VE FOUND THE\n"\
"SUPER SECRET LEVEL! YOU'D BETTER\n"\
"BLAZE THROUGH THIS ONE!\n"
/*** Plutonia ***/
/* after map 06 */
#define P1TEXT \
"You gloat over the steaming carcass of the\n"\
"Guardian. With its death, you've wrested\n"\
"the Accelerator from the stinking claws\n"\
"of Hell. You relax and glance around the\n"\
"room. Damn! There was supposed to be at\n"\
"least one working prototype, but you can't\n"\
"see it. The demons must have taken it.\n"\
"\n"\
"You must find the prototype, or all your\n"\
"struggles will have been wasted. Keep\n"\
"moving, keep fighting, keep killing.\n"\
"Oh yes, keep living, too."
/* after map 11 */
#define P2TEXT \
"Even the deadly Arch-Vile labyrinth could\n"\
"not stop you, and you've gotten to the\n"\
"prototype Accelerator which is soon\n"\
"efficiently and permanently deactivated.\n"\
"\n"\
"You're good at that kind of thing."
/* after map 20 */
#define P3TEXT \
"You've bashed and battered your way into\n"\
"the heart of the devil-hive. Time for a\n"\
"Search-and-Destroy mission, aimed at the\n"\
"Gatekeeper, whose foul offspring is\n"\
"cascading to Earth. Yeah, he's bad. But\n"\
"you know who's worse!\n"\
"\n"\
"Grinning evilly, you check your gear, and\n"\
"get ready to give the bastard a little Hell\n"\
"of your own making!"
/* after map 30 */
#define P4TEXT \
"The Gatekeeper's evil face is splattered\n"\
"all over the place. As its tattered corpse\n"\
"collapses, an inverted Gate forms and\n"\
"sucks down the shards of the last\n"\
"prototype Accelerator, not to mention the\n"\
"few remaining demons. You're done. Hell\n"\
"has gone back to pounding bad dead folks \n"\
"instead of good live ones. Remember to\n"\
"tell your grandkids to put a rocket\n"\
"launcher in your coffin. If you go to Hell\n"\
"when you die, you'll need it for some\n"\
"final cleaning-up ..."
/* before map 31 */
#define P5TEXT \
"You've found the second-hardest level we\n"\
"got. Hope you have a saved game a level or\n"\
"two previous. If not, be prepared to die\n"\
"aplenty. For master marines only."
/* before map 32 */
#define P6TEXT \
"Betcha wondered just what WAS the hardest\n"\
"level we had ready for ya? Now you know.\n"\
"No one gets out alive."
/*** TNT: Evilution ***/
#define T1TEXT \
"You've fought your way out of the infested\n"\
"experimental labs. It seems that UAC has\n"\
"once again gulped it down. With their\n"\
"high turnover, it must be hard for poor\n"\
"old UAC to buy corporate health insurance\n"\
"nowadays..\n"\
"\n"\
"Ahead lies the military complex, now\n"\
"swarming with diseased horrors hot to get\n"\
"their teeth into you. With luck, the\n"\
"complex still has some warlike ordnance\n"\
"laying around."
#define T2TEXT \
"You hear the grinding of heavy machinery\n"\
"ahead. You sure hope they're not stamping\n"\
"out new hellspawn, but you're ready to\n"\
"ream out a whole herd if you have to.\n"\
"They might be planning a blood feast, but\n"\
"you feel about as mean as two thousand\n"\
"maniacs packed into one mad killer.\n"\
"\n"\
"You don't plan to go down easy."
#define T3TEXT \
"The vista opening ahead looks real damn\n"\
"familiar. Smells familiar, too -- like\n"\
"fried excrement. You didn't like this\n"\
"place before, and you sure as hell ain't\n"\
"planning to like it now. The more you\n"\
"brood on it, the madder you get.\n"\
"Hefting your gun, an evil grin trickles\n"\
"onto your face. Time to take some names."
#define T4TEXT \
"Suddenly, all is silent, from one horizon\n"\
"to the other. The agonizing echo of Hell\n"\
"fades away, the nightmare sky turns to\n"\
"blue, the heaps of monster corpses start \n"\
"to evaporate along with the evil stench \n"\
"that filled the air. Jeeze, maybe you've\n"\
"done it. Have you really won?\n"\
"\n"\
"Something rumbles in the distance.\n"\
"A blue light begins to glow inside the\n"\
"ruined skull of the demon-spitter."
#define T5TEXT \
"What now? Looks totally different. Kind\n"\
"of like King Tut's condo. Well,\n"\
"whatever's here can't be any worse\n"\
"than usual. Can it? Or maybe it's best\n"\
"to let sleeping gods lie.."
#define T6TEXT \
"Time for a vacation. You've burst the\n"\
"bowels of hell and by golly you're ready\n"\
"for a break. You mutter to yourself,\n"\
"Maybe someone else can kick Hell's ass\n"\
"next time around. Ahead lies a quiet town,\n"\
"with peaceful flowing water, quaint\n"\
"buildings, and presumably no Hellspawn.\n"\
"\n"\
"As you step off the transport, you hear\n"\
"the stomp of a cyberdemon's iron shoe."
/*
* Character cast strings F_FINALE.C
*/
#define CC_ZOMBIE "ZOMBIEMAN"
#define CC_SHOTGUN "SHOTGUN GUY"
#define CC_HEAVY "HEAVY WEAPON DUDE"
#define CC_IMP "IMP"
#define CC_DEMON "DEMON"
#define CC_LOST "LOST SOUL"
#define CC_CACO "CACODEMON"
#define CC_HELL "HELL KNIGHT"
#define CC_BARON "BARON OF HELL"
#define CC_ARACH "ARACHNOTRON"
#define CC_PAIN "PAIN ELEMENTAL"
#define CC_REVEN "REVENANT"
#define CC_MANCU "MANCUBUS"
#define CC_ARCH "ARCH-VILE"
#define CC_SPIDER "THE SPIDER MASTERMIND"
#define CC_CYBER "THE CYBERDEMON"
#define CC_HERO "OUR HERO"
#endif
+125
View File
@@ -0,0 +1,125 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Event information structures.
*
*-----------------------------------------------------------------------------*/
#ifndef __D_EVENT__
#define __D_EVENT__
#include "doomtype.h"
//
// Event handling.
//
// Input event types.
typedef enum
{
ev_keydown,
ev_keyup,
ev_mouse,
ev_joystick
} evtype_t;
// Event structure.
typedef struct
{
evtype_t type;
int data1; // keys / mouse/joystick buttons
int data2; // mouse/joystick x move
int data3; // mouse/joystick y move
} event_t;
typedef enum
{
ga_nothing,
ga_loadlevel,
ga_newgame,
ga_loadgame,
ga_savegame,
ga_playdemo,
ga_completed,
ga_victory,
ga_worlddone,
} gameaction_t;
//
// Button/action code definitions.
//
typedef enum
{
// Press "Fire".
BT_ATTACK = 1,
// Use button, to open doors, activate switches.
BT_USE = 2,
// Flag: game events, not really buttons.
BT_SPECIAL = 128,
BT_SPECIALMASK = 3,
// Flag, weapon change pending.
// If true, the next 4 bits hold weapon num.
BT_CHANGE = 4,
// The 4bit weapon mask and shift, convenience.
//BT_WEAPONMASK = (8+16+32),
BT_WEAPONMASK = (8+16+32+64), // extended to pick up SSG // phares
BT_WEAPONSHIFT = 3,
// Special events
BTS_LOADGAME = 0, // Loads a game
// Pause the game.
BTS_PAUSE = 1,
// Save the game at each console.
BTS_SAVEGAME = 2,
BTS_RESTARTLEVEL= 3, // Restarts the current level
// Savegame slot numbers occupy the second byte of buttons.
BTS_SAVEMASK = (4+8+16),
BTS_SAVESHIFT = 2,
} buttoncode_t;
//
// GLOBAL VARIABLES
//
extern gameaction_t gameaction;
#endif
+291
View File
@@ -0,0 +1,291 @@
#include <stdio.h>
#include <stdlib.h>
#include <netipx/ipx.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "protocol.h"
#define BACKUPTICS 12
#define NCMD_EXIT 0x80000000
#define NCMD_RETRANSMIT 0x40000000
#define NCMD_SETUP 0x20000000
#define NCMD_KILL 0x10000000 // kill game
#define NCMD_CHECKSUM 0x0fffffff
typedef struct
{
short gameid; // so multiple games can setup at once
short drone;
short nodesfound;
short nodeswanted;
} setupdata_t;
typedef struct
a
{
// High bit is retransmit request.
unsigned checksum;
// Only valid if NCMD_RETRANSMIT.
byte retransmitfrom;
byte starttic;
byte player;
byte numtics;
ticcmd_t cmds[BACKUPTICS];
} doomdata_t;
typedef struct
{
signed int tic;
union altu {
setupdata_t s;
unsigned char data[100];
doomdata_t d;
} u;
} ipxpacket_t;
int nodes;
unsigned short port = 0x869b;
int ipx_socket(void) {
int s = socket(PF_IPX,SOCK_DGRAM,0);
struct sockaddr_ipx sa;
if (s == -1) {
fprintf(stderr,"socket(PF_PIPX): %s\n",strerror(errno));
exit(-1);
}
memset(&sa,0,sizeof(sa));
memset(sa.sipx_node,0xff,sizeof(sa.sipx_node));
sa.sipx_port = htons(port);
if (bind(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) {
fprintf(stderr,"bind(%d): %s\n",port,strerror(errno));
exit(-1);
}
return s;
}
int udp_socket(const char* ip) {
struct sockaddr_in sa;
int s = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
if (s == -1) {
fprintf(stderr,"socket(PF_INET): %s\n", strerror(errno));
exit(-1);
}
sa.sin_family=PF_INET;
inet_aton(ip,&sa.sin_addr);
sa.sin_port = htons(5030);
if (connect(s,(struct sockaddr*)&sa,sizeof sa) == -1) {
fprintf(stderr,"connect(%s:%d): %s\n", ip, 5030, strerror(errno));
exit(-1);
}
return s;
}
static byte ChecksumPacket(const packet_header_t* buffer, size_t len)
{
const byte* p = (void*)buffer;
byte sum = 0;
if (len==0)
return 0;
while (p++, --len)
sum += *p;
return sum;
}
//
// Checksum
//
unsigned NetbufferChecksum (void* p, size_t l)
{
unsigned c;
c = 0x1234567;
l = l/4;
for (int i=0 ; i<l ; i++)
c += ((unsigned *)p)[i] * (i+1);
return c & NCMD_CHECKSUM;
}
int ipxs, udps;
int consoleplayer;
int basetic;
int ExpandTics (int low, int maketic)
{
int delta;
delta = low - (maketic&0xff);
if (delta >= -64 && delta <= 64)
return (maketic&~0xff) + low;
if (delta > 64)
return (maketic&~0xff) - 256 + low;
if (delta < -64)
return (maketic&~0xff) + 256 + low;
fprintf(stderr,"ExpandTics strange value %i at maketic %i\n",low,maketic);
exit(-2);
}
void send_udp_packet(enum packet_type_e type, unsigned tic, void* data, size_t len) {
packet_header_t* p = calloc(sizeof(packet_header_t)+len+1,1);
p->tic = doom_htonl(basetic = tic); p->type = type;
if (!data) {
data = (void*)&consoleplayer; len = 1;
}
memcpy(((char*)p)+sizeof(*p),data,len);
p->checksum = ChecksumPacket(p,sizeof(packet_header_t)+len);
write(udps,p,sizeof(packet_header_t)+len+1);
}
int connected;
int ipxcounter;
void ipx_receive(int s) {
ipxpacket_t buf;
int rc;
struct sockaddr from;
size_t sl = sizeof(from);
rc = recvfrom(s,&buf,sizeof buf,0,&from,&sl);
if (rc == -1) {
fprintf(stderr,"read(ipx): %s\n", strerror(errno));
exit(-2);
}
if (rc > 0) {
if (buf.tic == -1) {
// Setup packet
if (!connected++) {
connect(s,&from,sl);
send_udp_packet(PKT_INIT,0,NULL,0);
}
} else {
if (buf.u.d.checksum & NCMD_SETUP) {
printf("setup packet, dropped\n");
} else if (buf.u.d.checksum & NCMD_EXIT) {
send_udp_packet(PKT_QUIT,buf.u.d.starttic,NULL,0);
exit(0);
} else if ((buf.u.d.checksum & NCMD_CHECKSUM) == buf.u.d.checksum) {
// No flags, normal game packet
char outbuf[100];
int tics;
outbuf[0] = tics = buf.u.d.numtics;
outbuf[1] = buf.u.d.player;
for (int i=0; i< tics; i++)
TicToRaw(outbuf+2+i*sizeof(ticcmd_t),&buf.u.d.cmds[i]);
send_udp_packet(PKT_TICC, ExpandTics(buf.u.d.starttic, basetic), outbuf, 2+tics*sizeof(ticcmd_t));
}
}
}
}
void udp_receive(int s) {
size_t len = 1024;
packet_header_t *p = malloc(len);
int rc;
rc = read(s,p,len);
if (rc < 0) {
fprintf(stderr,"read(udp): %s\n", strerror(errno));
exit(-2);
}
if (rc > 0) {
switch (p->type) {
case PKT_SETUP:
{
struct setup_packet_s *sinfo = (void*)(p+1);
consoleplayer = sinfo->yourplayer;
send_udp_packet(PKT_GO,0,NULL,0);
write(ipxs,"\xff\xff\xff\xff\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00",16);
}
break;
case PKT_GO:
{
ipxpacket_t pkt;
memset(&pkt,0,sizeof(pkt));
pkt.tic = ipxcounter++;
pkt.u.d.player = consoleplayer^1;
pkt.u.d.starttic = 0;
pkt.u.d.numtics = 0;
pkt.u.d.retransmitfrom = 0;
pkt.u.d.checksum = NetbufferChecksum(&pkt.u.d.retransmitfrom, 4);
write(ipxs,&pkt,16);
}
break;
case PKT_TICS:
{
ipxpacket_t pkt;
int tic = doom_ntohl(p->tic);
byte *pp = (void*)(p+1);
int tics = *pp++;
memset(&pkt,0,sizeof(pkt));
size_t len;
pkt.tic = ipxcounter++;
pkt.u.d.starttic = tic;
pkt.u.d.player = (consoleplayer == 0 ? 1 : 0);
pkt.u.d.numtics = tics;
for (int t=0; t<tics; t++) {
int players = *pp++;
for (int i=0; i<players; i++) {
if (*pp++ == pkt.u.d.player)
RawToTic(&pkt.u.d.cmds[t],pp);
pp += sizeof(ticcmd_t);
}
}
pkt.u.d.retransmitfrom = 0;
len = 12+tics*sizeof(ticcmd_t);
len = (len+7)&0xff8; // round up to next 16
pkt.u.d.checksum = NetbufferChecksum(&pkt.u.d.retransmitfrom, len-8);
write(ipxs,&pkt,len);
}
}
}
}
void loop(int ipxs, int udps) {
for (;;) {
struct timeval wt = {60,0};
fd_set fds;
int rc;
FD_ZERO(&fds);
FD_SET(ipxs,&fds);
FD_SET(udps,&fds);
rc = select(ipxs+udps,&fds,NULL,NULL,&wt);
if (rc < 0) {
fprintf(stderr,"select: %s\n", strerror(errno));
exit(-2);
}
if (rc>0) {
if (FD_ISSET(ipxs,&fds))
ipx_receive(ipxs);
if (FD_ISSET(udps,&fds))
udp_receive(udps);
}
}
}
int main(int argc, char**argv) {
ipxs = ipx_socket();
udps = udp_socket(argv[1]);
loop(ipxs,udps);
return 0;
}
+140
View File
@@ -0,0 +1,140 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Something to do with weapon sprite frames. Don't ask me.
*
*-----------------------------------------------------------------------------
*/
// We are referring to sprite numbers.
#include "doomtype.h"
#include "info.h"
#ifdef __GNUG__
#pragma implementation "d_items.h"
#endif
#include "d_items.h"
//
// PSPRITE ACTIONS for waepons.
// This struct controls the weapon animations.
//
// Each entry is:
// ammo/amunition type
// upstate
// downstate
// readystate
// atkstate, i.e. attack/fire/hit frame
// flashstate, muzzle flash
//
weaponinfo_t weaponinfo[NUMWEAPONS] =
{
{
// fist
am_noammo,
S_PUNCHUP,
S_PUNCHDOWN,
S_PUNCH,
S_PUNCH1,
S_NULL
},
{
// pistol
am_clip,
S_PISTOLUP,
S_PISTOLDOWN,
S_PISTOL,
S_PISTOL1,
S_PISTOLFLASH
},
{
// shotgun
am_shell,
S_SGUNUP,
S_SGUNDOWN,
S_SGUN,
S_SGUN1,
S_SGUNFLASH1
},
{
// chaingun
am_clip,
S_CHAINUP,
S_CHAINDOWN,
S_CHAIN,
S_CHAIN1,
S_CHAINFLASH1
},
{
// missile launcher
am_misl,
S_MISSILEUP,
S_MISSILEDOWN,
S_MISSILE,
S_MISSILE1,
S_MISSILEFLASH1
},
{
// plasma rifle
am_cell,
S_PLASMAUP,
S_PLASMADOWN,
S_PLASMA,
S_PLASMA1,
S_PLASMAFLASH1
},
{
// bfg 9000
am_cell,
S_BFGUP,
S_BFGDOWN,
S_BFG,
S_BFG1,
S_BFGFLASH1
},
{
// chainsaw
am_noammo,
S_SAWUP,
S_SAWDOWN,
S_SAW,
S_SAW1,
S_NULL
},
{
// super shotgun
am_shell,
S_DSGUNUP,
S_DSGUNDOWN,
S_DSGUN,
S_DSGUN1,
S_DSGUNFLASH1
},
};
+59
View File
@@ -0,0 +1,59 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Items: key cards, artifacts, weapon, ammunition.
*
*-----------------------------------------------------------------------------*/
#ifndef __D_ITEMS__
#define __D_ITEMS__
#include "doomdef.h"
#ifdef __GNUG__
#pragma interface
#endif
/* Weapon info: sprite frames, ammunition use. */
typedef struct
{
ammotype_t ammo;
int upstate;
int downstate;
int readystate;
int atkstate;
int flashstate;
} weaponinfo_t;
extern weaponinfo_t weaponinfo[NUMWEAPONS];
#endif
+1742
View File
File diff suppressed because it is too large Load Diff
+80
View File
@@ -0,0 +1,80 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Main startup and splash screenstuff.
*
*-----------------------------------------------------------------------------*/
#ifndef __D_MAIN__
#define __D_MAIN__
#include "d_event.h"
#include "w_wad.h"
#ifdef __GNUG__
#pragma interface
#endif
/* CPhipps - removed wadfiles[] stuff to w_wad.h */
extern char basesavegame[]; // killough 2/16/98: savegame path
//jff 1/24/98 make command line copies of play modes available
extern boolean clnomonsters; // checkparm of -nomonsters
extern boolean clrespawnparm; // checkparm of -respawn
extern boolean clfastparm; // checkparm of -fast
//jff end of external declaration of command line playmode
extern boolean nosfxparm;
extern boolean nomusicparm;
extern int ffmap;
// Called by IO functions when input is detected.
void D_PostEvent(event_t* ev);
// Demo stuff
extern boolean advancedemo;
void D_AdvanceDemo(void);
void D_DoAdvanceDemo (void);
//
// BASE LEVEL
//
void D_Display(void);
void D_PageTicker(void);
void D_StartTitle(void);
void D_DoomMain(void);
void D_AddFile (const char *file, wad_source_t source);
/* cph - MBF-like wad/deh/bex autoload code */
#define MAXLOADFILES 2
extern const char *wad_files[MAXLOADFILES], *deh_files[MAXLOADFILES];
#endif
+214
View File
@@ -0,0 +1,214 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Networking stuff.
*
*-----------------------------------------------------------------------------*/
#ifndef __D_NET__
#define __D_NET__
#include "d_player.h"
#ifdef __GNUG__
#pragma interface
#endif
//
// Network play related stuff.
// There is a data struct that stores network
// communication related stuff, and another
// one that defines the actual packets to
// be transmitted.
//
#define DOOMCOM_ID 0x12345678l
// Max computers/players in a game.
#define MAXNETNODES 8
typedef enum
{
CMD_SEND = 1,
CMD_GET = 2
} command_t;
//
// Network packet data.
//
typedef struct
{
// High bit is retransmit request.
unsigned checksum;
// Only valid if NCMD_RETRANSMIT.
byte retransmitfrom;
byte starttic;
byte player;
byte numtics;
ticcmd_t cmds[BACKUPTICS];
} doomdata_t;
//
// Startup packet difference
// SG: 4/12/98
// Added so we can send more startup data to synch things like
// bobbing, recoil, etc.
// this is just mapped over the ticcmd_t array when setup packet is sent
//
// Note: the original code takes care of startskill, deathmatch, nomonsters
// respawn, startepisode, startmap
// Note: for phase 1 we need to add monsters_remember, variable_friction,
// weapon_recoil, allow_pushers, over_under, player_bobbing,
// fastparm, demo_insurance, and the rngseed
//Stick all options into bytes so we don't need to mess with bitfields
//WARNING: make sure this doesn't exceed the size of the ticcmds area!
//sizeof(ticcmd_t)*BACKUPTICS
//This is the current length of our extra stuff
//
//killough 5/2/98: this should all be replaced by calls to G_WriteOptions()
//and G_ReadOptions(), which were specifically designed to set up packets.
//By creating a separate struct and functions to read/write the options,
//you now have two functions and data to maintain instead of just one.
//If the array in g_game.c which G_WriteOptions()/G_ReadOptions() operates
//on, is too large (more than sizeof(ticcmd_t)*BACKUPTICS), it can
//either be shortened, or the net code needs to divide it up
//automatically into packets. The STARTUPLEN below is non-portable.
//There's a portable way to do it without having to know the sizes.
#define STARTUPLEN 12
typedef struct
{
byte monsters_remember;
byte variable_friction;
byte weapon_recoil;
byte allow_pushers;
byte over_under;
byte player_bobbing;
byte fastparm;
byte demo_insurance;
unsigned long rngseed;
char filler[sizeof(ticcmd_t)*BACKUPTICS-STARTUPLEN];
} startup_t;
typedef enum {
// Leave space, so low values corresponding to normal netgame setup packets can be ignored
nm_plcolour = 3,
nm_savegamename = 4,
} netmisctype_t;
typedef struct
{
netmisctype_t type;
size_t len;
byte value[sizeof(ticcmd_t)*BACKUPTICS - sizeof(netmisctype_t) - sizeof(size_t)];
} netmisc_t;
typedef struct
{
// Supposed to be DOOMCOM_ID?
long id;
// DOOM executes an int to execute commands.
short intnum;
// Communication between DOOM and the driver.
// Is CMD_SEND or CMD_GET.
short command;
// Is dest for send, set by get (-1 = no packet).
short remotenode;
// Number of bytes in doomdata to be sent
short datalength;
// Info common to all nodes.
// Console is allways node 0.
short numnodes;
// Flag: 1 = no duplication, 2-5 = dup for slow nets.
short ticdup;
// Flag: 1 = send a backup tic in every packet.
short extratics;
// Flag: 1 = deathmatch.
short deathmatch;
// Flag: -1 = new game, 0-5 = load savegame
short savegame;
short episode; // 1-3
short map; // 1-9
short skill; // 1-5
// Info specific to this node.
short consoleplayer;
short numplayers;
// These are related to the 3-display mode,
// in which two drones looking left and right
// were used to render two additional views
// on two additional computers.
// Probably not operational anymore.
// 1 = left, 0 = center, -1 = right
short angleoffset;
// 1 = drone
short drone;
// The packet data to be sent.
doomdata_t data;
} doomcom_t;
// Create any new ticcmds and broadcast to other players.
#ifdef HAVE_NET
void NetUpdate (void);
#else
void D_BuildNewTiccmds(void);
#endif
//? how many ticks to run?
void TryRunTics (void);
// CPhipps - move to header file
void D_InitNetGame (void); // This does the setup
void D_CheckNetGame(void); // This waits for game start
// CPhipps - misc info broadcast
void D_NetSendMisc(netmisctype_t type, size_t len, void* data);
// CPhipps - ask server for a wad file we need
boolean D_NetGetWad(const char* name);
// Netgame stuff (buffers and pointers, i.e. indices).
extern doomcom_t *doomcom;
extern doomdata_t *netbuffer; // This points inside doomcom.
#endif
+234
View File
@@ -0,0 +1,234 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Player state structure.
*
*-----------------------------------------------------------------------------*/
#ifndef __D_PLAYER__
#define __D_PLAYER__
// The player data structure depends on a number
// of other structs: items (internal inventory),
// animation states (closely tied to the sprites
// used to represent them, unfortunately).
#include "d_items.h"
#include "p_pspr.h"
// In addition, the player is just a special
// case of the generic moving object/actor.
#include "p_mobj.h"
// Finally, for odd reasons, the player input
// is buffered within the player data struct,
// as commands per game tick.
#include "d_ticcmd.h"
#ifdef __GNUG__
#pragma interface
#endif
//
// Player states.
//
typedef enum
{
// Playing or camping.
PST_LIVE,
// Dead on the ground, view follows killer.
PST_DEAD,
// Ready to restart/respawn???
PST_REBORN
} playerstate_t;
//
// Player internal flags, for cheats and debug.
//
typedef enum
{
// No clipping, walk through barriers.
CF_NOCLIP = 1,
// No damage, no health loss.
CF_GODMODE = 2,
// Not really a cheat, just a debug aid.
CF_NOMOMENTUM = 4
} cheat_t;
//
// Extended player object info: player_t
//
typedef struct player_s
{
mobj_t* mo;
playerstate_t playerstate;
ticcmd_t cmd;
// Determine POV,
// including viewpoint bobbing during movement.
// Focal origin above r.z
fixed_t viewz;
// Base height above floor for viewz.
fixed_t viewheight;
// Bob/squat speed.
fixed_t deltaviewheight;
// bounded/scaled total momentum.
fixed_t bob;
/* killough 10/98: used for realistic bobbing (i.e. not simply overall speed)
* mo->momx and mo->momy represent true momenta experienced by player.
* This only represents the thrust that the player applies himself.
* This avoids anomolies with such things as Boom ice and conveyors.
*/
fixed_t momx, momy; // killough 10/98
// This is only used between levels,
// mo->health is used during levels.
int health;
int armorpoints;
// Armor type is 0-2.
int armortype;
// Power ups. invinc and invis are tic counters.
int powers[NUMPOWERS];
boolean cards[NUMCARDS];
boolean backpack;
// Frags, kills of other players.
int frags[MAXPLAYERS];
weapontype_t readyweapon;
// Is wp_nochange if not changing.
weapontype_t pendingweapon;
boolean weaponowned[NUMWEAPONS];
int ammo[NUMAMMO];
int maxammo[NUMAMMO];
// True if button down last tic.
int attackdown;
int usedown;
// Bit flags, for cheats and debug.
// See cheat_t, above.
int cheats;
// Refired shots are less accurate.
int refire;
// For intermission stats.
int killcount;
int itemcount;
int secretcount;
// Hint messages. // CPhipps - const
const char* message;
// For screen flashing (red or bright).
int damagecount;
int bonuscount;
// Who did damage (NULL for floors/ceilings).
mobj_t* attacker;
// So gun flashes light up areas.
int extralight;
// Current PLAYPAL, ???
// can be set to REDCOLORMAP for pain, etc.
int fixedcolormap;
// Player skin colorshift,
// 0-3 for which color to draw player.
int colormap;
// Overlay view sprites (gun, etc).
pspdef_t psprites[NUMPSPRITES];
// True if secret level has been done.
boolean didsecret;
} player_t;
//
// INTERMISSION
// Structure passed e.g. to WI_Start(wb)
//
typedef struct
{
boolean in; // whether the player is in game
// Player stats, kills, collected items etc.
int skills;
int sitems;
int ssecret;
int stime;
int frags[4];
int score; // current score on entry, modified on return
} wbplayerstruct_t;
typedef struct
{
int epsd; // episode # (0-2)
// if true, splash the secret level
boolean didsecret;
// previous and next levels, origin 0
int last;
int next;
int maxkills;
int maxitems;
int maxsecret;
int maxfrags;
// the par time
int partime;
// index of this player in game
int pnum;
wbplayerstruct_t plyr[MAXPLAYERS];
// CPhipps - total game time for completed levels so far
int totaltimes;
} wbstartstruct_t;
#endif
+754
View File
@@ -0,0 +1,754 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2004 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Network game server code
* New for LxDoom, but drawing ideas and code fragments from the
* earlier net code
*-----------------------------------------------------------------------------
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdarg.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#ifdef USE_SDL_NET
#include "SDL.h"
#endif
#include "doomtype.h"
#include "protocol.h"
#include "i_network.h"
#ifndef PRBOOM_SERVER
#include "m_fixed.h"
#endif
#include "i_system.h"
#include "m_swap.h"
#ifndef HAVE_NET
int main(void)
{
fprintf(stderr,
PACKAGE "-server: You must compile with networking enabled!\n");
exit(1);
return 1;
}
#else
#ifndef HAVE_GETOPT
/* The following code for getopt is from the libc-source of FreeBSD,
* it might be changed a little bit.
* Florian Schulze (florian.schulze@gmx.net)
*/
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
#endif
static const char rcsid[] = "$FreeBSD$";
#endif /* LIBC_SCCS and not lint */
int opterr = 1, /* if error message should be printed */
optind = 1, /* index into parent argv vector */
optopt, /* character checked for validity */
optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
char *__progname="prboom_server";
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt(nargc, nargv, ostr)
int nargc;
char * const *nargv;
const char *ostr;
{
extern char *__progname;
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
int ret;
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
++optind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':')
(void)fprintf(stderr,
"%s: illegal option -- %c\n", __progname, optopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*ostr == ':')
ret = BADARG;
else
ret = BADCH;
if (opterr)
(void)fprintf(stderr,
"%s: option requires an argument -- %c\n",
__progname, optopt);
return (ret);
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}
#else
#include <unistd.h>
#endif
#define MAXPLAYERS 4
#define BACKUPTICS 12
// Dummies to forfill l_udp.c unused client stuff
int M_CheckParm(const char* p) { p = NULL; return 1; }
int myargc;
char** myargv;
void NORETURN I_Error(const char *error, ...) // killough 3/20/98: add const
{
va_list argptr;
va_start(argptr,error);
vfprintf(stderr,error,argptr);
va_end(argptr);
exit(-1);
}
int playerjoingame[MAXPLAYERS], playerleftgame[MAXPLAYERS];
UDP_CHANNEL remoteaddr[MAXPLAYERS];
enum { pc_unused, pc_connected, pc_ready, pc_confirmedready, pc_playing, pc_quit } playerstate[MAXPLAYERS];
int displaycounter;
boolean n_players_in_state(int n, int ps) {
int i,j;
for (i=j=0;i<MAXPLAYERS;i++)
if (playerstate[i] == ps) j++;
return (j == n);
}
void BroadcastPacket(packet_header_t *packet, size_t len)
{
int i;
for (i=0; i<MAXPLAYERS; i++)
if (playerstate[i] != pc_unused && playerstate[i] != pc_quit)
I_SendPacketTo(packet, len, &remoteaddr[i]);
}
byte def_game_options[GAME_OPTIONS_SIZE] = \
{ // cf g_game.c:G_WriteOptions()
1, // monsters remember
1, // friction
0, // weapon recoil
1, // pushers
0, // reserved/unused
1, // player bobbing
0, 0, 0, // respawn, fast, nomonsters
1, // demo insurance
0, 0, 0, 0, // 4 bytes of random number seed
1, 0, 0, 0,
0, 128, /* distfriend */
0, 1, 1, 1, 1, 0,
/* Zeroes for all compatibility stuff */
};
#define nosuch "NOSUCHCONFIGITEM"
const char* gameopt_config_names[] = {
"monsters_remember",nosuch,"weapon_recoil",nosuch,nosuch,"player_bobbing",nosuch,nosuch,nosuch,"demo_insurance",
nosuch,nosuch,nosuch,nosuch, // RNG seed
"monster_infighting","player_helpers",nosuch,nosuch,
nosuch,nosuch, // distfriend
"monster_backing","monster_avoid_hazards","monster_friction","help_friends","dog_jumping","monkeys",
"comp_telefrag","comp_dropoff","comp_vile","comp_pain","comp_skull","comp_blazing","comp_doorlight","comp_model","comp_god","comp_falloff","comp_floors","comp_skymap","comp_pursuit","comp_doorstuck","comp_staylift","comp_zombie","comp_stairs","comp_infcheat","comp_zerotags","comp_moveblock","comp_respawn","comp_sound" };
const int num_gameopts = sizeof gameopt_config_names / sizeof gameopt_config_names[0];
int verbose;
void NORETURN sig_handler(int signum)
{
char buf[80];
I_SigString(buf,80,signum);
printf("Received signal: %s\n", buf);
// Any signal is fatal
exit(1);
}
void doexit(void)
{
packet_header_t packet;
// Send "downed" packet
packet_set(&packet, PKT_DOWN, 0);
BroadcastPacket(&packet, sizeof packet);
}
#ifndef USE_SDL_NET
static void I_InitSockets(int v4port)
{
v4socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
I_SetupSocket(v4socket, v4port, AF_INET);
}
#else
static void I_InitSockets(Uint16 port)
{
I_InitNetwork();
udp_socket = I_Socket(port);
if (!udp_socket) I_Error("I_InitSockets: failed to open UDP port %d\n",port);
}
#endif
long int ptic(packet_header_t* p)
{
return doom_ntohl(p->tic);
}
void read_config_file(FILE* fp, struct setup_packet_s* sp)
{
byte* gameopt = sp->game_options;
while (!feof(fp)) {
char def[80];
char strparm[100];
if (fscanf (fp, "%79s %99[^\n]\n", def, strparm) == 2) {
int v = atoi(strparm);
if (!strcmp(def,"default_skill")) {
if (verbose) printf("config file sets default_skill to %d\n",v);
sp->skill = v-1;
} else if (!strcmp(def,"default_compatibility_level")) {
if (verbose) printf("config file sets compatibility_level to %d\n",v);
if (v == -1) v = MAX_COMPATIBILITY_LEVEL-1; //e6y: -1 => maxcompat
sp->complevel = v;
} else {
int i;
for (i=0; i<num_gameopts; i++) {
if (!!strcmp(gameopt_config_names[i],def)) continue;
if (verbose) printf("config file sets %s to %d\n",def,v);
gameopt[i] = v;
}
}
}
}
}
static int badplayer(int n) { return (n < 0 || n >= MAXPLAYERS); }
int main(int argc, char** argv)
{
#ifndef USE_SDL_NET
int localport = 5030;
#else
Uint16 localport = 5030;
#endif
int numplayers = 2, xtratics = 0, ticdup = 1;
int exectics = 0; // gametics completed
struct setup_packet_s setupinfo = { 2, 0, 1, 1, 1, 0, best_compatibility, 0, 0};
char**wadname = NULL;
char**wadget = NULL;
int numwads = 0;
{
int opt;
byte *gameopt = setupinfo.game_options;
memcpy(gameopt, &def_game_options, sizeof (setupinfo.game_options));
while ((opt = getopt(argc, argv, "c:t:x:p:e:l:adrfns:N:vw:")) != EOF)
switch (opt) {
case 'c':
{
FILE *cf = fopen(optarg,"r");
if (!cf) { perror("fopen"); return -1; }
read_config_file(cf,&setupinfo);
fclose(cf);
}
break;
case 't':
if (optarg) ticdup = atoi(optarg);
break;
case 'x':
if (optarg) xtratics = atoi(optarg);
break;
case 'p':
if (optarg) localport = atoi(optarg);
break;
case 'e':
if (optarg) setupinfo.episode = atoi(optarg);
break;
case 'l':
if (optarg) setupinfo.level = atoi(optarg);
break;
case 'a':
setupinfo.deathmatch = 2;
break;
case 'd':
setupinfo.deathmatch = 1;
break;
case 'r':
setupinfo.game_options[6] = 1;
break;
case 'f':
setupinfo.game_options[7] = 1;
break;
case 'n':
setupinfo.game_options[8] = 1;
break;
case 's':
if (optarg) setupinfo.skill = atoi(optarg)-1;
break;
case 'N':
if (optarg) setupinfo.players = numplayers = atoi(optarg);
break;
case 'v':
verbose++;
break;
case 'w':
if (optarg) {
char *p;
wadname = realloc(wadname, ++numwads * sizeof *wadname);
wadget = realloc(wadget , numwads * sizeof *wadget );
wadname[numwads-1] = strdup(optarg);
if ((p = strchr(wadname[numwads-1], ','))) {
*p++ = 0; wadget[numwads-1] = p;
} else wadget[numwads-1] = NULL;
}
break;
}
}
setupinfo.ticdup = ticdup; setupinfo.extratic = xtratics;
{ /* Random number seed
* Mirrors the corresponding code in G_ReadOptions */
int rngseed = (int)time(NULL);
setupinfo.game_options[13] = rngseed & 0xff;
rngseed >>= 8;
setupinfo.game_options[12] = rngseed & 0xff;
rngseed >>= 8;
setupinfo.game_options[11] = rngseed & 0xff;
rngseed >>= 8;
setupinfo.game_options[10] = rngseed & 0xff;
}
I_InitSockets(localport);
printf("Listening on port %d, waiting for %d players\n", localport, numplayers);
{ // no players initially
int i;
for (i=0; i<MAXPLAYERS; i++) {
playerjoingame[i] = INT_MAX;
playerleftgame[i] = 0;
playerstate[i] = pc_unused;
}
// Print wads
for (i=0; i<numwads; i++)
printf("Wad %s (%s)\n", wadname[i], wadget[i] ? wadget[i] : "");
}
// Exit and signal handling
atexit(doexit); // heh
signal(SIGTERM, sig_handler);
signal(SIGINT , sig_handler);
#ifndef USE_SDL_NET
signal(SIGQUIT, sig_handler);
signal(SIGKILL, sig_handler);
signal(SIGHUP , sig_handler);
#endif
{
int remoteticfrom[MAXPLAYERS] = { 0, 0, 0, 0 };
int remoteticto[MAXPLAYERS] = { 0, 0, 0, 0 };
int backoffcounter[MAXPLAYERS] = { 0, 0, 0, 0 };
int curplayers = 0;
int confirming = 0;
boolean ingame = false;
ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
while (1) {
packet_header_t *packet = malloc(10000);
size_t len;
I_WaitForPacket(120*1000);
while ((len = I_GetPacket(packet, 10000))) {
if (verbose>2) printf("Received packet:");
switch (packet->type) {
case PKT_INIT:
if (!ingame) {
{
int n;
struct setup_packet_s *sinfo = (void*)(packet+1);
/* Find player number and add to the game */
n = *(short*)(packet+1);
if (badplayer(n) || playerstate[n] != pc_unused)
for (n=0; n<numplayers; n++)
if (playerstate[n] == pc_unused) break;
if (n == numplayers) break; // Full game
playerstate[n] = pc_connected;
#ifndef USE_SDL_NET
remoteaddr[n] = sentfrom;
#else
if (sentfrom==-1)
remoteaddr[n]=I_RegisterPlayer(&sentfrom_addr);
#endif
printf("Join by ");
I_PrintAddress(stdout, &remoteaddr[n]);
#ifdef USE_SDL_NET
printf(" (channel %d)",remoteaddr[n]);
#endif
printf(" as player %d\n",n);
{
int i;
size_t extrabytes = 0;
// Send setup packet
packet_set(packet, PKT_SETUP, 0);
memcpy(sinfo, &setupinfo, sizeof setupinfo);
sinfo->yourplayer = n;
sinfo->numwads = numwads;
for (i=0; i<numwads; i++) {
strcpy(sinfo->wadnames + extrabytes, wadname[i]);
extrabytes += strlen(wadname[i]) + 1;
}
I_SendPacketTo(packet, sizeof *packet + sizeof setupinfo + extrabytes,
remoteaddr+n);
I_uSleep(10000);
I_SendPacketTo(packet, sizeof *packet + sizeof setupinfo + extrabytes,
remoteaddr+n);
}
}
}
break;
case PKT_GO:
if (!ingame) {
int from = *(byte*)(packet+1);
if (badplayer(from) || playerstate[from] == pc_unused) break;
if (confirming) {
if (playerstate[from] != pc_confirmedready) curplayers++;
playerstate[from] = pc_confirmedready;
} else
playerstate[from] = pc_ready;
}
break;
case PKT_TICC:
{
byte tics = *(byte*)(packet+1);
int from = *(((byte*)(packet+1))+1);
if (badplayer(from)) break;
if (verbose>2)
printf("tics %ld - %ld from %d\n", ptic(packet), ptic(packet) + tics - 1, from);
if (ptic(packet) > remoteticfrom[from]) {
// Missed tics, so request a resend
packet_set(packet, PKT_RETRANS, remoteticfrom[from]);
I_SendPacketTo(packet, sizeof *packet, remoteaddr+from);
} else {
ticcmd_t *newtic = (void*)(((byte*)(packet+1))+2);
if (ptic(packet) + tics < remoteticfrom[from]) break; // Won't help
remoteticfrom[from] = ptic(packet);
while (tics--)
netcmds[from][remoteticfrom[from]++%BACKUPTICS] = *newtic++;
}
}
break;
case PKT_RETRANS:
{
int from = *(byte*)(packet+1);
if (badplayer(from)) break;
if (verbose>2) printf("%d requests resend from %d\n", from, ptic(packet));
remoteticto[from] = ptic(packet);
}
break;
case PKT_QUIT:
{
int from = *(byte*)(packet+1);
if (badplayer(from)) break;
if (!ingame && playerstate[from] != pc_unused) {
// If we already got a PKT_GO, we have to remove this player frmo the count of ready players. And we then flag this player slot as vacant.
printf("player %d pulls out\n", from);
if (playerstate[from] == pc_confirmedready) curplayers--;
playerstate[from] = pc_unused;
} else
if (playerleftgame[from] == INT_MAX) { // In the game
playerleftgame[from] = ptic(packet);
--curplayers;
if (verbose) printf("%d quits at %d (%d left)\n", from, ptic(packet),curplayers);
if (ingame && !curplayers) exit(0); // All players have exited
}
}
// Fall through and broadcast it
case PKT_EXTRA:
BroadcastPacket(packet, len);
if (packet->type == PKT_EXTRA) {
if (verbose>2) printf("misc from %d\n", *(((byte*)(packet+1))+1));
}
break;
case PKT_WAD:
{
int i;
int from = *(byte*)(packet+1);
char *name = 1 + (char*)(packet+1);
size_t size = sizeof(packet_header_t);
packet_header_t *reply;
if (badplayer(from) || playerstate[from] != pc_unused) break;
if (verbose) printf("Request for %s ", name);
for (i=0; i<numwads; i++)
if (!strcasecmp(name, wadname[i]))
break;
if ((i==numwads) || !wadget[i]) {
if (verbose) printf("n/a\n");
*(char*)(packet+1) = 0;
I_SendPacketTo(packet, size+1, remoteaddr + from);
} else {
size += strlen(wadname[i]) + strlen(wadget[i]) + 2;
reply = malloc(size);
packet_set(reply, PKT_WAD, 0);
strcpy((char*)(reply+1), wadname[i]);
strcpy((char*)(reply+1) + strlen(wadname[i]) + 1, wadget[i]);
printf("sending %s\n", wadget[i]);
I_SendPacketTo(reply, size, remoteaddr + from);
free(reply);
}
}
break;
default:
printf("Unrecognised packet type %d\n", packet->type);
break;
}
}
free(packet);
if (!ingame && n_players_in_state(numplayers,pc_confirmedready)) {
int i;
packet_header_t gopacket;
packet = &gopacket;
ingame=true;
printf("All players joined, beginning game.\n");
for (i=0; i<MAXPLAYERS; i++) {
if (playerstate[i] == pc_confirmedready) {
playerjoingame[i] = 0;
playerleftgame[i] = INT_MAX;
playerstate[i] = pc_playing;
}
}
packet_set(packet, PKT_GO, 0);
BroadcastPacket(packet, sizeof *packet);
I_uSleep(10000);
BroadcastPacket(packet, sizeof *packet);
I_uSleep(10000);
}
if (confirming && !--confirming && !ingame) {
int i;
curplayers = 0;
for (i=0; i<MAXPLAYERS; i++) {
if (playerstate[i] == pc_ready) {
playerstate[i] = pc_unused;
printf("Player %d dropped, no PKT_GO received in confirmation\n", i);
}
if (playerstate[i] == pc_confirmedready) playerstate[i] = pc_ready;
}
}
if (!ingame && n_players_in_state(numplayers,pc_ready)) {
printf("All players ready, now confirming.\n");
confirming = 100;
}
if (ingame) { // Run some tics
int lowtic = INT_MAX;
int i;
for (i=0; i<MAXPLAYERS; i++)
if (playerstate[i] == pc_playing || playerstate[i] == pc_quit) {
if (remoteticfrom[i] < playerleftgame[i]-1 && remoteticfrom[i]<lowtic)
lowtic = remoteticfrom[i];
}
if (verbose>1) printf("%d new tics can be run\n", lowtic - exectics);
if (lowtic > exectics)
exectics = lowtic; // count exec'ed tics
// Now send all tics up to lowtic
for (i=0; i<MAXPLAYERS; i++)
if (playerstate[i] == pc_playing) {
int tics;
if (lowtic <= remoteticto[i]) continue;
if ((remoteticto[i] -= xtratics) < 0) remoteticto[i] = 0;
tics = lowtic - remoteticto[i];
{
byte *p;
packet = malloc(sizeof(packet_header_t) + 1 +
tics * (1 + numplayers * (1 + sizeof(ticcmd_t))));
p = (void*)(packet+1);
packet_set(packet, PKT_TICS, remoteticto[i]);
*p++ = tics;
if (verbose>1) printf("sending %d tics to %d\n", tics, i);
while (tics--) {
int j, playersthistic = 0;
byte *q = p++;
for (j=0; j<MAXPLAYERS; j++)
if ((playerjoingame[j] <= remoteticto[i]) &&
(playerleftgame[j] > remoteticto[i])) {
*p++ = j;
memcpy(p, &netcmds[j][remoteticto[i]%BACKUPTICS], sizeof(ticcmd_t));
p += sizeof(ticcmd_t);
playersthistic++;
}
*q = playersthistic;
remoteticto[i]++;
}
I_SendPacketTo(packet, p - ((byte*)packet), remoteaddr+i);
free(packet);
}
{
if (remoteticfrom[i] == remoteticto[i]) {
backoffcounter[i] = 0;
} else if (remoteticfrom[i] > remoteticto[i]+1) {
if ((backoffcounter[i] += remoteticfrom[i] - remoteticto[i] - 1) > 35) {
packet_header_t *packet = malloc(sizeof(packet_header_t));
packet_set(packet, PKT_BACKOFF, remoteticto[i]);
I_SendPacketTo(packet,sizeof *packet,remoteaddr+i);
backoffcounter[i] = 0;
if (verbose) printf("telling client %d to back off\n",i);
free(packet);
}
}
}
}
}
if (!((ingame ? 0xff : 0xf) & displaycounter++)) {
int i;
fprintf(stderr,"Player states: [");
for (i=0;i<MAXPLAYERS;i++) {
switch (playerstate[i]) {
case pc_unused: fputc(' ',stderr); break;
case pc_connected: fputc('c',stderr); break;
case pc_ready: fputc('r',stderr); break;
case pc_confirmedready: fputc('g',stderr); break;
case pc_playing: fputc('p',stderr); break;
case pc_quit: fputc('x',stderr); break;
}
}
fprintf(stderr,"]\n");
}
}
}
}
#endif // HAVE_NET
+94
View File
@@ -0,0 +1,94 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* MapObj data. Map Objects or mobjs are actors, entities,
* thinker, take-your-pick... anything that moves, acts, or
* suffers state changes of more or less violent nature.
*
*-----------------------------------------------------------------------------*/
#ifndef __D_THINK__
#define __D_THINK__
#ifdef __GNUG__
#pragma interface
#endif
/*
* Experimental stuff.
* To compile this as "ANSI C with classes"
* we will need to handle the various
* action functions cleanly.
*/
// killough 11/98: convert back to C instead of C++
typedef void (*actionf_t)();
//typedef void (*actionf_v)();
//typedef void (*actionf_p1)( void* );
//typedef void (*actionf_p2)( void*, void* );
/* Note: In d_deh.c you will find references to these
* wherever code pointers and function handlers exist
*/
/*
typedef union
{
actionf_p1 acp1;
actionf_v acv;
actionf_p2 acp2;
} actionf_t;
*/
/* Historically, "think_t" is yet another
* function pointer to a routine to handle
* an actor.
*/
typedef actionf_t think_t;
/* Doubly linked list of actors. */
typedef struct thinker_s
{
struct thinker_s* prev;
struct thinker_s* next;
think_t function;
/* killough 8/29/98: we maintain thinkers in several equivalence classes,
* according to various criteria, so as to allow quicker searches.
*/
struct thinker_s *cnext, *cprev; /* Next, previous thinkers in same class */
/* killough 11/98: count of how many other objects reference
* this one using pointers. Used for garbage collection.
*/
unsigned references;
} thinker_t;
#endif
+59
View File
@@ -0,0 +1,59 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* System specific interface stuff.
*
*-----------------------------------------------------------------------------*/
#ifndef __D_TICCMD__
#define __D_TICCMD__
#include "doomtype.h"
#ifdef __GNUG__
#pragma interface
#endif
/* The data sampled per tick (single player)
* and transmitted to other peers (multiplayer).
* Mainly movements/button commands per game tick,
* plus a checksum for internal state consistency.
* CPhipps - explicitely signed the elements, since they have to be signed to work right
*/
typedef struct
{
signed char forwardmove; /* *2048 for move */
signed char sidemove; /* *2048 for move */
signed short angleturn; /* <<16 for angle delta */
short consistancy; /* checks for net game */
byte chatchar;
byte buttons;
} ticcmd_t;
#endif
+204
View File
@@ -0,0 +1,204 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* all external data is defined here
* most of the data is loaded into different structures at run time
* some internal structures shared by many modules are here
*
*-----------------------------------------------------------------------------*/
#ifndef __DOOMDATA__
#define __DOOMDATA__
// The most basic types we use, portability.
#include "config.h"
#include "doomtype.h"
//
// Map level types.
// The following data structures define the persistent format
// used in the lumps of the WAD files.
//
// Lump order in a map WAD: each map needs a couple of lumps
// to provide a complete scene geometry description.
enum {
ML_LABEL, // A separator, name, ExMx or MAPxx
ML_THINGS, // Monsters, items..
ML_LINEDEFS, // LineDefs, from editing
ML_SIDEDEFS, // SideDefs, from editing
ML_VERTEXES, // Vertices, edited and BSP splits generated
ML_SEGS, // LineSegs, from LineDefs split by BSP
ML_SSECTORS, // SubSectors, list of LineSegs
ML_NODES, // BSP nodes
ML_SECTORS, // Sectors, from editing
ML_REJECT, // LUT, sector-sector visibility
ML_BLOCKMAP // LUT, motion clipping, walls/grid element
};
#ifdef _MSC_VER // proff: This is the same as __attribute__ ((packed)) in GNUC
#pragma pack(push)
#pragma pack(1)
#endif //_MSC_VER
// A single Vertex.
typedef struct {
short x,y;
} PACKEDATTR mapvertex_t;
// A SideDef, defining the visual appearance of a wall,
// by setting textures and offsets.
typedef struct {
short textureoffset;
short rowoffset;
char toptexture[8];
char bottomtexture[8];
char midtexture[8];
short sector; // Front sector, towards viewer.
} PACKEDATTR mapsidedef_t;
// A LineDef, as used for editing, and as input to the BSP builder.
typedef struct {
unsigned short v1;
unsigned short v2;
unsigned short flags;
short special;
short tag;
// proff 07/23/2006 - support more than 32768 sidedefs
// use the unsigned value and special case the -1
// sidenum[1] will be -1 (NO_INDEX) if one sided
unsigned short sidenum[2];
} PACKEDATTR maplinedef_t;
#define NO_INDEX ((unsigned short)-1)
//
// LineDef attributes.
//
// Solid, is an obstacle.
#define ML_BLOCKING 1
// Blocks monsters only.
#define ML_BLOCKMONSTERS 2
// Backside will not be drawn if not two sided.
#define ML_TWOSIDED 4
// If a texture is pegged, the texture will have
// the end exposed to air held constant at the
// top or bottom of the texture (stairs or pulled
// down things) and will move with a height change
// of one of the neighbor sectors.
// Unpegged textures always have the first row of
// the texture at the top pixel of the line for both
// top and bottom textures (use next to windows).
// upper texture unpegged
#define ML_DONTPEGTOP 8
// lower texture unpegged
#define ML_DONTPEGBOTTOM 16
// In AutoMap: don't map as two sided: IT'S A SECRET!
#define ML_SECRET 32
// Sound rendering: don't let sound cross two of these.
#define ML_SOUNDBLOCK 64
// Don't draw on the automap at all.
#define ML_DONTDRAW 128
// Set if already seen, thus drawn in automap.
#define ML_MAPPED 256
//jff 3/21/98 Set if line absorbs use by player
//allow multiple push/switch triggers to be used on one push
#define ML_PASSUSE 512
// Sector definition, from editing.
typedef struct {
short floorheight;
short ceilingheight;
char floorpic[8];
char ceilingpic[8];
short lightlevel;
short special;
short tag;
} PACKEDATTR mapsector_t;
// SubSector, as generated by BSP.
typedef struct {
unsigned short numsegs;
unsigned short firstseg; // Index of first one; segs are stored sequentially.
} PACKEDATTR mapsubsector_t;
// LineSeg, generated by splitting LineDefs
// using partition lines selected by BSP builder.
typedef struct {
unsigned short v1;
unsigned short v2;
short angle;
unsigned short linedef;
short side;
short offset;
} PACKEDATTR mapseg_t;
// BSP node structure.
// Indicate a leaf.
#define NF_SUBSECTOR 0x8000
typedef struct {
short x; // Partition line from (x,y) to x+dx,y+dy)
short y;
short dx;
short dy;
// Bounding box for each child, clip against view frustum.
short bbox[2][4];
// If NF_SUBSECTOR its a subsector, else it's a node of another subtree.
unsigned short children[2];
} PACKEDATTR mapnode_t;
// Thing definition, position, orientation and type,
// plus skill/visibility flags and attributes.
typedef struct {
short x;
short y;
short angle;
short type;
short options;
} PACKEDATTR mapthing_t;
#ifdef _MSC_VER
#pragma pack(pop)
#endif //_MSC_VER
#endif // __DOOMDATA__
+48
View File
@@ -0,0 +1,48 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* DoomDef - basic defines for DOOM, e.g. Version, game mode
* and skill level, and display parameters.
*
*-----------------------------------------------------------------------------
*/
#ifdef __GNUG__
#pragma implementation "doomdef.h"
#endif
#include "doomdef.h"
// Location for any defines turned variables.
// None.
// proff 08/17/98: Changed for high-res
int SCREENWIDTH=320;
int SCREENHEIGHT=200;
int SCREENPITCH=320;
+335
View File
@@ -0,0 +1,335 @@
/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* DESCRIPTION:
* Internally used data structures for virtually everything,
* key definitions, lots of other stuff.
*
*-----------------------------------------------------------------------------*/
#ifndef __DOOMDEF__
#define __DOOMDEF__
/* use config.h if autoconf made one -- josh */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
// killough 4/25/98: Make gcc extensions mean nothing on other compilers
#ifndef __GNUC__
#define __attribute__(x)
#endif
// This must come first, since it redefines malloc(), free(), etc. -- killough:
#include "z_zone.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
// this should go here, not in makefile/configure.ac -- josh
#ifndef O_BINARY
#define O_BINARY 0
#endif
#include "m_swap.h"
#include "version.h"
// Game mode handling - identify IWAD version
// to handle IWAD dependend animations etc.
typedef enum {
shareware, // DOOM 1 shareware, E1, M9
registered, // DOOM 1 registered, E3, M27
commercial, // DOOM 2 retail, E1 M34 (DOOM 2 german edition not handled)
retail, // DOOM 1 retail, E4, M36
indetermined // Well, no IWAD found.
} GameMode_t;
// Mission packs - might be useful for TC stuff?
typedef enum {
doom, // DOOM 1
doom2, // DOOM 2
pack_tnt, // TNT mission pack
pack_plut, // Plutonia pack
none
} GameMission_t;
// Identify language to use, software localization.
typedef enum {
english,
french,
german,
unknown
} Language_t;
//
// For resize of screen, at start of game.
//
#define BASE_WIDTH 320
// It is educational but futile to change this
// scaling e.g. to 2. Drawing of status bar,
// menues etc. is tied to the scale implied
// by the graphics.
#define INV_ASPECT_RATIO 0.625 /* 0.75, ideally */
// killough 2/8/98: MAX versions for maximum screen sizes
// allows us to avoid the overhead of dynamic allocation
// when multiple screen sizes are supported
// proff 08/17/98: Changed for high-res
#ifdef IPHONE // JDC
#define MAX_SCREENWIDTH 480
#define MAX_SCREENHEIGHT 320
#else
#define MAX_SCREENWIDTH 2048
#define MAX_SCREENHEIGHT 1536
#endif
// SCREENWIDTH and SCREENHEIGHT define the visible size
extern int SCREENWIDTH;
extern int SCREENHEIGHT;
// SCREENPITCH is the size of one line in the buffer and
// can be bigger than the SCREENWIDTH depending on the size
// of one pixel (8, 16 or 32 bit) and the padding at the
// end of the line caused by hardware considerations
extern int SCREENPITCH;
// The maximum number of players, multiplayer/networking.
#define MAXPLAYERS 4
// phares 5/14/98:
// DOOM Editor Numbers (aka doomednum in mobj_t)
#define DEN_PLAYER5 4001
#define DEN_PLAYER6 4002
#define DEN_PLAYER7 4003
#define DEN_PLAYER8 4004
// State updates, number of tics / second.
#define TICRATE 35
// The current state of the game: whether we are playing, gazing
// at the intermission screen, the game final animation, or a demo.
typedef enum {
GS_LEVEL,
GS_INTERMISSION,
GS_FINALE,
GS_DEMOSCREEN
} gamestate_t;
//
// Difficulty/skill settings/filters.
//
// These are Thing flags
// Skill flags.
#define MTF_EASY 1
#define MTF_NORMAL 2
#define MTF_HARD 4
// Deaf monsters/do not react to sound.
#define MTF_AMBUSH 8
/* killough 11/98 */
#define MTF_NOTSINGLE 16
#define MTF_NOTDM 32
#define MTF_NOTCOOP 64
#define MTF_FRIEND 128
#define MTF_RESERVED 256
typedef enum {
sk_none=-1, //jff 3/24/98 create unpicked skill setting
sk_baby=0,
sk_easy,
sk_medium,
sk_hard,
sk_nightmare
} skill_t;
//
// Key cards.
//
typedef enum {
it_bluecard,
it_yellowcard,
it_redcard,
it_blueskull,
it_yellowskull,
it_redskull,
NUMCARDS
} card_t;
// The defined weapons, including a marker
// indicating user has not changed weapon.
typedef enum {
wp_fist,
wp_pistol,
wp_shotgun,
wp_chaingun,
wp_missile,
wp_plasma,
wp_bfg,
wp_chainsaw,
wp_supershotgun,
NUMWEAPONS,
wp_nochange // No pending weapon change.
} weapontype_t;
// Ammunition types defined.
typedef enum {
am_clip, // Pistol / chaingun ammo.
am_shell, // Shotgun / double barreled shotgun.
am_cell, // Plasma rifle, BFG.
am_misl, // Missile launcher.
NUMAMMO,
am_noammo // Unlimited for chainsaw / fist.
} ammotype_t;
// Power up artifacts.
typedef enum {
pw_invulnerability,
pw_strength,
pw_invisibility,
pw_ironfeet,
pw_allmap,
pw_infrared,
NUMPOWERS
} powertype_t;
// Power up durations (how many seconds till expiration).
typedef enum {
INVULNTICS = (30*TICRATE),
INVISTICS = (60*TICRATE),
INFRATICS = (120*TICRATE),
IRONTICS = (60*TICRATE)
} powerduration_t;
// DOOM keyboard definition.
// This is the stuff configured by Setup.Exe.
// Most key data are simple ascii (uppercased).
#define KEYD_RIGHTARROW 0xae
#define KEYD_LEFTARROW 0xac
#define KEYD_UPARROW 0xad
#define KEYD_DOWNARROW 0xaf
#define KEYD_ESCAPE 27
#define KEYD_ENTER 13
#define KEYD_TAB 9
#define KEYD_F1 (0x80+0x3b)
#define KEYD_F2 (0x80+0x3c)
#define KEYD_F3 (0x80+0x3d)
#define KEYD_F4 (0x80+0x3e)
#define KEYD_F5 (0x80+0x3f)
#define KEYD_F6 (0x80+0x40)
#define KEYD_F7 (0x80+0x41)
#define KEYD_F8 (0x80+0x42)
#define KEYD_F9 (0x80+0x43)
#define KEYD_F10 (0x80+0x44)
#define KEYD_F11 (0x80+0x57)
#define KEYD_F12 (0x80+0x58)
#define KEYD_BACKSPACE 127
#define KEYD_PAUSE 0xff
#define KEYD_EQUALS 0x3d
#define KEYD_MINUS 0x2d
#define KEYD_RSHIFT (0x80+0x36)
#define KEYD_RCTRL (0x80+0x1d)
#define KEYD_RALT (0x80+0x38)
#define KEYD_LALT KEYD_RALT
#define KEYD_CAPSLOCK 0xba // phares
// phares 3/2/98:
#define KEYD_INSERT 0xd2
#define KEYD_HOME 0xc7
#define KEYD_PAGEUP 0xc9
#define KEYD_PAGEDOWN 0xd1
#define KEYD_DEL 0xc8
#define KEYD_END 0xcf
#define KEYD_SCROLLLOCK 0xc6
#define KEYD_SPACEBAR 0x20
// phares 3/2/98
#define KEYD_NUMLOCK 0xC5 // killough 3/6/98
// cph - Add the numeric keypad keys, as suggested by krose 4/22/99:
// The way numbers are assigned to keys is a mess, but it's too late to
// change that easily. At least these additions are don neatly.
// Codes 0x100-0x200 are reserved for number pad
#define KEYD_KEYPAD0 (0x100 + '0')
#define KEYD_KEYPAD1 (0x100 + '1')
#define KEYD_KEYPAD2 (0x100 + '2')
#define KEYD_KEYPAD3 (0x100 + '3')
#define KEYD_KEYPAD4 (0x100 + '4')
#define KEYD_KEYPAD5 (0x100 + '5')
#define KEYD_KEYPAD6 (0x100 + '6')
#define KEYD_KEYPAD7 (0x100 + '7')
#define KEYD_KEYPAD8 (0x100 + '8')
#define KEYD_KEYPAD9 (0x100 + '9')
#define KEYD_KEYPADENTER (0x100 + KEYD_ENTER)
#define KEYD_KEYPADDIVIDE (0x100 + '/')
#define KEYD_KEYPADMULTIPLY (0x100 + '*')
#define KEYD_KEYPADMINUS (0x100 + '-')
#define KEYD_KEYPADPLUS (0x100 + '+')
#define KEYD_KEYPADPERIOD (0x100 + '.')
// phares 4/19/98:
// Defines Setup Screen groups that config variables appear in.
// Used when resetting the defaults for every item in a Setup group.
typedef enum {
ss_none,
ss_keys,
ss_weap,
ss_stat,
ss_auto,
ss_enem,
ss_mess,
ss_chat,
ss_gen, /* killough 10/98 */
ss_comp, /* killough 10/98 */
ss_max
} ss_types;
// phares 3/20/98:
//
// Player friction is variable, based on controlling
// linedefs. More friction can create mud, sludge,
// magnetized floors, etc. Less friction can create ice.
#define MORE_FRICTION_MOMENTUM 15000 // mud factor based on momentum
#define ORIG_FRICTION 0xE800 // original value
#define ORIG_FRICTION_FACTOR 2048 // original value
#endif // __DOOMDEF__

Some files were not shown because too many files have changed in this diff Show More