mirror of
https://github.com/id-Software/DOOM-iOS.git
synced 2026-05-14 03:49:10 +02:00
The DOOM Classic for iPhone 1.0 source as released on November 3, 2009
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -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
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -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 |
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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
@@ -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
|
||||
@@ -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 }
|
||||
};
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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();
|
||||
@@ -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
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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) {}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+1118
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
};
|
||||
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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__
|
||||
@@ -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;
|
||||
@@ -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
Reference in New Issue
Block a user