diff --git a/main/source/dlls/extdll.h b/main/source/dlls/extdll.h index abd24e3e..8467177e 100644 --- a/main/source/dlls/extdll.h +++ b/main/source/dlls/extdll.h @@ -49,14 +49,20 @@ typedef int BOOL; #define MAX_PATH PATH_MAX #include #include +#ifdef PUZL #ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef _vsnprintf #define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#endif +#ifndef _snprintf #define _snprintf(a,b,c,d,e) #endif +#endif #endif //_WIN32 // Misc C-runtime library headers diff --git a/main/source/linux/Makefile b/main/source/linux/Makefile index fc6f21e3..7fa25540 100644 --- a/main/source/linux/Makefile +++ b/main/source/linux/Makefile @@ -6,18 +6,18 @@ DLLNAME=ns -ARCH=i386 +ARCH=i686 #make sure this is the correct compiler for your system CC=gcc LD=gcc -LIBBASE=../../../libraries/current +LIBBASE=.. DLL_SRCDIR=../dlls GAME_SHARED_SRCDIR = ../game_shared MOD_SRCDIR = ../mod -PARTICLES_SRCDIR = ../particles +PARTICLES_SRCDIR = ../Particle PM_SHARED_SRCDIR=../pm_shared TEXT_SRCDIR = ../textrep UTIL_SRCDIR = ../util @@ -36,15 +36,15 @@ BASE_CFLAGS=-Dstricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp # -DAVH_SECURE_PRERELEASE_BUILD for CM testing #full optimization -CFLAGS=$(BASE_CFLAGS) -w -Wall -nostdinc++ -ffor-scope -fPIC -mcpu=i486 -O3 -pipe -funroll-loops -fdelayed-branch -malign-loops=4 -malign-jumps=4 -malign-functions=4 +CFLAGS=$(BASE_CFLAGS) -w -Wall -nostdinc++ -fpermissive -ffor-scope -fPIC -mtune=$(ARCH) -O3 -pipe -funroll-loops -fdelayed-branch -falign-loops=4 -falign-jumps=4 -falign-functions=4 #use these when debugging #CFLAGS=$(BASE_CFLAGS) -fPIC -fno-for-scope # add base directory (CGC) -INCLUDEDIRS=-I$(LIBBASE) -I. -I$(LIBBASE)/include -I$(LIBBASE)/include/stlport -I$(LIBBASE)/include/particle -I$(LIBBASE)/include/lua -I../dlls -I../engine -I../common -I../mod -I../game_shared -I../pm_shared -I.. -I/usr/include/c++/3.3/i486-linux -I/usr/include/c++/3.3 -I/usr/include/c++ -LINKDIRS= -L$(LIBBASE)/release +INCLUDEDIRS=-I$(LIBBASE) -I. -I$(LIBBASE)/include -I$(LIBBASE)/include/stlport -I$(LIBBASE)/particle -I$(LIBBASE)/include/lua -I../dlls -I../engine -I../common -I../mod -I../game_shared -I../pm_shared -I.. -I/usr/include/x86_64-linux-gnu/c++/4.8 -I/usr/include/c++/4.8 -I/usr/include/c++ +LINKDIRS= -L$(LIBBASE)/particle SHLIBEXT=so @@ -52,7 +52,7 @@ LDPRELIBS= $(CFLAGS) -shared \ -Wl,-Map,ns_map.txt \ $(LINKDIRS) -LDPOSTLIBS= -Wl,-Bstatic -lstlport_gcc -lstdc++ -lsupc++ -llua -llualib -lparticleMP -lcurl \ +LDPOSTLIBS= -Wl,-Bstatic -lstdc++ -lsupc++ -lparticleMP -lcurl \ -Wl,-Bdynamic -lm -lgcc -lgcc_eh # -lgcc -lgcc_eh -lelf DO_CC=$(CC) $(CFLAGS) $(INCLUDEDIRS) -o $@ -c $< @@ -257,18 +257,18 @@ release: $(DLLNAME)_$(ARCH).$(SHLIBEXT) curl: ./build-curl.sh neat: - -mkdir $(CURL_OBJDIR) - -mkdir $(DLL_OBJDIR) - -mkdir $(GAME_SHARED_OBJDIR) - -mkdir $(MOD_OBJDIR) - -mkdir $(PARTICLES_OBJDIR) - -mkdir $(PM_SHARED_OBJDIR) - -mkdir $(TEXT_OBJDIR) - -mkdir $(UTIL_OBJDIR) - -mkdir $(OUTPUT_DIR) + @-mkdir -p $(CURL_OBJDIR) + @-mkdir -p $(DLL_OBJDIR) + @-mkdir -p $(GAME_SHARED_OBJDIR) + @-mkdir -p $(MOD_OBJDIR) + @-mkdir -p $(PARTICLES_OBJDIR) + @-mkdir -p $(PM_SHARED_OBJDIR) + @-mkdir -p $(TEXT_OBJDIR) + @-mkdir -p $(UTIL_OBJDIR) + @-mkdir -p $(OUTPUT_DIR) clean: - -rm -f $(OBJ) - -rm -f $(DLLNAME)_$(ARCH).$(SHLIBEXT) + -rm $(OBJ) + -rm $(DLLNAME)_$(ARCH).$(SHLIBEXT) spotless: clean -rm -r $(CURL_OBJDIR) -rm -r $(DLL_OBJDIR) diff --git a/main/source/mod/AnimationUtil.cpp b/main/source/mod/AnimationUtil.cpp index c978a467..e2ee7e5f 100644 --- a/main/source/mod/AnimationUtil.cpp +++ b/main/source/mod/AnimationUtil.cpp @@ -1,4 +1,5 @@ #include +#include #include "types.h" #ifdef AVH_CLIENT @@ -665,4 +666,4 @@ void NS_GetBoneMatrices(const NS_AnimationData& inAnimationData, float time, NS_ } -} \ No newline at end of file +} diff --git a/main/source/mod/AvHEntities.cpp b/main/source/mod/AvHEntities.cpp index ce37c058..291206a7 100644 --- a/main/source/mod/AvHEntities.cpp +++ b/main/source/mod/AvHEntities.cpp @@ -1211,17 +1211,17 @@ void TriggerPresence::KeyValue(KeyValueData* pkvd) } else if(FStrEq(pkvd->szKeyName, "timebeforeleave")) { - this->mTimeBeforeLeave = max(atof(pkvd->szValue), 0.0f); + this->mTimeBeforeLeave = max(atof(pkvd->szValue), (double)0.0f); pkvd->fHandled = TRUE; } else if(FStrEq(pkvd->szKeyName, "momentaryopentime")) { - this->mMomentaryOpenTime = max(atof(pkvd->szValue), 0.01f); + this->mMomentaryOpenTime = max(atof(pkvd->szValue), (double)0.01f); pkvd->fHandled = TRUE; } else if(FStrEq(pkvd->szKeyName, "momentaryclosetime")) { - this->mMomentaryCloseTime = max(atof(pkvd->szValue), 0.01f); + this->mMomentaryCloseTime = max(atof(pkvd->szValue), (double)0.01f); pkvd->fHandled = TRUE; } else if(FStrEq(pkvd->szKeyName, "spawnflags")) diff --git a/main/source/mod/AvHNexusServer.cpp b/main/source/mod/AvHNexusServer.cpp index 01d0c795..5de648f1 100644 --- a/main/source/mod/AvHNexusServer.cpp +++ b/main/source/mod/AvHNexusServer.cpp @@ -4,20 +4,22 @@ using std::string; #include "AvHNexusServer.h" #include "AvHServerUtil.h" - bool AvHNexus::send(entvars_t* const pev, const unsigned char* data, const unsigned int length) { return false; } - bool AvHNexus::recv(entvars_t* const pev, const char* data, const unsigned int length) { return false; } - string AvHNexus::getNetworkID(const edict_t* edict) { return AvHSUGetPlayerAuthIDString((edict_t *)edict); } - void AvHNexus::handleUnauthorizedJoinTeamAttempt(const edict_t* edict, const unsigned char team_index) {} - void AvHNexus::performSpeedTest(void) {} - void AvHNexus::processResponses(void) {} - void AvHNexus::setGeneratePerformanceData(const edict_t* edict, const bool generate) {} - bool AvHNexus::getGeneratePerformanceData(void) { return false; } - bool AvHNexus::isRecordingGame(void) { return false; } - void AvHNexus::cancelGame(void) {} - void AvHNexus::finishGame(void) {} - void AvHNexus::startGame(void) {} - void AvHNexus::startup(void) {} - void AvHNexus::shutdown(void) {} +namespace AvHNexus { + bool send(entvars_t* const pev, const unsigned char* data, const unsigned int length) { return false; } + bool recv(entvars_t* const pev, const char* data, const unsigned int length) { return false; } + string getNetworkID(const edict_t* edict) { return AvHSUGetPlayerAuthIDString((edict_t *)edict); } + void handleUnauthorizedJoinTeamAttempt(const edict_t* edict, const unsigned char team_index) {} + void performSpeedTest(void) {} + void processResponses(void) {} + void setGeneratePerformanceData(const edict_t* edict, const bool generate) {} + bool getGeneratePerformanceData(void) { return false; } + bool isRecordingGame(void) { return false; } + void cancelGame(void) {} + void finishGame(void) {} + void startGame(void) {} + void startup(void) {} + void shutdown(void) {} +} #else #include #include "AvHNexusServer.h" diff --git a/main/source/mod/AvHParticleSystem.cpp b/main/source/mod/AvHParticleSystem.cpp index b463d335..29563386 100644 --- a/main/source/mod/AvHParticleSystem.cpp +++ b/main/source/mod/AvHParticleSystem.cpp @@ -688,9 +688,9 @@ AvHParticleSystem::GenerateParticles(int inNumberParticles) else if(theDomain == PS_Sphere) { float theRadius1 = fabs(this->mGenerationEntityAbsMax[0] - this->mGenerationEntityAbsMin[0])/2.0f; - theRadius1 = max(theRadius1 - theEdgeInset, 0); + theRadius1 = max(theRadius1 - theEdgeInset, 0.0f); float theRadius2 = this->mGenerationEntityParam; - theRadius2 = max(theRadius2 - theEdgeInset, 0); + theRadius2 = max(theRadius2 - theEdgeInset, 0.0f); theParams[3] = theRadius1; theParams[4] = theRadius2; } diff --git a/main/source/mod/AvHParticleSystem.h b/main/source/mod/AvHParticleSystem.h index d9428b32..aef5a83a 100644 --- a/main/source/mod/AvHParticleSystem.h +++ b/main/source/mod/AvHParticleSystem.h @@ -205,4 +205,4 @@ private: }; -#endif \ No newline at end of file +#endif diff --git a/main/source/mod/AvHParticleSystemManager.h b/main/source/mod/AvHParticleSystemManager.h index 3e6d0563..1a7802e1 100644 --- a/main/source/mod/AvHParticleSystemManager.h +++ b/main/source/mod/AvHParticleSystemManager.h @@ -19,6 +19,7 @@ #ifndef AVH_PSMANAGER_H #define AVH_PSMANAGER_H +#include #include "util/nowarnings.h" #include "types.h" #include "mod/AvHParticleSystem.h" @@ -169,4 +170,4 @@ private: }; -#endif \ No newline at end of file +#endif diff --git a/main/source/mod/AvHParticleTemplate.cpp b/main/source/mod/AvHParticleTemplate.cpp index 2ee9cbf9..e97ed170 100644 --- a/main/source/mod/AvHParticleTemplate.cpp +++ b/main/source/mod/AvHParticleTemplate.cpp @@ -1,3 +1,4 @@ +#include #include "util/nowarnings.h" #include "mod/AvHParticleTemplate.h" #include "common/renderingconst.h" diff --git a/main/source/mod/AvHPlayer.cpp b/main/source/mod/AvHPlayer.cpp index 3ac61825..c50acaab 100644 --- a/main/source/mod/AvHPlayer.cpp +++ b/main/source/mod/AvHPlayer.cpp @@ -6878,7 +6878,7 @@ void AvHPlayer::InternalMovementThink() float factor = pushbackfactor / (radius / distance); float weigthFactor=1.0f; - int veriticalLimit=110; + float veriticalLimit=110.0f; switch(theEntity->pev->iuser3) { case AVH_USER3_ALIEN_PLAYER4: weigthFactor=0.70f; @@ -9227,7 +9227,7 @@ bool AvHPlayer::RunClientScript(const string& inScriptName) void AvHPlayer::PrintWeaponListToClient(CBaseEntity *theAvHPlayer) { char msg[1024]; - sprintf(msg, "Weapons for %s:\n", this->GetPlayerName()); + sprintf(msg, "Weapons for %s:\n", this->GetPlayerName().c_str()); ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, msg); for(int i = 0; i < MAX_ITEM_TYPES; i++) diff --git a/main/source/mod/AvHScriptClient.cpp b/main/source/mod/AvHScriptClient.cpp index 9d4b8d3c..9b5bbae3 100644 --- a/main/source/mod/AvHScriptClient.cpp +++ b/main/source/mod/AvHScriptClient.cpp @@ -46,13 +46,16 @@ #include "common/event_api.h" #include "mod/AvHScriptManager.h" +#ifdef USE_LUA extern "C" { #include } +#endif extern void DrawScaledHUDSprite(int inSpriteHandle, int inMode, int inRowsInSprite = 1, int inX = 0, int inY = 0, int inWidth = ScreenWidth(), int inHeight = ScreenHeight(), int inForceSpriteFrame = -1, float inStartU = 0.0f, float inStartV = 0.0f, float inEndU = 1.0f, float inEndV = 1.0f, float inRotateUVRadians = 0.0f, bool inUVWrapsOverFrames = false); extern vec3_t v_origin; +#ifdef USE_LUA static int errormessage(lua_State* inState) { const char *s = lua_tostring(inState, 1); @@ -93,6 +96,7 @@ static int print(lua_State* inState) return 0; } + // Runs a client command, returns success static int clientCommand(lua_State* inState) { @@ -438,9 +442,11 @@ static int triFog(lua_State* inState) return 1; } +#endif void AvHScriptInstance::InitClient() { +#ifdef USE_LUA //lua_register(this->mState, LUA_ERRORMESSAGE, errormessage); lua_register(this->mState, "print", print); lua_register(this->mState, "clientCommand", clientCommand); @@ -469,4 +475,5 @@ void AvHScriptInstance::InitClient() //lua_register(this->mState, "drawScaledHUDSprite", drawScaledHUDSpriteUV); //lua_register(this->mState, "drawScaledTiledHUDSprite", drawScaledTiledHUDSprite); -} \ No newline at end of file +#endif +} diff --git a/main/source/mod/AvHScriptManager.cpp b/main/source/mod/AvHScriptManager.cpp index 77f86075..0f5ae8d4 100644 --- a/main/source/mod/AvHScriptManager.cpp +++ b/main/source/mod/AvHScriptManager.cpp @@ -28,17 +28,21 @@ #include "util/STLUtil.h" #include "mod/AvHConstants.h" +#ifdef USE_LUA extern "C" { #include #include #include } +#endif AvHScriptInstance* gRunningScript = NULL; AvHScriptInstance::AvHScriptInstance(string inScriptName) { +#ifdef USE_LUA this->mState = NULL; +#endif this->Init(); @@ -70,10 +74,12 @@ void AvHScriptInstance::CallSimpleFunction(const string& inFunctionName) { gRunningScript = this; +#ifdef USE_LUA // Execute callback lua_getglobal(this->mState, inFunctionName.c_str()); //lua_pushstring(this->mState, inFunctionName.c_str()); lua_call(this->mState, 0, 0); +#endif gRunningScript = NULL; } @@ -83,16 +89,21 @@ void AvHScriptInstance::Cleanup() ASSERT(!this->CallbacksPending()); ASSERT(this->mState); +#ifdef USE_LUA lua_close(this->mState); +#endif } +#ifdef USE_LUA lua_State* AvHScriptInstance::GetState() { return this->mState; } +#endif void AvHScriptInstance::Init() { +#ifdef USE_LUA this->mState = lua_open(); lua_baselibopen(this->mState); @@ -107,6 +118,7 @@ void AvHScriptInstance::Init() #else this->InitClient(); #endif +#endif } void AvHScriptInstance::Reset() @@ -127,7 +139,9 @@ void AvHScriptInstance::Run() // Set global current script so it's accessible statically (needed for setting callbacks) gRunningScript = this; +#ifdef USE_LUA lua_dofile(this->mState, this->mScriptName.c_str()); +#endif gRunningScript = NULL; } @@ -235,6 +249,7 @@ void AvHScriptManager::Update(float inTime) #ifdef AVH_CLIENT void AvHScriptManager::ClientUpdate(float inTimePassed) { +#ifdef USE_LUA // For all scripts for(AvHScriptInstanceListType::iterator theIter = this->mScriptList.begin(); theIter != this->mScriptList.end(); /* no increment */) { @@ -269,6 +284,7 @@ void AvHScriptManager::ClientUpdate(float inTimePassed) theIter++; } } +#endif } void AvHScriptManager::DrawNormal() @@ -301,6 +317,7 @@ void AvHScriptManager::DrawNoZBuffering() bool AvHScriptManager::GetClientMove(int& outButtonBits, int& outImpulse) { bool theSuccess = false; +#ifdef USE_LUA #ifdef DEBUG @@ -335,6 +352,7 @@ bool AvHScriptManager::GetClientMove(int& outButtonBits, int& outImpulse) #endif +#endif return theSuccess; } diff --git a/main/source/mod/AvHScriptManager.h b/main/source/mod/AvHScriptManager.h index b8c8a793..9afd94e5 100644 --- a/main/source/mod/AvHScriptManager.h +++ b/main/source/mod/AvHScriptManager.h @@ -23,7 +23,9 @@ #include "util/Checksum.h" #include "util/STLUtil.h" +#ifdef USE_LUA extern "C" struct lua_State; +#endif class AvHScriptInstance { @@ -38,7 +40,9 @@ public: void Cleanup(); +#ifdef USE_LUA lua_State* GetState(); +#endif void Reset(); @@ -58,7 +62,9 @@ private: #endif // This gets copied around, so make sure elements can be shallow-copied or write a copy constructor +#ifdef USE_LUA lua_State* mState; +#endif string mScriptName; typedef pair CallbackType; @@ -96,4 +102,4 @@ private: }; -#endif \ No newline at end of file +#endif diff --git a/main/source/mod/AvHScriptServer.cpp b/main/source/mod/AvHScriptServer.cpp index c1adc5eb..d920b29e 100644 --- a/main/source/mod/AvHScriptServer.cpp +++ b/main/source/mod/AvHScriptServer.cpp @@ -33,6 +33,7 @@ #include "util/Checksum.h" #include "util/STLUtil.h" +#ifdef USE_LUA extern "C" { #include } @@ -545,8 +546,11 @@ static int setServerVariable(lua_State* inState) return 0; } +#endif + void AvHScriptInstance::InitServer() { +#ifdef USE_LUA //lua_register(this->mState, LUA_ERRORMESSAGE, errormessage); //lua_register(this->mState, "execute", execute); @@ -579,4 +583,6 @@ void AvHScriptInstance::InitServer() lua_register(this->mState, "runClientScript", runClientScript); lua_register(this->mState, "setServerVariable", setServerVariable); -} \ No newline at end of file +#endif +} + diff --git a/main/source/mod/AvHScriptShared.cpp b/main/source/mod/AvHScriptShared.cpp index dfd29ab5..617aabe1 100644 --- a/main/source/mod/AvHScriptShared.cpp +++ b/main/source/mod/AvHScriptShared.cpp @@ -37,6 +37,7 @@ #include "mod/AvHSharedUtil.h" +#ifdef USE_LUA extern "C" { #include } @@ -93,3 +94,4 @@ void AvHScriptInstance::InitShared() lua_register(this->mState, "getTime", getTime); } +#endif diff --git a/main/source/particle/Makefile b/main/source/particle/Makefile new file mode 100644 index 00000000..3b05842f --- /dev/null +++ b/main/source/particle/Makefile @@ -0,0 +1,43 @@ +###################################################################### +# Particle System API +# +# Copyright 1998 by David K. McAllister. +# +###################################################################### + +C++ = g++ + +GLUT_HOME =/usr/local/contrib/unmoderated + +MP = #-mp -DPARTICLE_MP + +# Make it real fast on an Origin 2000. +#LNO =-LNO:opt=1:fusion=2:fission=2:fusion_peeling_limit=2048:cs1=32K:cs2=8M +COPT = $(MP) -O3 $(LNO) -fPIC + +CFLAGS = $(COPT) $(COMPFLAGS) -I. -I.. -I$(GLUT_HOME)/include -I/usr/include/c++/4.8/ + +POBJS =action_api.o actions.o opengl.o system.o + +ALL = libparticleMP.a SPDir + +all: $(ALL) + +# following line needed for c++ .cc files +.SUFFIXES : .cpp + +.cpp.o: + $(C++) $(CFLAGS) -c $< + +libparticleMP.a: $(POBJS) + rm -f $@ + ar clq $@ $(POBJS) + +SPDir: + (cd SP ; smake) + +clean: + (cd SP ; smake clean) + rm -f libpar* + rm -f *.o + rm -rf *ii_files diff --git a/main/source/particle/ParticleDLL.dsp b/main/source/particle/ParticleDLL.dsp new file mode 100644 index 00000000..38049cc9 --- /dev/null +++ b/main/source/particle/ParticleDLL.dsp @@ -0,0 +1,132 @@ +# Microsoft Developer Studio Project File - Name="ParticleDLL" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ParticleDLL - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ParticleDLL.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ParticleDLL.mak" CFG="ParticleDLL - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ParticleDLL - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ParticleDLL - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ParticleDLL - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I ".." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "PARTICLEDLL_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 opengl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# SUBTRACT LINK32 /profile + +!ELSEIF "$(CFG)" == "ParticleDLL - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "PARTICLEDLL_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 opengl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "ParticleDLL - Win32 Release" +# Name "ParticleDLL - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\action_api.cpp +# End Source File +# Begin Source File + +SOURCE=.\actions.cpp +# End Source File +# Begin Source File + +SOURCE=.\opengl.cpp +# End Source File +# Begin Source File + +SOURCE=.\system.cpp +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\general.h +# End Source File +# Begin Source File + +SOURCE=.\p_vector.h +# End Source File +# Begin Source File + +SOURCE=.\papi.h +# End Source File +# End Group +# End Target +# End Project diff --git a/main/source/particle/Readme.txt b/main/source/particle/Readme.txt new file mode 100644 index 00000000..9e1847b9 --- /dev/null +++ b/main/source/particle/Readme.txt @@ -0,0 +1,4 @@ +For SGI we make a version of the library that's made for +multiprocessing in this directory and a version that's made for single +processing in the SP directory. Typing make in this directory will make both. + diff --git a/main/source/particle/action_api.cpp b/main/source/particle/action_api.cpp new file mode 100644 index 00000000..c7ecc7e2 --- /dev/null +++ b/main/source/particle/action_api.cpp @@ -0,0 +1,390 @@ +// action_api.cpp +// +// Copyright 1997-1998 by David K. McAllister +// +// This file implements the action API calls by creating +// action class instances, which are either executed or +// added to an action list. + +#include "general.h" + +extern void _pAddActionToList(ParticleAction *S, int size); +extern void _pCallActionList(ParticleAction *pa, int num_actions, + ParticleGroup *pg); + +// Do not call this function. +void _pSendAction(ParticleAction *S, PActionEnum type, int size) +{ + _ParticleState &_ps = _GetPState(); + + S->type = type; + + if(_ps.in_new_list) + { + _pAddActionToList(S, size); + } + else + { + // Immediate mode. Execute it. + // This is a hack to give them local access to dt. + S->dt = _ps.dt; + _pCallActionList(S, 1, _ps.pgrp); + } +} + +PARTICLEDLL_API void pAvoid(float magnitude, float epsilon, float look_ahead, + PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + PAAvoid S; + + S.position = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); + S.magnitude = magnitude; + S.epsilon = epsilon; + S.look_ahead = look_ahead; + + _pSendAction(&S, PAAvoidID, sizeof(PAAvoid)); +} + +PARTICLEDLL_API void pBounce(float friction, float resilience, float cutoff, + PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + PABounce S; + + S.position = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); + S.oneMinusFriction = 1.0f - friction; + S.resilience = resilience; + S.cutoffSqr = fsqr(cutoff); + + _pSendAction(&S, PABounceID, sizeof(PABounce)); +} + +PARTICLEDLL_API void pCopyVertexB(bool copy_pos, bool copy_vel) +{ + PACopyVertexB S; + + S.copy_pos = copy_pos; + S.copy_vel = copy_vel; + + _pSendAction(&S, PACopyVertexBID, sizeof(PACopyVertexB)); +} + +PARTICLEDLL_API void pDamping(float damping_x, float damping_y, float damping_z, + float vlow, float vhigh) +{ + PADamping S; + + S.damping = pVector(damping_x, damping_y, damping_z); + S.vlowSqr = fsqr(vlow); + S.vhighSqr = fsqr(vhigh); + + _pSendAction(&S, PADampingID, sizeof(PADamping)); +} + +PARTICLEDLL_API void pExplosion(float center_x, float center_y, float center_z, float velocity, + float magnitude, float stdev, float epsilon, float age) +{ + PAExplosion S; + + S.center = pVector(center_x, center_y, center_z); + S.velocity = velocity; + S.magnitude = magnitude; + S.stdev = stdev; + S.epsilon = epsilon; + S.age = age; + + if(S.epsilon < 0.0f) + S.epsilon = P_EPS; + + _pSendAction(&S, PAExplosionID, sizeof(PAExplosion)); +} + +PARTICLEDLL_API void pFollow(float magnitude, float epsilon, float max_radius) +{ + PAFollow S; + + S.magnitude = magnitude; + S.epsilon = epsilon; + S.max_radius = max_radius; + + _pSendAction(&S, PAFollowID, sizeof(PAFollow)); +} + +PARTICLEDLL_API void pGravitate(float magnitude, float epsilon, float max_radius) +{ + PAGravitate S; + + S.magnitude = magnitude; + S.epsilon = epsilon; + S.max_radius = max_radius; + + _pSendAction(&S, PAGravitateID, sizeof(PAGravitate)); +} + +PARTICLEDLL_API void pGravity(float dir_x, float dir_y, float dir_z) +{ + PAGravity S; + + S.direction = pVector(dir_x, dir_y, dir_z); + + _pSendAction(&S, PAGravityID, sizeof(PAGravity)); +} + +PARTICLEDLL_API void pJet(float center_x, float center_y, float center_z, + float magnitude, float epsilon, float max_radius) +{ + _ParticleState &_ps = _GetPState(); + + PAJet S; + + S.center = pVector(center_x, center_y, center_z); + S.acc = _ps.Vel; + S.magnitude = magnitude; + S.epsilon = epsilon; + S.max_radius = max_radius; + + _pSendAction(&S, PAJetID, sizeof(PAJet)); +} + +PARTICLEDLL_API void pKillOld(float age_limit, bool kill_less_than) +{ + PAKillOld S; + + S.age_limit = age_limit; + S.kill_less_than = kill_less_than; + + _pSendAction(&S, PAKillOldID, sizeof(PAKillOld)); +} + +PARTICLEDLL_API void pMatchVelocity(float magnitude, float epsilon, float max_radius) +{ + PAMatchVelocity S; + + S.magnitude = magnitude; + S.epsilon = epsilon; + S.max_radius = max_radius; + + _pSendAction(&S, PAMatchVelocityID, sizeof(PAMatchVelocity)); +} + +PARTICLEDLL_API void pMove() +{ + PAMove S; + + _pSendAction(&S, PAMoveID, sizeof(PAMove)); +} + +PARTICLEDLL_API void pOrbitLine(float p_x, float p_y, float p_z, + float axis_x, float axis_y, float axis_z, + float magnitude, float epsilon, float max_radius) +{ + PAOrbitLine S; + + S.p = pVector(p_x, p_y, p_z); + S.axis = pVector(axis_x, axis_y, axis_z); + S.axis.normalize(); + S.magnitude = magnitude; + S.epsilon = epsilon; + S.max_radius = max_radius; + + _pSendAction(&S, PAOrbitLineID, sizeof(PAOrbitLine)); +} + +PARTICLEDLL_API void pOrbitPoint(float center_x, float center_y, float center_z, + float magnitude, float epsilon, float max_radius) +{ + PAOrbitPoint S; + + S.center = pVector(center_x, center_y, center_z); + S.magnitude = magnitude; + S.epsilon = epsilon; + S.max_radius = max_radius; + + _pSendAction(&S, PAOrbitPointID, sizeof(PAOrbitPoint)); +} + +PARTICLEDLL_API void pRandomAccel(PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + PARandomAccel S; + + S.gen_acc = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); + + _pSendAction(&S, PARandomAccelID, sizeof(PARandomAccel)); +} + +PARTICLEDLL_API void pRandomDisplace(PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + PARandomDisplace S; + + S.gen_disp = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); + + _pSendAction(&S, PARandomDisplaceID, sizeof(PARandomDisplace)); +} + +PARTICLEDLL_API void pRandomVelocity(PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + PARandomVelocity S; + + S.gen_vel = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); + + _pSendAction(&S, PARandomVelocityID, sizeof(PARandomVelocity)); +} + +PARTICLEDLL_API void pRestore(float time_left) +{ + PARestore S; + + S.time_left = time_left; + + _pSendAction(&S, PARestoreID, sizeof(PARestore)); +} + +PARTICLEDLL_API void pSink(bool kill_inside, PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + PASink S; + + S.kill_inside = kill_inside; + S.position = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); + + _pSendAction(&S, PASinkID, sizeof(PASink)); +} + +PARTICLEDLL_API void pSinkVelocity(bool kill_inside, PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + PASinkVelocity S; + + S.kill_inside = kill_inside; + S.velocity = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); + + _pSendAction(&S, PASinkVelocityID, sizeof(PASinkVelocity)); +} + +PARTICLEDLL_API void pSource(float particle_rate, PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + _ParticleState &_ps = _GetPState(); + + PASource S; + + S.particle_rate = particle_rate; + S.position = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); + S.positionB = _ps.VertexB; + S.size = _ps.Size; + S.velocity = _ps.Vel; + S.color = _ps.Color; + S.alpha = _ps.Alpha; + S.age = _ps.Age; + S.age_sigma = _ps.AgeSigma; + S.vertexB_tracks = _ps.vertexB_tracks; + + _pSendAction(&S, PASourceID, sizeof(PASource)); +} + +PARTICLEDLL_API void pSpeedLimit(float min_speed, float max_speed) +{ + PASpeedLimit S; + + S.min_speed = min_speed; + S.max_speed = max_speed; + + _pSendAction(&S, PASpeedLimitID, sizeof(PASpeedLimit)); +} + +PARTICLEDLL_API void pTargetColor(float color_x, float color_y, float color_z, + float alpha, float scale) +{ + PATargetColor S; + + S.color = pVector(color_x, color_y, color_z); + S.alpha = alpha; + S.scale = scale; + + _pSendAction(&S, PATargetColorID, sizeof(PATargetColor)); +} + +PARTICLEDLL_API void pTargetSize(float size_x, float size_y, float size_z, + float scale_x, float scale_y, float scale_z) +{ + PATargetSize S; + + S.size = pVector(size_x, size_y, size_z); + S.scale = pVector(scale_x, scale_y, scale_z); + + _pSendAction(&S, PATargetSizeID, sizeof(PATargetSize)); +} + +PARTICLEDLL_API void pTargetVelocity(float vel_x, float vel_y, float vel_z, float scale) +{ + PATargetVelocity S; + + S.velocity = pVector(vel_x, vel_y, vel_z); + S.scale = scale; + + _pSendAction(&S, PATargetVelocityID, sizeof(PATargetVelocity)); +} + +// If in immediate mode, quickly add a vertex. +// If building an action list, call pSource. +PARTICLEDLL_API void pVertex(float x, float y, float z) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + { + pSource(1, PDPoint, x, y, z); + return; + } + + // Immediate mode. Quickly add the vertex. + if(_ps.pgrp == NULL) + return; + + pVector pos(x, y, z); + pVector siz, vel, col, posB; + if(_ps.vertexB_tracks) + posB = pos; + else + _ps.VertexB.Generate(posB); + _ps.Size.Generate(siz); + _ps.Vel.Generate(vel); + _ps.Color.Generate(col); + _ps.pgrp->Add(pos, posB, siz, vel, col, _ps.Alpha, _ps.Age); +} + +PARTICLEDLL_API void pVortex(float center_x, float center_y, float center_z, + float axis_x, float axis_y, float axis_z, + float magnitude, float epsilon, float max_radius) +{ + PAVortex S; + + S.center = pVector(center_x, center_y, center_z); + S.axis = pVector(axis_x, axis_y, axis_z); + S.axis.normalize(); + S.magnitude = magnitude; + S.epsilon = epsilon; + S.max_radius = max_radius; + + _pSendAction(&S, PAVortexID, sizeof(PAVortex)); +} diff --git a/main/source/particle/actions.cpp b/main/source/particle/actions.cpp new file mode 100644 index 00000000..ce053e12 --- /dev/null +++ b/main/source/particle/actions.cpp @@ -0,0 +1,1840 @@ +// actions.cpp +// +// Copyright 1997-1998 by David K. McAllister +// +// I used code Copyright 1997 by Jonathan P. Leech +// as an example in implenting this. +// +// This file implements the dynamics of particle actions. + +#include "general.h" +#include +#include + +#define SQRT2PI 2.506628274631000502415765284811045253006 +#define ONEOVERSQRT2PI (1. / SQRT2PI) + +// To offset [0 .. 1] vectors to [-.5 .. .5] +static pVector vHalf(0.5, 0.5, 0.5); + +static inline pVector RandVec() +{ + return pVector(drand48(), drand48(), drand48()); +} + +// Return a random number with a normal distribution. +static inline float NRand(float sigma = 1.0f) +{ +#define ONE_OVER_SIGMA_EXP (1.0f / 0.7975f) + + if(sigma == 0) return 0; + + float y; + do + { + y = -logf(drand48()); + } + while(drand48() > expf(-fsqr(y - 1.0f)*0.5f)); + + if(rand() & 0x1) + return y * sigma * ONE_OVER_SIGMA_EXP; + else + return -y * sigma * ONE_OVER_SIGMA_EXP; +} + +void PAAvoid::Execute(ParticleGroup *group) +{ + float magdt = magnitude * dt; + + switch(position.type) + { + case PDPlane: + { + if(look_ahead < P_MAXFLOAT) + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. + float dist = m.pos * position.p2 + position.radius1; + + if(dist < look_ahead) + { + float vm = m.vel.length(); + pVector Vn = m.vel / vm; + // float dot = Vn * position.p2; + + pVector tmp = (position.p2 * (magdt / (dist*dist+epsilon))) + Vn; + m.vel = tmp * (vm / tmp.length()); + } + } + } + else + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. + float dist = m.pos * position.p2 + position.radius1; + + float vm = m.vel.length(); + pVector Vn = m.vel / vm; + // float dot = Vn * position.p2; + + pVector tmp = (position.p2 * (magdt / (dist*dist+epsilon))) + Vn; + m.vel = tmp * (vm / tmp.length()); + } + } + } + break; + case PDRectangle: + { + // Compute the inverse matrix of the plane basis. + pVector &u = position.u; + pVector &v = position.v; + + // The normalized bases are needed inside the loop. + pVector un = u / position.radius1Sqr; + pVector vn = v / position.radius2Sqr; + + // w = u cross v + float wx = u.y*v.z-u.z*v.y; + float wy = u.z*v.x-u.x*v.z; + float wz = u.x*v.y-u.y*v.x; + + float det = 1/(wz*u.x*v.y-wz*u.y*v.x-u.z*wx*v.y-u.x*v.z*wy+v.z*wx*u.y+u.z*v.x*wy); + + pVector s1((v.y*wz-v.z*wy), (v.z*wx-v.x*wz), (v.x*wy-v.y*wx)); + s1 *= det; + pVector s2((u.y*wz-u.z*wy), (u.z*wx-u.x*wz), (u.x*wy-u.y*wx)); + s2 *= -det; + + // See which particles bounce. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // See if particle's current and next positions cross plane. + // If not, couldn't bounce, so keep going. + pVector pnext(m.pos + m.vel * dt * look_ahead); + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. + float distold = m.pos * position.p2 + position.radius1; + float distnew = pnext * position.p2 + position.radius1; + + // Opposite signs if product < 0 + // There is no faster way to do this. + if(distold * distnew >= 0) + continue; + + float nv = position.p2 * m.vel; + float t = -distold / nv; + + // Actual intersection point p(t) = pos + vel t + pVector phit(m.pos + m.vel * t); + + // Offset from origin in plane, p - origin + pVector offset(phit - position.p1); + + // Dot product with basis vectors of old frame + // in terms of new frame gives position in uv frame. + float upos = offset * s1; + float vpos = offset * s2; + + // Did it cross plane outside triangle? + if(upos < 0 || vpos < 0 || upos > 1 || vpos > 1) + continue; + + // A hit! A most palpable hit! + // Compute distance to the three edges. + pVector uofs = (un * (un * offset)) - offset; + float udistSqr = uofs.length2(); + pVector vofs = (vn * (vn * offset)) - offset; + float vdistSqr = vofs.length2(); + + pVector foffset((u + v) - offset); + pVector fofs = (un * (un * foffset)) - foffset; + float fdistSqr = fofs.length2(); + pVector gofs = (un * (un * foffset)) - foffset; + float gdistSqr = gofs.length2(); + + pVector S; + if(udistSqr <= vdistSqr && udistSqr <= fdistSqr + && udistSqr <= gdistSqr) S = uofs; + else if(vdistSqr <= fdistSqr && vdistSqr <= gdistSqr) S = vofs; + else if(fdistSqr <= gdistSqr) S = fofs; + else S = gofs; + + S.normalize(); + + // We now have a vector to safety. + float vm = m.vel.length(); + pVector Vn = m.vel / vm; + + // Blend S into V. + pVector tmp = (S * (magdt / (t*t+epsilon))) + Vn; + m.vel = tmp * (vm / tmp.length()); + } + } + break; + case PDTriangle: + { + // Compute the inverse matrix of the plane basis. + pVector &u = position.u; + pVector &v = position.v; + + // The normalized bases are needed inside the loop. + pVector un = u / position.radius1Sqr; + pVector vn = v / position.radius2Sqr; + + // f is the third (non-basis) triangle edge. + pVector f = v - u; + pVector fn(f); + fn.normalize(); + + // w = u cross v + float wx = u.y*v.z-u.z*v.y; + float wy = u.z*v.x-u.x*v.z; + float wz = u.x*v.y-u.y*v.x; + + float det = 1/(wz*u.x*v.y-wz*u.y*v.x-u.z*wx*v.y-u.x*v.z*wy+v.z*wx*u.y+u.z*v.x*wy); + + pVector s1((v.y*wz-v.z*wy), (v.z*wx-v.x*wz), (v.x*wy-v.y*wx)); + s1 *= det; + pVector s2((u.y*wz-u.z*wy), (u.z*wx-u.x*wz), (u.x*wy-u.y*wx)); + s2 *= -det; + + // See which particles bounce. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // See if particle's current and next positions cross plane. + // If not, couldn't bounce, so keep going. + pVector pnext(m.pos + m.vel * dt * look_ahead); + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. + float distold = m.pos * position.p2 + position.radius1; + float distnew = pnext * position.p2 + position.radius1; + + // Opposite signs if product < 0 + // Is there a faster way to do this? + if(distold * distnew >= 0) + continue; + + float nv = position.p2 * m.vel; + float t = -distold / nv; + + // Actual intersection point p(t) = pos + vel t + pVector phit(m.pos + m.vel * t); + + // Offset from origin in plane, p - origin + pVector offset(phit - position.p1); + + // Dot product with basis vectors of old frame + // in terms of new frame gives position in uv frame. + float upos = offset * s1; + float vpos = offset * s2; + + // Did it cross plane outside triangle? + if(upos < 0 || vpos < 0 || (upos + vpos) > 1) + continue; + + // A hit! A most palpable hit! + // Compute distance to the three edges. + pVector uofs = (un * (un * offset)) - offset; + float udistSqr = uofs.length2(); + pVector vofs = (vn * (vn * offset)) - offset; + float vdistSqr = vofs.length2(); + pVector foffset(offset - u); + pVector fofs = (fn * (fn * foffset)) - foffset; + float fdistSqr = fofs.length2(); + pVector S; + if(udistSqr <= vdistSqr && udistSqr <= fdistSqr) S = uofs; + else if(vdistSqr <= fdistSqr) S = vofs; + else S = fofs; + + S.normalize(); + + // We now have a vector to safety. + float vm = m.vel.length(); + pVector Vn = m.vel / vm; + + // Blend S into V. + pVector tmp = (S * (magdt / (t*t+epsilon))) + Vn; + m.vel = tmp * (vm / tmp.length()); + } + } + break; + case PDDisc: + { + float r1Sqr = fsqr(position.radius1); + float r2Sqr = fsqr(position.radius2); + + // See which particles bounce. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // See if particle's current and next positions cross plane. + // If not, couldn't bounce, so keep going. + pVector pnext(m.pos + m.vel * dt * look_ahead); + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. radius1Sqr stores d. + float distold = m.pos * position.p2 + position.radius1Sqr; + float distnew = pnext * position.p2 + position.radius1Sqr; + + // Opposite signs if product < 0 + // Is there a faster way to do this? + if(distold * distnew >= 0) + continue; + + // Find position at the crossing point by parameterizing + // p(t) = pos + vel * t + // Solve dist(p(t),plane) = 0 e.g. + // n * p(t) + D = 0 -> + // n * p + t (n * v) + D = 0 -> + // t = -(n * p + D) / (n * v) + // Could factor n*v into distnew = distold + n*v and save a bit. + // Safe since n*v != 0 assured by quick rejection test. + // This calc is indep. of dt because we have established that it + // will hit before dt. We just want to know when. + float nv = position.p2 * m.vel; + float t = -distold / nv; + + // Actual intersection point p(t) = pos + vel t + pVector phit(m.pos + m.vel * t); + + // Offset from origin in plane, phit - origin + pVector offset(phit - position.p1); + + float rad = offset.length2(); + + if(rad > r1Sqr || rad < r2Sqr) + continue; + + // A hit! A most palpable hit! + pVector S = offset; + S.normalize(); + + // We now have a vector to safety. + float vm = m.vel.length(); + pVector Vn = m.vel / vm; + + // Blend S into V. + pVector tmp = (S * (magdt / (t*t+epsilon))) + Vn; + m.vel = tmp * (vm / tmp.length()); + } + } + break; + case PDSphere: + { + float rSqr = position.radius1 * position.radius1; + + // See which particles are aimed toward the sphere. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // First do a ray-sphere intersection test and + // see if it's soon enough. + // Can I do this faster without t? + float vm = m.vel.length(); + pVector Vn = m.vel / vm; + + pVector L = position.p1 - m.pos; + float v = L * Vn; + + float disc = rSqr - (L * L) + v * v; + if(disc < 0) + continue; // I'm not heading toward it. + + // Compute length for second rejection test. + float t = v - sqrtf(disc); + if(t < 0 || t > (vm * look_ahead)) + continue; + + // Get a vector to safety. + pVector C = Vn ^ L; + C.normalize(); + pVector S = Vn ^ C; + + // Blend S into V. + pVector tmp = (S * (magdt / (t*t+epsilon))) + Vn; + m.vel = tmp * (vm / tmp.length()); + } + } + break; + } +} + +void PABounce::Execute(ParticleGroup *group) +{ + switch(position.type) + { + case PDTriangle: + { + // Compute the inverse matrix of the plane basis. + pVector &u = position.u; + pVector &v = position.v; + + // w = u cross v + float wx = u.y*v.z-u.z*v.y; + float wy = u.z*v.x-u.x*v.z; + float wz = u.x*v.y-u.y*v.x; + + float det = 1/(wz*u.x*v.y-wz*u.y*v.x-u.z*wx*v.y-u.x*v.z*wy+v.z*wx*u.y+u.z*v.x*wy); + + pVector s1((v.y*wz-v.z*wy), (v.z*wx-v.x*wz), (v.x*wy-v.y*wx)); + s1 *= det; + pVector s2((u.y*wz-u.z*wy), (u.z*wx-u.x*wz), (u.x*wy-u.y*wx)); + s2 *= -det; + + // See which particles bounce. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // See if particle's current and next positions cross plane. + // If not, couldn't bounce, so keep going. + pVector pnext(m.pos + m.vel * dt); + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. + float distold = m.pos * position.p2 + position.radius1; + float distnew = pnext * position.p2 + position.radius1; + + // Opposite signs if product < 0 + // Is there a faster way to do this? + if(distold * distnew >= 0) + continue; + + // Find position at the crossing point by parameterizing + // p(t) = pos + vel * t + // Solve dist(p(t),plane) = 0 e.g. + // n * p(t) + D = 0 -> + // n * p + t (n * v) + D = 0 -> + // t = -(n * p + D) / (n * v) + // Could factor n*v into distnew = distold + n*v and save a bit. + // Safe since n*v != 0 assured by quick rejection test. + // This calc is indep. of dt because we have established that it + // will hit before dt. We just want to know when. + float nv = position.p2 * m.vel; + float t = -distold / nv; + + // Actual intersection point p(t) = pos + vel t + pVector phit(m.pos + m.vel * t); + + // Offset from origin in plane, p - origin + pVector offset(phit - position.p1); + + // Dot product with basis vectors of old frame + // in terms of new frame gives position in uv frame. + float upos = offset * s1; + float vpos = offset * s2; + + // Did it cross plane outside triangle? + if(upos < 0 || vpos < 0 || (upos + vpos) > 1) + continue; + + // A hit! A most palpable hit! + + // Compute tangential and normal components of velocity + pVector vn(position.p2 * nv); // Normal Vn = (V.N)N + pVector vt(m.vel - vn); // Tangent Vt = V - Vn + + // Compute new velocity heading out: + // Don't apply friction if tangential velocity < cutoff + if(vt.length2() <= cutoffSqr) + m.vel = vt - vn * resilience; + else + m.vel = vt * oneMinusFriction - vn * resilience; + } + } + break; + case PDDisc: + { + float r1Sqr = fsqr(position.radius1); + float r2Sqr = fsqr(position.radius2); + + // See which particles bounce. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // See if particle's current and next positions cross plane. + // If not, couldn't bounce, so keep going. + pVector pnext(m.pos + m.vel * dt); + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. radius1Sqr stores d. + float distold = m.pos * position.p2 + position.radius1Sqr; + float distnew = pnext * position.p2 + position.radius1Sqr; + + // Opposite signs if product < 0 + // Is there a faster way to do this? + if(distold * distnew >= 0) + continue; + + // Find position at the crossing point by parameterizing + // p(t) = pos + vel * t + // Solve dist(p(t),plane) = 0 e.g. + // n * p(t) + D = 0 -> + // n * p + t (n * v) + D = 0 -> + // t = -(n * p + D) / (n * v) + // Could factor n*v into distnew = distold + n*v and save a bit. + // Safe since n*v != 0 assured by quick rejection test. + // This calc is indep. of dt because we have established that it + // will hit before dt. We just want to know when. + float nv = position.p2 * m.vel; + float t = -distold / nv; + + // Actual intersection point p(t) = pos + vel t + pVector phit(m.pos + m.vel * t); + + // Offset from origin in plane, phit - origin + pVector offset(phit - position.p1); + + float rad = offset.length2(); + + if(rad > r1Sqr || rad < r2Sqr) + continue; + + // A hit! A most palpable hit! + + // Compute tangential and normal components of velocity + pVector vn(position.p2 * nv); // Normal Vn = (V.N)N + pVector vt(m.vel - vn); // Tangent Vt = V - Vn + + // Compute new velocity heading out: + // Don't apply friction if tangential velocity < cutoff + if(vt.length2() <= cutoffSqr) + m.vel = vt - vn * resilience; + else + m.vel = vt * oneMinusFriction - vn * resilience; + } + } + break; + case PDPlane: + { + // See which particles bounce. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // See if particle's current and next positions cross plane. + // If not, couldn't bounce, so keep going. + pVector pnext(m.pos + m.vel * dt); + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. + float distold = m.pos * position.p2 + position.radius1; + float distnew = pnext * position.p2 + position.radius1; + + // Opposite signs if product < 0 + if(distold * distnew >= 0) + continue; + + // Compute tangential and normal components of velocity + float nmag = m.vel * position.p2; + pVector vn(position.p2 * nmag); // Normal Vn = (V.N)N + pVector vt(m.vel - vn); // Tangent Vt = V - Vn + + // Compute new velocity heading out: + // Don't apply friction if tangential velocity < cutoff + if(vt.length2() <= cutoffSqr) + m.vel = vt - vn * resilience; + else + m.vel = vt * oneMinusFriction - vn * resilience; + } + } + break; + case PDRectangle: + { + // Compute the inverse matrix of the plane basis. + pVector &u = position.u; + pVector &v = position.v; + + // w = u cross v + float wx = u.y*v.z-u.z*v.y; + float wy = u.z*v.x-u.x*v.z; + float wz = u.x*v.y-u.y*v.x; + + float det = 1/(wz*u.x*v.y-wz*u.y*v.x-u.z*wx*v.y-u.x*v.z*wy+v.z*wx*u.y+u.z*v.x*wy); + + pVector s1((v.y*wz-v.z*wy), (v.z*wx-v.x*wz), (v.x*wy-v.y*wx)); + s1 *= det; + pVector s2((u.y*wz-u.z*wy), (u.z*wx-u.x*wz), (u.x*wy-u.y*wx)); + s2 *= -det; + + // See which particles bounce. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // See if particle's current and next positions cross plane. + // If not, couldn't bounce, so keep going. + pVector pnext(m.pos + m.vel * dt); + + // p2 stores the plane normal (the a,b,c of the plane eqn). + // Old and new distances: dist(p,plane) = n * p + d + // radius1 stores -n*p, which is d. + float distold = m.pos * position.p2 + position.radius1; + float distnew = pnext * position.p2 + position.radius1; + + // Opposite signs if product < 0 + if(distold * distnew >= 0) + continue; + + // Find position at the crossing point by parameterizing + // p(t) = pos + vel * t + // Solve dist(p(t),plane) = 0 e.g. + // n * p(t) + D = 0 -> + // n * p + t (n * v) + D = 0 -> + // t = -(n * p + D) / (n * v) + float t = -distold / (position.p2 * m.vel); + + // Actual intersection point p(t) = pos + vel t + pVector phit(m.pos + m.vel * t); + + // Offset from origin in plane, p - origin + pVector offset(phit - position.p1); + + // Dot product with basis vectors of old frame + // in terms of new frame gives position in uv frame. + float upos = offset * s1; + float vpos = offset * s2; + + // Crossed plane outside bounce region if !(0<=[uv]pos<=1) + if(upos < 0 || upos > 1 || vpos < 0 || vpos > 1) + continue; + + // A hit! A most palpable hit! + + // Compute tangential and normal components of velocity + float nmag = m.vel * position.p2; + pVector vn(position.p2 * nmag); // Normal Vn = (V.N)N + pVector vt(m.vel - vn); // Tangent Vt = V - Vn + + // Compute new velocity heading out: + // Don't apply friction if tangential velocity < cutoff + if(vt.length2() <= cutoffSqr) + m.vel = vt - vn * resilience; + else + m.vel = vt * oneMinusFriction - vn * resilience; + } + } + break; + case PDSphere: + { + // Sphere that particles bounce off + // The particles are always forced out of the sphere. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // See if particle's next position is inside domain. + // If so, bounce it. + pVector pnext(m.pos + m.vel * dt); + + if(position.Within(pnext)) + { + // See if we were inside on previous timestep. + bool pinside = position.Within(m.pos); + + // Normal to surface. This works for a sphere. Isn't + // computed quite right, should extrapolate particle + // position to surface. + pVector n(m.pos - position.p1); + n.normalize(); + + // Compute tangential and normal components of velocity + float nmag = m.vel * n; + + pVector vn(n * nmag); // Normal Vn = (V.N)N + pVector vt = m.vel - vn; // Tangent Vt = V - Vn + + if(pinside) + { + // Previous position was inside. If normal component of + // velocity points in, reverse it. This effectively + // repels particles which would otherwise be trapped + // in the sphere. + if(nmag < 0) + m.vel = vt - vn; + } + else + { + // Previous position was outside -> particle will cross + // surface boundary. Reverse normal component of velocity, + // and apply friction (if Vt >= cutoff) and resilience. + + // Compute new velocity heading out: + // Don't apply friction if tangential velocity < cutoff + if(vt.length2() <= cutoffSqr) + m.vel = vt - vn * resilience; + else + m.vel = vt * oneMinusFriction - vn * resilience; + } + } + } + } + } +} + +// Set the secondary position of each particle to be its position. +void PACallActionList::Execute(ParticleGroup *group) +{ + pCallActionList(action_list_num); +} + +// Set the secondary position of each particle to be its position. +void PACopyVertexB::Execute(ParticleGroup *group) +{ + int i; + + if(copy_pos) + { + for(i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + m.posB = m.pos; + } + } + + if(copy_vel) + { + for(i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + m.velB = m.vel; + } + } +} + +// Dampen velocities +void PADamping::Execute(ParticleGroup *group) +{ + // This is important if dt is != 1. + pVector one(1,1,1); + pVector scale(one - ((one - damping) * dt)); + + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + float vSqr = m.vel.length2(); + + if(vSqr >= vlowSqr && vSqr <= vhighSqr) + { + m.vel.x *= scale.x; + m.vel.y *= scale.y; + m.vel.z *= scale.z; + } + } +} + +// Exert force on each particle away from explosion center +void PAExplosion::Execute(ParticleGroup *group) +{ + float radius = velocity * age; + float magdt = magnitude * dt; + float oneOverSigma = 1.0f / stdev; + float inexp = -0.5f*fsqr(oneOverSigma); + float outexp = ONEOVERSQRT2PI * oneOverSigma; + + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Figure direction to particle. + pVector dir(m.pos - center); + float distSqr = dir.length2(); + float dist = sqrtf(distSqr); + float DistFromWaveSqr = fsqr(radius - dist); + + float Gd = exp(DistFromWaveSqr * inexp) * outexp; + + m.vel += dir * (Gd * magdt / (dist * (distSqr + epsilon))); + } + + age += dt; +} + +// Follow the next particle in the list +void PAFollow::Execute(ParticleGroup *group) +{ + float magdt = magnitude * dt; + float max_radiusSqr = max_radius * max_radius; + + if(max_radiusSqr < P_MAXFLOAT) + { + for(int i = 0; i < group->p_count - 1; i++) + { + Particle &m = group->list[i]; + + // Accelerate toward the particle after me in the list. + pVector tohim(group->list[i+1].pos - m.pos); // tohim = p1 - p0 + float tohimlenSqr = tohim.length2(); + + if(tohimlenSqr < max_radiusSqr) + { + // Compute force exerted between the two bodies + m.vel += tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon))); + } + } + } + else + { + for(int i = 0; i < group->p_count - 1; i++) + { + Particle &m = group->list[i]; + + // Accelerate toward the particle after me in the list. + pVector tohim(group->list[i+1].pos - m.pos); // tohim = p1 - p0 + float tohimlenSqr = tohim.length2(); + + // Compute force exerted between the two bodies + m.vel += tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon))); + } + } +} + +// Inter-particle gravitation +void PAGravitate::Execute(ParticleGroup *group) +{ + float magdt = magnitude * dt; + float max_radiusSqr = max_radius * max_radius; + + if(max_radiusSqr < P_MAXFLOAT) + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Add interactions with other particles + for(int j = i + 1; j < group->p_count; j++) + { + Particle &mj = group->list[j]; + + pVector tohim(mj.pos - m.pos); // tohim = p1 - p0 + float tohimlenSqr = tohim.length2(); + + if(tohimlenSqr < max_radiusSqr) + { + // Compute force exerted between the two bodies + pVector acc(tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon)))); + + m.vel += acc; + mj.vel -= acc; + } + } + } + } + else + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Add interactions with other particles + for(int j = i + 1; j < group->p_count; j++) + { + Particle &mj = group->list[j]; + + pVector tohim(mj.pos - m.pos); // tohim = p1 - p0 + float tohimlenSqr = tohim.length2(); + + // Compute force exerted between the two bodies + pVector acc(tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon)))); + + m.vel += acc; + mj.vel -= acc; + } + } + } +} + +// Acceleration in a constant direction +void PAGravity::Execute(ParticleGroup *group) +{ + pVector ddir(direction * dt); + + for(int i = 0; i < group->p_count; i++) + { + // Step velocity with acceleration + group->list[i].vel += ddir; + } +} + +// Accelerate particles along a line +void PAJet::Execute(ParticleGroup *group) +{ + float magdt = magnitude * dt; + float max_radiusSqr = max_radius * max_radius; + + if(max_radiusSqr < P_MAXFLOAT) + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Figure direction to particle. + pVector dir(m.pos - center); + + // Distance to jet (force drops as 1/r^2) + // Soften by epsilon to avoid tight encounters to infinity + float rSqr = dir.length2(); + + if(rSqr < max_radiusSqr) + { + pVector accel; + acc.Generate(accel); + + // Step velocity with acceleration + m.vel += accel * (magdt / (rSqr + epsilon)); + } + } + } + else + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Figure direction to particle. + pVector dir(m.pos - center); + + // Distance to jet (force drops as 1/r^2) + // Soften by epsilon to avoid tight encounters to infinity + float rSqr = dir.length2(); + + pVector accel; + acc.Generate(accel); + + // Step velocity with acceleration + m.vel += accel * (magdt / (rSqr + epsilon)); + } + } +} + +// Get rid of older particles +void PAKillOld::Execute(ParticleGroup *group) +{ + // Must traverse list in reverse order so Remove will work + for(int i = group->p_count-1; i >= 0; i--) + { + Particle &m = group->list[i]; + + if(!((m.age < age_limit) ^ kill_less_than)) + group->Remove(i); + } +} + +// Match velocity to near neighbors +void PAMatchVelocity::Execute(ParticleGroup *group) +{ + float magdt = magnitude * dt; + float max_radiusSqr = max_radius * max_radius; + + if(max_radiusSqr < P_MAXFLOAT) + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Add interactions with other particles + for(int j = i + 1; j < group->p_count; j++) + { + Particle &mj = group->list[j]; + + pVector tohim(mj.pos - m.pos); // tohim = p1 - p0 + float tohimlenSqr = tohim.length2(); + + if(tohimlenSqr < max_radiusSqr) + { + // Compute force exerted between the two bodies + pVector acc(mj.vel * (magdt / (tohimlenSqr + epsilon))); + + m.vel += acc; + mj.vel -= acc; + } + } + } + } + else + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Add interactions with other particles + for(int j = i + 1; j < group->p_count; j++) + { + Particle &mj = group->list[j]; + + pVector tohim(mj.pos - m.pos); // tohim = p1 - p0 + float tohimlenSqr = tohim.length2(); + + // Compute force exerted between the two bodies + pVector acc(mj.vel * (magdt / (tohimlenSqr + epsilon))); + + m.vel += acc; + mj.vel -= acc; + } + } + } +} + +void PAMove::Execute(ParticleGroup *group) +{ + // Step particle positions forward by dt, and age the particles. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + m.age += dt; + m.pos += m.vel * dt; + } +} + +// Accelerate particles towards a line +void PAOrbitLine::Execute(ParticleGroup *group) +{ + float magdt = magnitude * dt; + float max_radiusSqr = max_radius * max_radius; + + if(max_radiusSqr < P_MAXFLOAT) + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Figure direction to particle from base of line. + pVector f(m.pos - p); + + pVector w(axis * (f * axis)); + + // Direction from particle to nearest point on line. + pVector into = w - f; + + // Distance to line (force drops as 1/r^2, normalize by 1/r) + // Soften by epsilon to avoid tight encounters to infinity + float rSqr = into.length2(); + + if(rSqr < max_radiusSqr) + // Step velocity with acceleration + m.vel += into * (magdt / (sqrtf(rSqr) + (rSqr + epsilon))); + } + } + else + { + // Removed because it causes pipeline stalls. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Figure direction to particle from base of line. + pVector f(m.pos - p); + + pVector w(axis * (f * axis)); + + // Direction from particle to nearest point on line. + pVector into = w - f; + + // Distance to line (force drops as 1/r^2, normalize by 1/r) + // Soften by epsilon to avoid tight encounters to infinity + float rSqr = into.length2(); + + // Step velocity with acceleration + m.vel += into * (magdt / (sqrtf(rSqr) + (rSqr + epsilon))); + } + } +} + +// Accelerate particles towards a point +void PAOrbitPoint::Execute(ParticleGroup *group) +{ + float magdt = magnitude * dt; + float max_radiusSqr = max_radius * max_radius; + + if(max_radiusSqr < P_MAXFLOAT) + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Figure direction to particle. + pVector dir(center - m.pos); + + // Distance to gravity well (force drops as 1/r^2, normalize by 1/r) + // Soften by epsilon to avoid tight encounters to infinity + float rSqr = dir.length2(); + + // Step velocity with acceleration + if(rSqr < max_radiusSqr) + m.vel += dir * (magdt / (sqrtf(rSqr) + (rSqr + epsilon))); + } + } + else + { + // Avoids pipeline stalls. + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Figure direction to particle. + pVector dir(center - m.pos); + + // Distance to gravity well (force drops as 1/r^2, normalize by 1/r) + // Soften by epsilon to avoid tight encounters to infinity + float rSqr = dir.length2(); + + // Step velocity with acceleration + m.vel += dir * (magdt / (sqrtf(rSqr) + (rSqr + epsilon))); + } + } +} + +// Accelerate in random direction each time step +void PARandomAccel::Execute(ParticleGroup *group) +{ + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + pVector acceleration; + gen_acc.Generate(acceleration); + + // dt will affect this by making a higher probability of + // being near the original velocity after unit time. Smaller + // dt approach a normal distribution instead of a square wave. + m.vel += acceleration * dt; + } +} + +// Immediately displace position randomly +void PARandomDisplace::Execute(ParticleGroup *group) +{ + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + pVector displacement; + gen_disp.Generate(displacement); + + // dt will affect this by making a higher probability of + // being near the original position after unit time. Smaller + // dt approach a normal distribution instead of a square wave. + m.pos += displacement * dt; + } +} + +// Immediately assign a random velocity +void PARandomVelocity::Execute(ParticleGroup *group) +{ + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + pVector velocity; + gen_vel.Generate(velocity); + + // Shouldn't multiply by dt because velocities are + // invariant of dt. How should dt affect this? + m.vel = velocity; + } +} + +#if 0 +// Produce coefficients of a velocity function v(t)=at^2 + bt + c +// satisfying initial x(0)=x0,v(0)=v0 and desired x(t)=xf,v(t)=vf, +// where x = x(0) + integrate(v(T),0,t) +static inline void _pconstrain(float x0, float v0, float xf, float vf, + float t, float *a, float *b, float *c) +{ + *c = v0; + *b = 2 * (-t*vf - 2*t*v0 + 3*xf - 3*x0) / (t * t); + *a = 3 * (t*vf + t*v0 - 2*xf + 2*x0) / (t * t * t); +} +#endif + +// Over time, restore particles to initial positions +// Put all particles on the surface of a statue, explode the statue, +// and then suck the particles back to the original position. Cool! +void PARestore::Execute(ParticleGroup *group) +{ + if(time_left <= 0) + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Already constrained, keep it there. + m.pos = m.posB; + m.vel = pVector(0,0,0); + } + } + else + { + float t = time_left; + float dtSqr = dt * dt; + float tSqrInv2dt = dt * 2.0f / (t * t); + float tCubInv3dtSqr = dtSqr * 3.0f / (t * t * t); + + for(int i = 0; i < group->p_count; i++) + { +#if 1 + Particle &m = group->list[i]; + + // Solve for a desired-behavior velocity function in each axis + // _pconstrain(m.pos.x, m.vel.x, m.posB.x, 0., timeLeft, &a, &b, &c); + + // Figure new velocity at next timestep + // m.vel.x = a * dtSqr + b * dt + c; + + float b = (-2*t*m.vel.x + 3*m.posB.x - 3*m.pos.x) * tSqrInv2dt; + float a = (t*m.vel.x - m.posB.x - m.posB.x + m.pos.x + m.pos.x) * tCubInv3dtSqr; + + // Figure new velocity at next timestep + m.vel.x += a + b; + + b = (-2*t*m.vel.y + 3*m.posB.y - 3*m.pos.y) * tSqrInv2dt; + a = (t*m.vel.y - m.posB.y - m.posB.y + m.pos.y + m.pos.y) * tCubInv3dtSqr; + + // Figure new velocity at next timestep + m.vel.y += a + b; + + b = (-2*t*m.vel.z + 3*m.posB.z - 3*m.pos.z) * tSqrInv2dt; + a = (t*m.vel.z - m.posB.z - m.posB.z + m.pos.z + m.pos.z) * tCubInv3dtSqr; + + // Figure new velocity at next timestep + m.vel.z += a + b; +#else + Particle &m = group->list[i]; + + // XXX Optimize this. + // Solve for a desired-behavior velocity function in each axis + float a, b, c; // Coefficients of velocity function needed + + _pconstrain(m.pos.x, m.vel.x, m.posB.x, 0., + timeLeft, &a, &b, &c); + + // Figure new velocity at next timestep + m.vel.x = a * dtSqr + b * dt + c; + + _pconstrain(m.pos.y, m.vel.y, m.posB.y, 0., + timeLeft, &a, &b, &c); + + // Figure new velocity at next timestep + m.vel.y = a * dtSqr + b * dt + c; + + _pconstrain(m.pos.z, m.vel.z, m.posB.z, 0., + timeLeft, &a, &b, &c); + + // Figure new velocity at next timestep + m.vel.z = a * dtSqr + b * dt + c; + +#endif + } + } + + time_left -= dt; +} + +// Kill particles with positions on wrong side of the specified domain +void PASink::Execute(ParticleGroup *group) +{ + // Must traverse list in reverse order so Remove will work + for(int i = group->p_count-1; i >= 0; i--) + { + Particle &m = group->list[i]; + + // Remove if inside/outside flag matches object's flag + if(!(position.Within(m.pos) ^ kill_inside)) + group->Remove(i); + } +} + +// Kill particles with velocities on wrong side of the specified domain +void PASinkVelocity::Execute(ParticleGroup *group) +{ + // Must traverse list in reverse order so Remove will work + for(int i = group->p_count-1; i >= 0; i--) + { + Particle &m = group->list[i]; + + // Remove if inside/outside flag matches object's flag + if(!(velocity.Within(m.vel) ^ kill_inside)) + group->Remove(i); + } +} + +// Randomly add particles to the system +void PASource::Execute(ParticleGroup *group) +{ + int rate = int(floor(particle_rate * dt)); + + // Dither the fraction particle in time. + if(drand48() < particle_rate * dt - float(rate)) + rate++; + + // Don't emit more than it can hold. + if(group->p_count + rate > group->max_particles) + rate = group->max_particles - group->p_count; + + pVector pos, posB, vel, col, siz; + + if(vertexB_tracks) + { + for(int i = 0; i < rate; i++) + { + position.Generate(pos); + size.Generate(siz); + velocity.Generate(vel); + color.Generate(col); + float ag = age + NRand(age_sigma); + + group->Add(pos, pos, siz, vel, col, alpha, ag); + } + } + else + { + for(int i = 0; i < rate; i++) + { + position.Generate(pos); + positionB.Generate(posB); + size.Generate(siz); + velocity.Generate(vel); + color.Generate(col); + float ag = age + NRand(age_sigma); + + group->Add(pos, posB, siz, vel, col, alpha, ag); + } + } +} + +void PASpeedLimit::Execute(ParticleGroup *group) +{ + float min_sqr = min_speed*min_speed; + float max_sqr = max_speed*max_speed; + + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + float sSqr = m.vel.length2(); + if(sSqrmax_sqr) + { + float s = sqrtf(sSqr); + m.vel *= (max_speed/s); + } + } +} + +// Change color of all particles toward the specified color +void PATargetColor::Execute(ParticleGroup *group) +{ + float scaleFac = scale * dt; + + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + m.color += (color - m.color) * scaleFac; + m.alpha += (alpha - m.alpha) * scaleFac; + } +} + +// Change sizes of all particles toward the specified size +void PATargetSize::Execute(ParticleGroup *group) +{ + float scaleFac_x = scale.x * dt; + float scaleFac_y = scale.y * dt; + float scaleFac_z = scale.z * dt; + + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + pVector dif(size - m.size); + dif.x *= scaleFac_x; + dif.y *= scaleFac_y; + dif.z *= scaleFac_z; + m.size += dif; + } +} + +// Change velocity of all particles toward the specified velocity +void PATargetVelocity::Execute(ParticleGroup *group) +{ + float scaleFac = scale * dt; + + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + m.vel += (velocity - m.vel) * scaleFac; + } +} + +// Immediately displace position using vortex +// Vortex tip at center, around axis, with magnitude +// and tightness exponent +void PAVortex::Execute(ParticleGroup *group) +{ + float magdt = magnitude * dt; + float max_radiusSqr = max_radius * max_radius; + + if(max_radiusSqr < P_MAXFLOAT) + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Vector from tip of vortex + pVector offset(m.pos - center); + + // Compute distance from particle to tip of vortex. + float rSqr = offset.length2(); + + // Don't do anything to particle if too close or too far. + if(rSqr > max_radiusSqr) + continue; + + float r = sqrtf(rSqr); + + // Compute normalized offset vector. + pVector offnorm(offset / r); + + // Construct orthogonal vector frame in which to rotate + // transformed point around origin + float axisProj = offnorm * axis; // offnorm . axis + + // Components of offset perpendicular and parallel to axis + pVector w(axis * axisProj); // parallel component + pVector u(offnorm - w); // perpendicular component + + // Perpendicular component completing frame: + pVector v(axis ^ u); + + // Figure amount of rotation + // Resultant is (cos theta) u + (sin theta) v + float theta = magdt / (rSqr + epsilon); + float s = sinf(theta); + float c = cosf(theta); + + offset = (u * c + v * s + w) * r; + + // Translate back to object space + m.pos = offset + center; + } + } + else + { + for(int i = 0; i < group->p_count; i++) + { + Particle &m = group->list[i]; + + // Vector from tip of vortex + pVector offset(m.pos - center); + + // Compute distance from particle to tip of vortex. + float rSqr = offset.length2(); + + float r = sqrtf(rSqr); + + // Compute normalized offset vector. + pVector offnorm(offset / r); + + // Construct orthogonal vector frame in which to rotate + // transformed point around origin + float axisProj = offnorm * axis; // offnorm . axis + + // Components of offset perpendicular and parallel to axis + pVector w(axis * axisProj); // parallel component + pVector u(offnorm - w); // perpendicular component + + // Perpendicular component completing frame: + pVector v(axis ^ u); + + // Figure amount of rotation + // Resultant is (cos theta) u + (sin theta) v + float theta = magdt / (rSqr + epsilon); + float s = sinf(theta); + float c = cosf(theta); + + offset = (u * c + v * s + w) * r; + + // Translate back to object space + m.pos = offset + center; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Stuff for the pDomain. + +pDomain::pDomain(PDomainEnum dtype, float a0, float a1, + float a2, float a3, float a4, float a5, + float a6, float a7, float a8) +{ + type = dtype; + switch(type) + { + case PDPoint: + p1 = pVector(a0, a1, a2); + break; + case PDLine: + { + p1 = pVector(a0, a1, a2); + pVector tmp(a3, a4, a5); + // p2 is vector from p1 to other endpoint. + p2 = tmp - p1; + } + break; + case PDBox: + // p1 is the min corner. p2 is the max corner. + if(a0 < a3) + { + p1.x = a0; p2.x = a3; + } + else + { + p1.x = a3; p2.x = a0; + } + if(a1 < a4) + { + p1.y = a1; p2.y = a4; + } + else + { + p1.y = a4; p2.y = a1; + } + if(a2 < a5) + { + p1.z = a2; p2.z = a5; + } + else + { + p1.z = a5; p2.z = a2; + } + break; + case PDTriangle: + { + p1 = pVector(a0, a1, a2); + pVector tp2 = pVector(a3, a4, a5); + pVector tp3 = pVector(a6, a7, a8); + + u = tp2 - p1; + v = tp3 - p1; + + // The rest of this is needed for bouncing. + radius1Sqr = u.length(); + pVector tu = u / radius1Sqr; + radius2Sqr = v.length(); + pVector tv = v / radius2Sqr; + + p2 = tu ^ tv; // This is the non-unit normal. + p2.normalize(); // Must normalize it. + + // radius1 stores the d of the plane eqn. + radius1 = -(p1 * p2); + } + break; + case PDRectangle: + { + p1 = pVector(a0, a1, a2); + u = pVector(a3, a4, a5); + v = pVector(a6, a7, a8); + + // The rest of this is needed for bouncing. + radius1Sqr = u.length(); + pVector tu = u / radius1Sqr; + radius2Sqr = v.length(); + pVector tv = v / radius2Sqr; + + p2 = tu ^ tv; // This is the non-unit normal. + p2.normalize(); // Must normalize it. + + // radius1 stores the d of the plane eqn. + radius1 = -(p1 * p2); + } + break; + case PDPlane: + { + p1 = pVector(a0, a1, a2); + p2 = pVector(a3, a4, a5); + p2.normalize(); // Must normalize it. + + // radius1 stores the d of the plane eqn. + radius1 = -(p1 * p2); + } + break; + case PDSphere: + p1 = pVector(a0, a1, a2); + if(a3 > a4) + { + radius1 = a3; radius2 = a4; + } + else + { + radius1 = a4; radius2 = a3; + } + radius1Sqr = radius1 * radius1; + radius2Sqr = radius2 * radius2; + break; + case PDCone: + case PDCylinder: + { + // p2 is a vector from p1 to the other end of cylinder. + // p1 is apex of cone. + + p1 = pVector(a0, a1, a2); + pVector tmp(a3, a4, a5); + p2 = tmp - p1; + + if(a6 > a7) + { + radius1 = a6; radius2 = a7; + } + else + { + radius1 = a7; radius2 = a6; + } + radius1Sqr = fsqr(radius1); + + // Given an arbitrary nonzero vector n, make two orthonormal + // vectors u and v forming a frame [u,v,n.normalize()]. + pVector n = p2; + float p2l2 = n.length2(); // Optimize this. + n.normalize(); + + // radius2Sqr stores 1 / (p2.p2) + // XXX Used to have an actual if. + radius2Sqr = p2l2 ? 1.0f / p2l2 : 0.0f; + + // Find a vector orthogonal to n. + pVector basis(1.0f, 0.0f, 0.0f); + if(fabs(basis * n) > 0.999) + basis = pVector(0.0f, 1.0f, 0.0f); + + // Project away N component, normalize and cross to get + // second orthonormal vector. + u = basis - n * (basis * n); + u.normalize(); + v = n ^ u; + } + break; + case PDBlob: + { + p1 = pVector(a0, a1, a2); + radius1 = a3; + float tmp = 1./radius1; + radius2Sqr = -0.5f*fsqr(tmp); + radius2 = ONEOVERSQRT2PI * tmp; + } + break; + case PDDisc: + { + p1 = pVector(a0, a1, a2); // Center point + p2 = pVector(a3, a4, a5); // Normal (not used in Within and Generate) + p2.normalize(); + + if(a6 > a7) + { + radius1 = a6; radius2 = a7; + } + else + { + radius1 = a7; radius2 = a6; + } + + // Find a vector orthogonal to n. + pVector basis(1.0f, 0.0f, 0.0f); + if(fabs(basis * p2) > 0.999) + basis = pVector(0.0f, 1.0f, 0.0f); + + // Project away N component, normalize and cross to get + // second orthonormal vector. + u = basis - p2 * (basis * p2); + u.normalize(); + v = p2 ^ u; + radius1Sqr = -(p1 * p2); // D of the plane eqn. + } + break; + } +} + +// Determines if pos is inside the domain +bool pDomain::Within(const pVector &pos) const +{ + switch (type) + { + case PDBox: + return !((pos.x < p1.x) || (pos.x > p2.x) || + (pos.y < p1.y) || (pos.y > p2.y) || + (pos.z < p1.z) || (pos.z > p2.z)); + case PDPlane: + // Distance from plane = n * p + d + // Inside is the positive half-space. + return pos * p2 >= -radius1; + case PDSphere: + { + pVector rvec(pos - p1); + float rSqr = rvec.length2(); + return rSqr <= radius1Sqr && rSqr >= radius2Sqr; + } + case PDCylinder: + case PDCone: + { + // This is painful and slow. Might be better to do quick + // accept/reject tests. + // Let p2 = vector from base to tip of the cylinder + // x = vector from base to test point + // x . p2 + // dist = ------ = projected distance of x along the axis + // p2. p2 ranging from 0 (base) to 1 (tip) + // + // rad = x - dist * p2 = projected vector of x along the base + // p1 is the apex of the cone. + + pVector x(pos - p1); + + // Check axial distance + // radius2Sqr stores 1 / (p2.p2) + float dist = (p2 * x) * radius2Sqr; + if(dist < 0.0f || dist > 1.0f) + return false; + + // Check radial distance; scale radius along axis for cones + pVector xrad = x - p2 * dist; // Radial component of x + float rSqr = xrad.length2(); + + if(type == PDCone) + return (rSqr <= fsqr(dist * radius1) && + rSqr >= fsqr(dist * radius2)); + else + return (rSqr <= radius1Sqr && rSqr >= fsqr(radius2)); + } + case PDBlob: + { + pVector x(pos - p1); + // return exp(-0.5 * xSq * Sqr(oneOverSigma)) * ONEOVERSQRT2PI * oneOverSigma; + float Gx = expf(x.length2() * radius2Sqr) * radius2; + return (drand48() < Gx); + } + case PDPoint: + case PDLine: + case PDRectangle: + case PDTriangle: + case PDDisc: + default: + return false; // XXX Is there something better? + } +} + +// Generate a random point uniformly distrbuted within the domain +void pDomain::Generate(pVector &pos) const +{ + switch (type) + { + case PDPoint: + pos = p1; + break; + case PDLine: + pos = p1 + p2 * drand48(); + break; + case PDBox: + // Scale and translate [0,1] random to fit box + pos.x = p1.x + (p2.x - p1.x) * drand48(); + pos.y = p1.y + (p2.y - p1.y) * drand48(); + pos.z = p1.z + (p2.z - p1.z) * drand48(); + break; + case PDTriangle: + { + float r1 = drand48(); + float r2 = drand48(); + if(r1 + r2 < 1.0f) + pos = p1 + u * r1 + v * r2; + else + pos = p1 + u * (1.0f-r1) + v * (1.0f-r2); + } + break; + case PDRectangle: + pos = p1 + u * drand48() + v * drand48(); + break; + case PDPlane: // How do I sensibly make a point on an infinite plane? + pos = p1; + break; + case PDSphere: + // Place on [-1..1] sphere + pos = RandVec() - vHalf; + pos.normalize(); + + // Scale unit sphere pos by [0..r] and translate + // (should distribute as r^2 law) + if(radius1 == radius2) + pos = p1 + pos * radius1; + else + pos = p1 + pos * (radius2 + drand48() * (radius1 - radius2)); + break; + case PDCylinder: + case PDCone: + { + // For a cone, p2 is the apex of the cone. + float dist = drand48(); // Distance between base and tip + float theta = drand48() * 2.0f * float(M_PI); // Angle around axis + // Distance from axis + float r = radius2 + drand48() * (radius1 - radius2); + + float x = r * cosf(theta); // Weighting of each frame vector + float y = r * sinf(theta); + + // Scale radius along axis for cones + if(type == PDCone) + { + x *= dist; + y *= dist; + } + + pos = p1 + p2 * dist + u * x + v * y; + } + break; + case PDBlob: + pos.x = p1.x + NRand(radius1); + pos.y = p1.y + NRand(radius1); + pos.z = p1.z + NRand(radius1); + + break; + case PDDisc: + { + float theta = drand48() * 2.0f * float(M_PI); // Angle around normal + // Distance from center + float r = radius2 + drand48() * (radius1 - radius2); + + float x = r * cosf(theta); // Weighting of each frame vector + float y = r * sinf(theta); + + pos = p1 + u * x + v * y; + } + break; + default: + pos = pVector(0,0,0); + } +} diff --git a/main/source/particle/general.h b/main/source/particle/general.h new file mode 100644 index 00000000..a4f661dc --- /dev/null +++ b/main/source/particle/general.h @@ -0,0 +1,452 @@ +// general.h +// +// Copyright 1998 by David K. McAllister. +// +// This file implements the API calls that are not particle actions. + +#ifndef general_h +#define general_h + +#include "papi.h" +#include "p_vector.h" + +#ifdef _WIN32 +#pragma warning (disable:4244) +#endif + +// A single particle +struct Particle +{ + pVector pos; + pVector posB; + pVector size; + pVector vel; + pVector velB; // Used to compute binormal, normal, etc. + pVector color; // Color must be next to alpha so glColor4fv works. + float alpha; // This is both cunning and scary. + float age; +}; + +// A group of particles - Info and an array of Particles +struct ParticleGroup +{ + int p_count; // Number of particles currently existing. + int max_particles; // Max particles allowed in group. + int particles_allocated; // Actual allocated size. + Particle list[1]; // Actually, num_particles in size + + inline void Remove(int i) + { + list[i] = list[--p_count]; + } + + inline bool Add(const pVector &pos, const pVector &posB, + const pVector &size, const pVector &vel, const pVector &color, + const float alpha = 1.0f, + const float age = 0.0f) + { + if(p_count >= max_particles) + return false; + else + { + list[p_count].pos = pos; + list[p_count].posB = posB; + list[p_count].size = size; + list[p_count].vel = vel; + list[p_count].velB = vel; // XXX This should be fixed. + list[p_count].color = color; + list[p_count].alpha = alpha; + list[p_count].age = age; + p_count++; + return true; + } + } +}; + +struct pDomain +{ + PDomainEnum type; // PABoxDomain, PASphereDomain, PAConeDomain... + pVector p1, p2; // Box vertices, Sphere center, Cylinder/Cone ends + pVector u, v; // Orthonormal basis vectors for Cylinder/Cone + float radius1; // Outer radius + float radius2; // Inner radius + float radius1Sqr; // Used for fast Within test of spheres, + float radius2Sqr; // and for mag. of u and v vectors for plane. + + bool Within(const pVector &) const; + void Generate(pVector &) const; + + // This constructor is used when default constructing a + // ParticleAction that has a pDomain. + inline pDomain() + { + } + + // Construct a domain in the standard way. + pDomain(PDomainEnum dtype, + float a0=0.0f, float a1=0.0f, float a2=0.0f, + float a3=0.0f, float a4=0.0f, float a5=0.0f, + float a6=0.0f, float a7=0.0f, float a8=0.0f); +}; + +////////////////////////////////////////////////////////////////////// +// Type codes for all actions +enum PActionEnum +{ + PAHeaderID, // The first action in each list. + PAAvoidID, // Avoid entering the domain of space. + PABounceID, // Bounce particles off a domain of space. + PACallActionListID, // + PACopyVertexBID, // Set the secondary position from current position. + PADampingID, // Dampen particle velocities. + PAExplosionID, // An Explosion. + PAFollowID, // Accelerate toward the previous particle in the group. + PAGravitateID, // Accelerate each particle toward each other particle. + PAGravityID, // Acceleration in the given direction. + PAJetID, // + PAKillOldID, // + PAMatchVelocityID, // + PAMoveID, // + PAOrbitLineID, // + PAOrbitPointID, // + PARandomAccelID, // + PARandomDisplaceID, // + PARandomVelocityID, // + PARestoreID, // + PASinkID, // + PASinkVelocityID, // + PASourceID, // + PASpeedLimitID, // + PATargetColorID, // + PATargetSizeID, // + PATargetVelocityID, // + PAVortexID // +}; + +// This method actually does the particle's action. +#define ExecMethod void Execute(ParticleGroup *pg); + +struct ParticleAction +{ + static float dt; // This is copied to here from global state. + PActionEnum type; // Type field +}; + +/////////////////////////////////////////////////////////////////////////// +// Data types derived from Action. + + +struct PAHeader : public ParticleAction +{ + int actions_allocated; + int count; // Total actions in the list. + float padding[96]; // This must be the largest action. + + ExecMethod +}; + +struct PAAvoid : public ParticleAction +{ + pDomain position; // Avoid region + float look_ahead; // how many time units ahead to look + float magnitude; // what percent of the way to go each time + float epsilon; // add to r^2 for softening + + ExecMethod +}; + +struct PABounce : public ParticleAction +{ + pDomain position; // Bounce region + float oneMinusFriction; // Friction tangent to surface + float resilience; // Resilence perpendicular to surface + float cutoffSqr; // cutoff velocity; friction applies iff v > cutoff + + ExecMethod +}; + +struct PACallActionList : public ParticleAction +{ + int action_list_num; // The action list number to call + + ExecMethod +}; + +struct PACopyVertexB : public ParticleAction +{ + bool copy_pos; // True to copy pos to posB. + bool copy_vel; // True to copy vel to velB. + + ExecMethod +}; + +struct PADamping : public ParticleAction +{ + pVector damping; // Damping constant applied to velocity + float vlowSqr; // Low and high cutoff velocities + float vhighSqr; + + ExecMethod +}; + +struct PAExplosion : public ParticleAction +{ + pVector center; // The center of the explosion + float velocity; // Of shock wave + float magnitude; // At unit radius + float stdev; // Sharpness or width of shock wave + float age; // How long it's been going on + float epsilon; // Softening parameter + + ExecMethod +}; + +struct PAFollow : public ParticleAction +{ + float magnitude; // The grav of each particle + float epsilon; // Softening parameter + float max_radius; // Only influence particles within max_radius + + ExecMethod +}; + +struct PAGravitate : public ParticleAction +{ + float magnitude; // The grav of each particle + float epsilon; // Softening parameter + float max_radius; // Only influence particles within max_radius + + ExecMethod +}; + +struct PAGravity : public ParticleAction +{ + pVector direction; // Amount to increment velocity + + ExecMethod +}; + +struct PAJet : public ParticleAction +{ + pVector center; // Center of the fan + pDomain acc; // Acceleration vector domain + float magnitude; // Scales acceleration + float epsilon; // Softening parameter + float max_radius; // Only influence particles within max_radius + + ExecMethod +}; + +struct PAKillOld : public ParticleAction +{ + float age_limit; // Exact age at which to kill particles. + bool kill_less_than; // True to kill particles less than limit. + + ExecMethod +}; + +struct PAMatchVelocity : public ParticleAction +{ + float magnitude; // The grav of each particle + float epsilon; // Softening parameter + float max_radius; // Only influence particles within max_radius + + ExecMethod +}; + +struct PAMove : public ParticleAction +{ + + ExecMethod +}; + +struct PAOrbitLine : public ParticleAction +{ + pVector p, axis; // Endpoints of line to which particles are attracted + float magnitude; // Scales acceleration + float epsilon; // Softening parameter + float max_radius; // Only influence particles within max_radius + + ExecMethod +}; + +struct PAOrbitPoint : public ParticleAction +{ + pVector center; // Point to which particles are attracted + float magnitude; // Scales acceleration + float epsilon; // Softening parameter + float max_radius; // Only influence particles within max_radius + + ExecMethod +}; + +struct PARandomAccel : public ParticleAction +{ + pDomain gen_acc; // The domain of random accelerations. + + ExecMethod +}; + +struct PARandomDisplace : public ParticleAction +{ + pDomain gen_disp; // The domain of random displacements. + + ExecMethod +}; + +struct PARandomVelocity : public ParticleAction +{ + pDomain gen_vel; // The domain of random velocities. + + ExecMethod +}; + +struct PARestore : public ParticleAction +{ + float time_left; // Time remaining until they should be in position. + + ExecMethod +}; + +struct PASink : public ParticleAction +{ + bool kill_inside; // True to dispose of particles *inside* domain + pDomain position; // Disposal region + + ExecMethod +}; + +struct PASinkVelocity : public ParticleAction +{ + bool kill_inside; // True to dispose of particles with vel *inside* domain + pDomain velocity; // Disposal region + + ExecMethod +}; + +struct PASpeedLimit : public ParticleAction +{ + float min_speed; // Clamp speed to this minimum. + float max_speed; // Clamp speed to this maximum. + + ExecMethod +}; + +struct PASource : public ParticleAction +{ + pDomain position; // Choose a position in this domain. + pDomain positionB; // Choose a positionB in this domain. + pDomain size; // Choose a size in this domain. + pDomain velocity; // Choose a velocity in this domain. + pDomain color; // Choose a color in this domain. + float alpha; // Alpha of all generated particles + float particle_rate; // Particles to generate per unit time + float age; // Initial age of the particles + float age_sigma; // St. dev. of initial age of the particles + bool vertexB_tracks; // True to get positionB from position. + + ExecMethod +}; + +struct PATargetColor : public ParticleAction +{ + pVector color; // Color to shift towards + float alpha; // Alpha value to shift towards + float scale; // Amount to shift by (1 == all the way) + + ExecMethod +}; + +struct PATargetSize : public ParticleAction +{ + pVector size; // Size to shift towards + pVector scale; // Amount to shift by per frame (1 == all the way) + + ExecMethod +}; + +struct PATargetVelocity : public ParticleAction +{ + pVector velocity; // Velocity to shift towards + float scale; // Amount to shift by (1 == all the way) + + ExecMethod +}; + +struct PAVortex : public ParticleAction +{ + pVector center; // Center of vortex + pVector axis; // Axis around which vortex is applied + float magnitude; // Scale for rotation around axis + float epsilon; // Softening parameter + float max_radius; // Only influence particles within max_radius + + ExecMethod +}; + +// Global state vector +struct _ParticleState +{ + float dt; + bool in_call_list; + bool in_new_list; + bool vertexB_tracks; + + int group_id; + int list_id; + ParticleGroup *pgrp; + PAHeader *pact; + int tid; // Only used in the MP case, but always define it. + + // These are static because all threads access the same groups. + // All accesses to these should be locked. + static ParticleGroup **group_list; + static PAHeader **alist_list; + static int group_count; + static int alist_count; + + pDomain Size; + pDomain Vel; + pDomain VertexB; + pDomain Color; + float Alpha; + float Age; + float AgeSigma; + + _ParticleState(); + + // Return an index into the list of particle groups where + // p_group_count groups can be added. + int GenerateGroups(int p_group_count); + int GenerateLists(int alist_count); + ParticleGroup *GetGroupPtr(int p_group_num); + PAHeader *GetListPtr(int action_list_num); +}; + +#ifdef PARTICLE_MP +// All entry points call this to get their particle state. +inline _ParticleState &_GetPState() +{ + // Returns a reference to the appropriate particle state. + extern _ParticleState &_GetPStateWithTID(); + + return _GetPStateWithTID(); +} + +#else + +// All entry points call this to get their particle state. +// For the non-MP case this is practically a no-op. +inline _ParticleState &_GetPState() +{ + // This is the global state. + extern _ParticleState __ps; + + return __ps; +} +#endif + +// Just a silly little function. +static inline float fsqr(float f) { return f * f; } + +#endif diff --git a/main/source/particle/libparticleMP.a b/main/source/particle/libparticleMP.a new file mode 100644 index 00000000..a84d4b2e Binary files /dev/null and b/main/source/particle/libparticleMP.a differ diff --git a/main/source/particle/opengl.cpp b/main/source/particle/opengl.cpp new file mode 100644 index 00000000..2f48a566 --- /dev/null +++ b/main/source/particle/opengl.cpp @@ -0,0 +1,149 @@ +// opengl.cpp +// +// Copyright 1998 by David K. McAllister +// +// This file implements the API calls that draw particle groups in OpenGL. + +#include "general.h" + +#ifdef WIN32 +// This is for something in gl.h. +#include +#endif + +#include +// XXX #include + +// Emit OpenGL calls to draw the particles. These are drawn with +// whatever primitive type the user specified(GL_POINTS, for +// example). The color and radius are set per primitive, by default. +// For GL_LINES, the other vertex of the line is the velocity vector. +// XXX const_size is ignored. +PARTICLEDLL_API void pDrawGroupp(int primitive, bool const_size, bool const_color) +{ + _ParticleState &_ps = _GetPState(); + + // Get a pointer to the particles in gp memory + ParticleGroup *pg = _ps.pgrp; + + if(pg == NULL) + return; // ERROR + + if(pg->p_count < 1) + return; + + if(primitive == GL_POINTS) + { + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); + glEnableClientState(GL_VERTEX_ARRAY); + if(!const_color) + { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, GL_FLOAT, sizeof(Particle), &pg->list[0].color); + } + + glVertexPointer(3, GL_FLOAT, sizeof(Particle), &pg->list[0].pos); + glDrawArrays((GLenum)primitive, 0, pg->p_count); + glPopClientAttrib(); + // XXX For E&S + glDisableClientState(GL_COLOR_ARRAY); + } + else + { + // Assume GL_LINES + glBegin((GLenum)primitive); + + if(!const_color) + { + for(int i = 0; i < pg->p_count; i++) + { + Particle &m = pg->list[i]; + + // Warning: this depends on alpha following color in the Particle struct. + glColor4fv((GLfloat *)&m.color); + glVertex3fv((GLfloat *)&m.pos); + + // For lines, make a tail with the velocity vector's direction and + // a length of radius. + pVector tail = m.pos - m.vel; + glVertex3fv((GLfloat *)&tail); + } + } + else + { + for(int i = 0; i < pg->p_count; i++) + { + Particle &m = pg->list[i]; + glVertex3fv((GLfloat *)&m.pos); + + // For lines, make a tail with the velocity vector's direction and + // a length of radius. + pVector tail = m.pos - m.vel; + glVertex3fv((GLfloat *)&tail); + } + } + glEnd(); + } +} + +PARTICLEDLL_API void pDrawGroupl(int dlist, bool const_size, bool const_color, bool const_rotation) +{ + _ParticleState &_ps = _GetPState(); + + // Get a pointer to the particles in gp memory + ParticleGroup *pg = _ps.pgrp; + if(pg == NULL) + return; // ERROR + + if(pg->p_count < 1) + return; + + //if(const_color) + // glColor4fv((GLfloat *)&pg->list[0].color); + + for(int i = 0; i < pg->p_count; i++) + { + Particle &m = pg->list[i]; + + glPushMatrix(); + glTranslatef(m.pos.x, m.pos.y, m.pos.z); + + if(!const_size) + glScalef(m.size.x, m.size.y, m.size.z); + else + glScalef(pg->list[i].size.x, pg->list[i].size.y, pg->list[i].size.z); + + // Expensive! A sqrt, cross prod and acos. Yow. + if(!const_rotation) + { + pVector vN(m.vel); + vN.normalize(); + pVector voN(m.velB); + voN.normalize(); + + pVector biN; + if(voN.x == vN.x && voN.y == vN.y && voN.z == vN.z) + biN = pVector(0, 1, 0); + else + biN = vN ^ voN; + biN.normalize(); + + pVector N(vN ^ biN); + + double M[16]; + M[0] = vN.x; M[4] = biN.x; M[8] = N.x; M[12] = 0; + M[1] = vN.y; M[5] = biN.y; M[9] = N.y; M[13] = 0; + M[2] = vN.z; M[6] = biN.z; M[10] = N.z; M[14] = 0; + M[3] = 0; M[7] = 0; M[11] = 0; M[15] = 1; + glMultMatrixd(M); + } + + // Warning: this depends on alpha following color in the Particle struct. + if(!const_color) + glColor4fv((GLfloat *)&m.color); + + glCallList(dlist); + + glPopMatrix(); + } +} diff --git a/main/source/particle/p_vector.h b/main/source/particle/p_vector.h new file mode 100644 index 00000000..45ef5d7b --- /dev/null +++ b/main/source/particle/p_vector.h @@ -0,0 +1,141 @@ +// p_vector.h - yet another vector class. +// +// Copyright 1997 by Jonathan P. Leech +// Modifications Copyright 1997-1999 by David K. McAllister +// +// A simple 3D float vector class for internal use by the particle systems. + +#ifndef particle_vector_h +#define particle_vector_h + +#include + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433f +#endif + +#ifdef WIN32 +#define drand48() (((float) rand())/((float) RAND_MAX)) +#define srand48(x) srand(x) + +// This is because their stupid compiler thinks it's smart. +#define inline __forceinline +#endif + +class pVector +{ +public: + float x, y, z; + + inline pVector(float ax, float ay, float az) : x(ax), y(ay), z(az) + { + //x = ax; y = ay; z = az; + } + + inline pVector() {} + + inline float length() const + { + return sqrtf(x*x+y*y+z*z); + } + + inline float length2() const + { + return (x*x+y*y+z*z); + } + + inline float normalize() + { + float onel = 1.0f / sqrtf(x*x+y*y+z*z); + x *= onel; + y *= onel; + z *= onel; + + return onel; + } + + inline float operator*(const pVector &a) const + { + return x*a.x + y*a.y + z*a.z; + } + + inline pVector operator*(const float s) const + { + return pVector(x*s, y*s, z*s); + } + + inline pVector operator/(const float s) const + { + float invs = 1.0f / s; + return pVector(x*invs, y*invs, z*invs); + } + + inline pVector operator+(const pVector& a) const + { + return pVector(x+a.x, y+a.y, z+a.z); + } + + inline pVector operator-(const pVector& a) const + { + return pVector(x-a.x, y-a.y, z-a.z); + } + + inline pVector operator-() + { + x = -x; + y = -y; + z = -z; + return *this; + } + + inline pVector& operator+=(const pVector& a) + { + x += a.x; + y += a.y; + z += a.z; + return *this; + } + + inline pVector& operator-=(const pVector& a) + { + x -= a.x; + y -= a.y; + z -= a.z; + return *this; + } + + inline pVector& operator*=(const float a) + { + x *= a; + y *= a; + z *= a; + return *this; + } + + inline pVector& operator/=(const float a) + { + float b = 1.0f / a; + x *= b; + y *= b; + z *= b; + return *this; + } + + inline pVector& operator=(const pVector& a) + { + x = a.x; + y = a.y; + z = a.z; + return *this; + } + + inline pVector operator^(const pVector& b) const + { + return pVector( + y*b.z-z*b.y, + z*b.x-x*b.z, + x*b.y-y*b.x); + } +}; + +#endif diff --git a/main/source/particle/papi.h b/main/source/particle/papi.h new file mode 100644 index 00000000..95ba22c7 --- /dev/null +++ b/main/source/particle/papi.h @@ -0,0 +1,229 @@ +// papi.h +// +// Copyright 1997-1998 by David K. McAllister +// http://www.cs.unc.edu/~davemc/Particle +// +// Include this file in all applications that use the Particle System API. + +#ifndef _particle_api_h +#define _particle_api_h + +#include + +// This is the major and minor version number of this release of the API. +#define P_VERSION 120 + +#ifdef WIN32 +#include + +#ifdef PARTICLEDLL_EXPORTS +#define PARTICLEDLL_API __declspec(dllexport) +#else +#define PARTICLEDLL_API __declspec(dllimport) +#endif + +#else +#define PARTICLEDLL_API +#endif + +// Actually this must be < sqrt(MAXFLOAT) since we store this value squared. +#define P_MAXFLOAT 1.0e16f + +#ifdef MAXINT +#define P_MAXINT MAXINT +#else +#define P_MAXINT 0x7fffffff +#endif + +#define P_EPS 1e-3f + +////////////////////////////////////////////////////////////////////// +// Type codes for domains +PARTICLEDLL_API enum PDomainEnum +{ + PDPoint = 0, // Single point + PDLine = 1, // Line segment + PDTriangle = 2, // Triangle + PDPlane = 3, // Arbitrarily-oriented plane + PDBox = 4, // Axis-aligned box + PDSphere = 5, // Sphere + PDCylinder = 6, // Cylinder + PDCone = 7, // Cone + PDBlob = 8, // Gaussian blob + PDDisc = 9, // Arbitrarily-oriented disc + PDRectangle = 10 // Rhombus-shaped planar region +}; + +// State setting calls + +PARTICLEDLL_API void pColor(float red, float green, float blue, float alpha = 1.0f); + +PARTICLEDLL_API void pColorD(float alpha, PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pSize(float size_x, float size_y = 1.0f, float size_z = 1.0f); + +PARTICLEDLL_API void pSizeD(PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pStartingAge(float age, float sigma = 1.0f); + +PARTICLEDLL_API void pTimeStep(float new_dt); + +PARTICLEDLL_API void pVelocity(float x, float y, float z); + +PARTICLEDLL_API void pVelocityD(PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pVertexB(float x, float y, float z); + +PARTICLEDLL_API void pVertexBD(PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pVertexBTracks(bool track_vertex = true); + + +// Action List Calls + +PARTICLEDLL_API void pCallActionList(int action_list_num); + +PARTICLEDLL_API void pDeleteActionLists(int action_list_num, int action_list_count = 1); + +PARTICLEDLL_API void pEndActionList(); + +PARTICLEDLL_API int pGenActionLists(int action_list_count = 1); + +PARTICLEDLL_API void pNewActionList(int action_list_num); + + +// Particle Group Calls + +PARTICLEDLL_API void pCopyGroup(int p_src_group_num, int index = 0, int copy_count = P_MAXINT); + +PARTICLEDLL_API void pCurrentGroup(int p_group_num); + +PARTICLEDLL_API void pDeleteParticleGroups(int p_group_num, int p_group_count = 1); + +PARTICLEDLL_API void pDrawGroupl(int dlist, bool const_size = false, + bool const_color = false, bool const_rotation = false); + +PARTICLEDLL_API void pDrawGroupp(int primitive, bool const_size = false, + bool const_color = false); + +PARTICLEDLL_API int pGenParticleGroups(int p_group_count = 1, int max_particles = 0); + +PARTICLEDLL_API int pGetGroupCount(); + +PARTICLEDLL_API int pGetParticles(int index, int count, float *position = NULL, float *color = NULL, + float *vel = NULL, float *size = NULL, float *age = NULL); + +PARTICLEDLL_API int pSetMaxParticles(int max_count); + + +// Actions + +PARTICLEDLL_API void pAvoid(float magnitude, float epsilon, float look_ahead, + PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pBounce(float friction, float resilience, float cutoff, + PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pCopyVertexB(bool copy_pos = true, bool copy_vel = false); + +PARTICLEDLL_API void pDamping(float damping_x, float damping_y, float damping_z, + float vlow = 0.0f, float vhigh = P_MAXFLOAT); + +PARTICLEDLL_API void pExplosion(float center_x, float center_y, float center_z, float velocity, + float magnitude, float stdev, float epsilon = P_EPS, float age = 0.0f); + +PARTICLEDLL_API void pFollow(float magnitude = 1.0f, float epsilon = P_EPS, float max_radius = P_MAXFLOAT); + +PARTICLEDLL_API void pGravitate(float magnitude = 1.0f, float epsilon = P_EPS, float max_radius = P_MAXFLOAT); + +PARTICLEDLL_API void pGravity(float dir_x, float dir_y, float dir_z); + +PARTICLEDLL_API void pJet(float center_x, float center_y, float center_z, float magnitude = 1.0f, + float epsilon = P_EPS, float max_radius = P_MAXFLOAT); + +PARTICLEDLL_API void pKillOld(float age_limit, bool kill_less_than = false); + +PARTICLEDLL_API void pMatchVelocity(float magnitude = 1.0f, float epsilon = P_EPS, + float max_radius = P_MAXFLOAT); + +PARTICLEDLL_API void pMove(); + +PARTICLEDLL_API void pOrbitLine(float p_x, float p_y, float p_z, + float axis_x, float axis_y, float axis_z, float magnitude = 1.0f, + float epsilon = P_EPS, float max_radius = P_MAXFLOAT); + +PARTICLEDLL_API void pOrbitPoint(float center_x, float center_y, float center_z, + float magnitude = 1.0f, float epsilon = P_EPS, + float max_radius = P_MAXFLOAT); + +PARTICLEDLL_API void pRandomAccel(PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pRandomDisplace(PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pRandomVelocity(PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pRestore(float time); + +PARTICLEDLL_API void pShade(float color_x, float color_y, float color_z, + float alpha, float scale); + +PARTICLEDLL_API void pSink(bool kill_inside, PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pSinkVelocity(bool kill_inside, PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pSource(float particle_rate, PDomainEnum dtype, + float a0 = 0.0f, float a1 = 0.0f, float a2 = 0.0f, + float a3 = 0.0f, float a4 = 0.0f, float a5 = 0.0f, + float a6 = 0.0f, float a7 = 0.0f, float a8 = 0.0f); + +PARTICLEDLL_API void pSpeedLimit(float min_speed, float max_speed = P_MAXFLOAT); + +PARTICLEDLL_API void pTargetColor(float color_x, float color_y, float color_z, + float alpha, float scale); + +PARTICLEDLL_API void pTargetSize(float size_x, float size_y, float size_z, + float scale_x = 0.0f, float scale_y = 0.0f, float scale_z = 0.0f); + +PARTICLEDLL_API void pTargetVelocity(float vel_x, float vel_y, float vel_z, float scale); + +PARTICLEDLL_API void pVertex(float x, float y, float z); + +PARTICLEDLL_API void pVortex(float center_x, float center_y, float center_z, + float axis_x, float axis_y, float axis_z, + float magnitude = 1.0f, float epsilon = P_EPS, + float max_radius = P_MAXFLOAT); + +#endif diff --git a/main/source/particle/system.cpp b/main/source/particle/system.cpp new file mode 100644 index 00000000..2cc2c712 --- /dev/null +++ b/main/source/particle/system.cpp @@ -0,0 +1,883 @@ +// system.cpp +// +// Copyright 1998 by David K. McAllister. +// +// This file implements the API calls that are not particle actions. + +#include "general.h" + +#include + +// XXX +#include +// using namespace std; + +// For Windows DLL. +#ifdef WIN32 +BOOL APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +float ParticleAction::dt; + +ParticleGroup **_ParticleState::group_list; +PAHeader **_ParticleState::alist_list; +int _ParticleState::group_count; +int _ParticleState::alist_count; + +// This AutoCall struct allows for static initialization of the above shared variables. +struct AutoCall +{ + AutoCall(); +}; + +AutoCall::AutoCall() +{ + // The list of groups, etc. + _ParticleState::group_list = new ParticleGroup *[16]; + _ParticleState::group_count = 16; + _ParticleState::alist_list = new PAHeader *[16]; + _ParticleState::alist_count = 16; + for(int i=0; i<16; i++) + { + _ParticleState::group_list[i] = NULL; + _ParticleState::alist_list[i] = NULL; + } +} + +#ifdef PARTICLE_MP +// This code is defined if we are compiling the library to be used on +// multiple threads. We need to have each API call figure out which +// _ParticleState belongs to it. We hash pointers to contexts in +// _CtxHash. Whenever a TID is asked for but doesn't exist we create +// it. + +#include + +// XXX This hard limit should get fixed. +int _CtxCount = 151; +_ParticleState **_CtxHash = NULL; + +inline int _HashTID(int tid) +{ + return ((tid << 13) ^ ((tid >> 11) ^ tid)) % _CtxCount; +} + +// Returns a reference to the appropriate particle state. +_ParticleState &_GetPStateWithTID() +{ + int tid = mp_my_threadnum(); + + int ind = _HashTID(tid); + + // cerr << tid << "->" << ind << endl; + + // Check through the hash table and find it. + for(int i=ind; i<_CtxCount; i++) + if(_CtxHash[i] && _CtxHash[i]->tid == tid) + { + //#pragma critical + //cerr << tid << " => " << i << endl; + + return *_CtxHash[i]; + } + + for(i=0; itid == tid) + return *_CtxHash[i]; + + // It didn't exist. It's a new context, so create it. + _ParticleState *psp = new _ParticleState(); + psp->tid = tid; + + // Find a place to put it. + for(i=ind; i<_CtxCount; i++) + if(_CtxHash[i] == NULL) + { + // #pragma critical + // cerr << "Stored " << tid << " at " << i << endl; + _CtxHash[i] = psp; + return *psp; + } + + for(i=0; i= group_count) + return NULL; // IERROR + + return group_list[p_group_num]; +} + +PAHeader *_ParticleState::GetListPtr(int a_list_num) +{ + if(a_list_num < 0) + return NULL; // IERROR + + if(a_list_num >= alist_count) + return NULL; // IERROR + + return alist_list[a_list_num]; +} + +// Return an index into the list of particle groups where +// p_group_count groups can be added. +int _ParticleState::GenerateGroups(int p_group_count) +{ + int num_empty = 0; + int first_empty = -1; + + for(int i=0; i= p_group_count) + return first_empty; + } + } + + // Couldn't find a big enough gap. Reallocate. + int new_count = 16 + group_count + p_group_count; + ParticleGroup **glist = new ParticleGroup *[new_count]; + memcpy(glist, group_list, group_count * sizeof(void*)); + for(int i=group_count; i= list_count) + return first_empty; + } + } + + // Couldn't find a big enough gap. Reallocate. + int new_count = 16 + alist_count + list_count; + PAHeader **new_list = new PAHeader *[new_count]; + memcpy(new_list, alist_list, alist_count * sizeof(void*)); + for(int i=list_count; itype) + { + case PAAvoidID: + ((PAAvoid *)pa)->Execute(pg); + break; + case PABounceID: + ((PABounce *)pa)->Execute(pg); + break; + case PACallActionListID: + ((PACallActionList *)pa)->Execute(pg); + break; + case PACopyVertexBID: + ((PACopyVertexB *)pa)->Execute(pg); + break; + case PADampingID: + ((PADamping *)pa)->Execute(pg); + break; + case PAExplosionID: + ((PAExplosion *)pa)->Execute(pg); + break; + case PAFollowID: + ((PAFollow *)pa)->Execute(pg); + break; + case PAGravitateID: + ((PAGravitate *)pa)->Execute(pg); + break; + case PAGravityID: + ((PAGravity *)pa)->Execute(pg); + break; + case PAJetID: + ((PAJet *)pa)->Execute(pg); + break; + case PAKillOldID: + ((PAKillOld *)pa)->Execute(pg); + break; + case PAMatchVelocityID: + ((PAMatchVelocity *)pa)->Execute(pg); + break; + case PAMoveID: + ((PAMove *)pa)->Execute(pg); + break; + case PAOrbitLineID: + ((PAOrbitLine *)pa)->Execute(pg); + break; + case PAOrbitPointID: + ((PAOrbitPoint *)pa)->Execute(pg); + break; + case PARandomAccelID: + ((PARandomAccel *)pa)->Execute(pg); + break; + case PARandomDisplaceID: + ((PARandomDisplace *)pa)->Execute(pg); + break; + case PARandomVelocityID: + ((PARandomVelocity *)pa)->Execute(pg); + break; + case PARestoreID: + ((PARestore *)pa)->Execute(pg); + break; + case PASinkID: + ((PASink *)pa)->Execute(pg); + break; + case PASinkVelocityID: + ((PASinkVelocity *)pa)->Execute(pg); + break; + case PASourceID: + ((PASource *)pa)->Execute(pg); + break; + case PASpeedLimitID: + ((PASpeedLimit *)pa)->Execute(pg); + break; + case PATargetColorID: + ((PATargetColor *)pa)->Execute(pg); + break; + case PATargetSizeID: + ((PATargetSize *)pa)->Execute(pg); + break; + case PATargetVelocityID: + ((PATargetVelocity *)pa)->Execute(pg); + break; + case PAVortexID: + ((PAVortex *)pa)->Execute(pg); + break; + } + } +} + +// Add the incoming action to the end of the current action list. +void _pAddActionToList(ParticleAction *S, int size) +{ + _ParticleState &_ps = _GetPState(); + + if(!_ps.in_new_list) + return; // ERROR + + if(_ps.pact == NULL) + return; // ERROR + + if(_ps.list_id < 0) + return; // ERROR + + PAHeader *alist = _ps.pact; + + if(alist->actions_allocated <= alist->count) + { + // Must reallocate. + int new_alloc = 16 + alist->actions_allocated; + PAHeader *new_alist = new PAHeader[new_alloc]; + memcpy(new_alist, alist, alist->count * sizeof(PAHeader)); + + delete [] alist; + _ps.alist_list[_ps.list_id] = _ps.pact = alist = new_alist; + + alist->actions_allocated = new_alloc; + } + + // Now add it in. + memcpy(&alist[alist->count], S, size); + alist->count++; +} + +//////////////////////////////////////////////////////// +// State setting calls + +PARTICLEDLL_API void pColor(float red, float green, float blue, float alpha) +{ + _ParticleState &_ps = _GetPState(); + + _ps.Alpha = alpha; + _ps.Color = pDomain(PDPoint, red, green, blue); +} + +PARTICLEDLL_API void pColorD(float alpha, PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + _ParticleState &_ps = _GetPState(); + + _ps.Alpha = alpha; + _ps.Color = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); +} + +PARTICLEDLL_API void pVelocity(float x, float y, float z) +{ + _ParticleState &_ps = _GetPState(); + + _ps.Vel = pDomain(PDPoint, x, y, z); +} + +PARTICLEDLL_API void pVelocityD(PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + _ParticleState &_ps = _GetPState(); + + _ps.Vel = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); +} + +PARTICLEDLL_API void pVertexB(float x, float y, float z) +{ + _ParticleState &_ps = _GetPState(); + + _ps.VertexB = pDomain(PDPoint, x, y, z); +} + +PARTICLEDLL_API void pVertexBD(PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + _ParticleState &_ps = _GetPState(); + + _ps.VertexB = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); +} + + +PARTICLEDLL_API void pVertexBTracks(bool trackVertex) +{ + _ParticleState &_ps = _GetPState(); + + _ps.vertexB_tracks = trackVertex; +} + +PARTICLEDLL_API void pSize(float size_x, float size_y, float size_z) +{ + _ParticleState &_ps = _GetPState(); + + _ps.Size = pDomain(PDPoint, size_x, size_y, size_z); +} + +PARTICLEDLL_API void pSizeD(PDomainEnum dtype, + float a0, float a1, float a2, + float a3, float a4, float a5, + float a6, float a7, float a8) +{ + _ParticleState &_ps = _GetPState(); + + _ps.Size = pDomain(dtype, a0, a1, a2, a3, a4, a5, a6, a7, a8); +} + +PARTICLEDLL_API void pStartingAge(float age, float sigma) +{ + _ParticleState &_ps = _GetPState(); + + _ps.Age = age; + _ps.AgeSigma = sigma; +} + +PARTICLEDLL_API void pTimeStep(float newDT) +{ + _ParticleState &_ps = _GetPState(); + + _ps.dt = newDT; +} + +//////////////////////////////////////////////////////// +// Action List Calls + +PARTICLEDLL_API int pGenActionLists(int action_list_count) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + return -1; // ERROR + + _PLock(); + + int ind = _ps.GenerateLists(action_list_count); + + for(int i=ind; iactions_allocated = 8; + _ps.alist_list[i]->type = PAHeaderID; + _ps.alist_list[i]->count = 1; + } + + _PUnLock(); + + return ind; +} + +PARTICLEDLL_API void pNewActionList(int action_list_num) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + return; // ERROR + + _ps.pact = _ps.GetListPtr(action_list_num); + + if(_ps.pact == NULL) + return; // ERROR + + _ps.list_id = action_list_num; + _ps.in_new_list = true; + + // Remove whatever used to be in the list. + _ps.pact->count = 1; +} + +PARTICLEDLL_API void pEndActionList() +{ + _ParticleState &_ps = _GetPState(); + + if(!_ps.in_new_list) + return; // ERROR + + _ps.in_new_list = false; + + _ps.pact = NULL; + _ps.list_id = -1; +} + +PARTICLEDLL_API void pDeleteActionLists(int action_list_num, int action_list_count) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + return; // ERROR + + if(action_list_num < 0) + return; // ERROR + + if(action_list_num + action_list_count > _ps.alist_count) + return; // ERROR + + _PLock(); + + for(int i = action_list_num; i < action_list_num + action_list_count; i++) + { + if(_ps.alist_list[i]) + { + delete [] _ps.alist_list[i]; + _ps.alist_list[i] = NULL; + } + else + { + _PUnLock(); + return; // ERROR + } + } + + _PUnLock(); +} + +PARTICLEDLL_API void pCallActionList(int action_list_num) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + { + // Add this call as an action to the current list. + extern void _pSendAction(ParticleAction *S, PActionEnum type, int size); + + PACallActionList S; + S.action_list_num = action_list_num; + + _pSendAction(&S, PACallActionListID, sizeof(PACallActionList)); + } + else + { + // Execute the specified action list. + PAHeader *pa = _ps.GetListPtr(action_list_num); + + if(pa == NULL) + return; // ERRROR + + // XXX A temporary hack. + pa->dt = _ps.dt; + + _ps.in_call_list = true; + + _pCallActionList(pa+1, pa->count-1, _ps.pgrp); + + _ps.in_call_list = false; + } +} + +//////////////////////////////////////////////////////// +// Particle Group Calls + +// Create particle groups, each with max_particles allocated. +PARTICLEDLL_API int pGenParticleGroups(int p_group_count, int max_particles) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + return -1; // ERROR + + _PLock(); + // cerr << "Generating pg " << _ps.tid << " cnt= " << max_particles << endl; + + int ind = _ps.GenerateGroups(p_group_count); + + for(int i=ind; imax_particles = max_particles; + _ps.group_list[i]->particles_allocated = max_particles; + _ps.group_list[i]->p_count = 0; + } + + _PUnLock(); + + return ind; +} + +PARTICLEDLL_API void pDeleteParticleGroups(int p_group_num, int p_group_count) +{ + _ParticleState &_ps = _GetPState(); + + if(p_group_num < 0) + return; // ERROR + + if(p_group_num + p_group_count > _ps.group_count) + return; // ERROR + + _PLock(); + + for(int i = p_group_num; i < p_group_num + p_group_count; i++) + { + if(_ps.group_list[i]) + { + delete [] _ps.group_list[i]; + _ps.group_list[i] = NULL; + } + else + { + _PUnLock(); + return; // ERROR + } + } + + _PUnLock(); +} + +// Change which group is current. +PARTICLEDLL_API void pCurrentGroup(int p_group_num) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + return; // ERROR + + _ps.pgrp = _ps.GetGroupPtr(p_group_num); + if(_ps.pgrp) + _ps.group_id = p_group_num; + else + _ps.group_id = -1; +} + +// Change the maximum number of particles in the current group. +PARTICLEDLL_API int pSetMaxParticles(int max_count) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + return 0; // ERROR + + ParticleGroup *pg = _ps.pgrp; + if(pg == NULL) + return 0; // ERROR + + if(max_count < 0) + return 0; // ERROR + + // Reducing max. + if(pg->particles_allocated >= max_count) + { + pg->max_particles = max_count; + + // May have to kill particles. + if(pg->p_count > pg->max_particles) + pg->p_count = pg->max_particles; + + return max_count; + } + + _PLock(); + + // Allocate particles. + ParticleGroup *pg2 =(ParticleGroup *)new Particle[max_count + 2]; + if(pg2 == NULL) + { + // Not enough memory. Just give all we've got. + // ERROR + pg->max_particles = pg->particles_allocated; + + _PUnLock(); + + return pg->max_particles; + } + + memcpy(pg2, pg, (pg->p_count + 2) * sizeof(Particle)); + + delete [] pg; + + _ps.group_list[_ps.group_id] = _ps.pgrp = pg2; + pg2->max_particles = max_count; + pg2->particles_allocated = max_count; + + _PUnLock(); + + return max_count; +} + +// Copy from the specified group to the current group. +PARTICLEDLL_API void pCopyGroup(int p_src_group_num, int index, int copy_count) +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + return; // ERROR + + ParticleGroup *srcgrp = _ps.GetGroupPtr(p_src_group_num); + if(srcgrp == NULL) + return; // ERROR + + ParticleGroup *destgrp = _ps.pgrp; + if(destgrp == NULL) + return; // ERROR + + // Find out exactly how many to copy. + int ccount = copy_count; + if(ccount > srcgrp->p_count - index) + ccount = srcgrp->p_count - index; + if(ccount > destgrp->max_particles - destgrp->p_count) + ccount = destgrp->max_particles - destgrp->p_count; + + // #pragma critical + // cerr << p_src_group_num << ": " << ccount << " " << srcgrp->p_count << " " << index << endl; + + if(ccount<0) + ccount = 0; + + // Directly copy the particles to the current list. + for(int i=0; ilist[destgrp->p_count+i] = + srcgrp->list[index+i]; + } + destgrp->p_count += ccount; +} + +// Copy from the current group to application memory. +PARTICLEDLL_API int pGetParticles(int index, int count, float *verts, + float *color, float *vel, float *size, float *age) +{ + _ParticleState &_ps = _GetPState(); + + // XXX I should think about whether color means color3, color4, or what. + // For now, it means color4. + + if(_ps.in_new_list) + return -1; // ERROR + + ParticleGroup *pg = _ps.pgrp; + if(pg == NULL) + return -2; // ERROR + + if(index < 0 || count < 0) + return -3; // ERROR + + if(index + count > pg->p_count) + { + count = pg->p_count - index; + if(count <= 0) + return -4; // ERROR index out of bounds. + } + + int vi = 0, ci = 0, li = 0, si = 0, ai = 0; + + // This could be optimized. + for(int i=0; ilist[index + i]; + + if(verts) + { + verts[vi++] = m.pos.x; + verts[vi++] = m.pos.y; + verts[vi++] = m.pos.z; + } + + if(color) + { + color[ci++] = m.color.x; + color[ci++] = m.color.y; + color[ci++] = m.color.z; + color[ci++] = m.alpha; + } + + if(vel) + { + vel[li++] = m.vel.x; + vel[li++] = m.vel.y; + vel[li++] = m.vel.z; + } + + if(size) + { + size[si++] = m.size.x; + size[si++] = m.size.y; + size[si++] = m.size.z; + } + + if(age) + { + age[ai++] = m.age; + } + } + + return count; +} + +// Returns the number of particles currently in the group. +PARTICLEDLL_API int pGetGroupCount() +{ + _ParticleState &_ps = _GetPState(); + + if(_ps.in_new_list) + return 0; // ERROR + + if(_ps.pgrp == NULL) + return 0; // ERROR + + return _ps.pgrp->p_count; +} diff --git a/main/source/util/Balance.cpp b/main/source/util/Balance.cpp index 5fcbb792..90692dc8 100644 --- a/main/source/util/Balance.cpp +++ b/main/source/util/Balance.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "Balance.h" using std::auto_ptr; @@ -1054,4 +1056,4 @@ bool FileValueContainer::save(void) file.close(); return true; -} \ No newline at end of file +} diff --git a/main/source/util/CString.h b/main/source/util/CString.h index 243e8163..7782d1c4 100644 --- a/main/source/util/CString.h +++ b/main/source/util/CString.h @@ -2,6 +2,7 @@ #define C_STRING_H #include +#include #include #include using std::string; diff --git a/main/source/util/STLUtil.h b/main/source/util/STLUtil.h index dde3e8dd..0fbf1532 100644 --- a/main/source/util/STLUtil.h +++ b/main/source/util/STLUtil.h @@ -29,6 +29,7 @@ #ifndef STL_UTIL_H #define STL_UTIL_H +#include #include "util/nowarnings.h" #include "util/CString.h" #include "types.h" @@ -53,4 +54,4 @@ bool MakeHexPairsFromBytes(const unsigned char* inBytes, string& ioHex, int numB extern int32 sprintf(string& inOutput, const char* inPattern, ...); -#endif \ No newline at end of file +#endif