NS/main/source/particles/system.cpp
Brett Caswell 36c78acbce
PR Merge of Patch 63 to Patch 61 (#6)
* handle particles project VC++ compilation errors with MVCS and project property changes

 - retarget to v142 platformtoolset
 - remove explicit windows SDK version; use latest by default
 - remove settings that are otherwise default values in project or are best determined by solution

* attempt to handle clang++ issues regarding unsupported c++11 features

* reset file changes to particledefs.h

* removing PARTICLEDLL_API definition and replacing usage with extern "C" block statements

* add g++ compiler option to specify c++11 standard

* attempt to resolve forward enum errors by adding std to base cflags

* replacing deprecated libstdc++ and removing -lstdc++ flag, updating MacOSX10 sdk version

* small refactor to Makefiles, and add  libstdc++ back to linux build

* remove added type to enum

* reset makefile changes that may be causing unexpected linux build failures

* refactoring std=c++11 declarations in Makefile to mitgate linux build failing

* ensure std is set for hl_cdll make

* attempt to define a standard library to resolve vector initialization_list constructor issue

* attempt to update MacOS sdk, set minimum os to be 10.7, and include export in travis ci to define deployment target
2020-07-05 11:11:06 -05:00

911 lines
No EOL
18 KiB
C++

// system.cpp
//
// Copyright 1998 by David K. McAllister.
//
// This file implements the API calls that are not particle actions.
#include "general.h"
#include <memory.h>
// XXX
#include <iostream>
// using namespace std;
// <<< cgc >>> removed DllMain() because I'm linking statically
// 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
// <<< cgc >>> added this pre-declaration
extern void _pSendAction(ParticleAction *S, PActionEnum type, int size);
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 <mpc.h>
// 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; i<ind; i++)
if(_CtxHash[i] && _CtxHash[i]->tid == 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<ind; i++)
if(_CtxHash[i] == NULL)
{
_CtxHash[i] = psp;
return *psp;
}
// We should never get here. The hash table got full.
exit(1);
// To appease warnings.
return *_CtxHash[0];
}
inline void _PLock()
{
// XXX This implementation is specific to the #pragma parallel directives.
// cerr << "Getting lock.\n";
// mp_setlock();
// cerr << "Got lock.\n";
}
inline void _PUnLock()
{
// XXX This implementation is specific to the #pragma parallel directives.
// cerr << "Giving lock.\n";
// mp_unsetlock();
// cerr << "Gave lock.\n";
}
#else
// This is the global state.
_ParticleState __ps;
inline void _PLock()
{
}
inline void _PUnLock()
{
}
#endif
_ParticleState::_ParticleState()
{
in_call_list = false;
in_new_list = false;
vertexB_tracks = true;
dt = 1.0f;
group_id = -1;
list_id = -1;
pgrp = NULL;
pact = NULL;
tid = 0; // This will be filled in above if we're MP.
Size = pDomain(PDPoint, 1.0f, 1.0f, 1.0f);
Vel = pDomain(PDPoint, 0.0f, 0.0f, 0.0f);
VertexB = pDomain(PDPoint, 0.0f, 0.0f, 0.0f);
Color = pDomain(PDPoint, 1.0f, 1.0f, 1.0f);
Alpha = 1.0f;
Age = 0.0f;
AgeSigma = 0.0f;
}
ParticleGroup *_ParticleState::GetGroupPtr(int p_group_num)
{
if(p_group_num < 0)
return NULL; // IERROR
if(p_group_num >= 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<group_count; i++)
{
if(group_list[i])
{
num_empty = 0;
first_empty = -1;
}
else
{
if(first_empty < 0)
first_empty = i;
num_empty++;
if(num_empty >= 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<new_count; i++)
glist[i] = NULL;
delete [] group_list;
group_list = glist;
group_count = new_count;
return GenerateGroups(p_group_count);
}
// Return an index into the list of action lists where
// list_count lists can be added.
int _ParticleState::GenerateLists(int list_count)
{
int num_empty = 0;
int first_empty = -1;
for(int i=0; i<alist_count; i++)
{
if(alist_list[i])
{
num_empty = 0;
first_empty = -1;
}
else
{
if(first_empty < 0)
first_empty = i;
num_empty++;
if(num_empty >= 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; i<new_count; i++)
new_list[i] = NULL;
delete [] alist_list;
alist_list = new_list;
alist_count = new_count;
return GenerateLists(list_count);
}
////////////////////////////////////////////////////////
// Auxiliary calls
void _pCallActionList(ParticleAction *apa, int num_actions,
ParticleGroup *pg)
{
// All these require a particle group, so check for it.
if(pg == NULL)
return;
PAHeader *pa = (PAHeader *)apa;
// Step through all the actions in the action list.
for(int action = 0; action < num_actions; action++, pa++)
{
switch(pa->type)
{
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
extern "C" {
void pColor(float red, float green, float blue, float alpha)
{
_ParticleState& _ps = _GetPState();
_ps.Alpha = alpha;
_ps.Color = pDomain(PDPoint, red, green, blue);
}
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);
}
void pVelocity(float x, float y, float z)
{
_ParticleState& _ps = _GetPState();
_ps.Vel = pDomain(PDPoint, x, y, z);
}
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);
}
void pVertexB(float x, float y, float z)
{
_ParticleState& _ps = _GetPState();
_ps.VertexB = pDomain(PDPoint, x, y, z);
}
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);
}
void pVertexBTracks(bool trackVertex)
{
_ParticleState& _ps = _GetPState();
_ps.vertexB_tracks = trackVertex;
}
void pSize(float size_x, float size_y, float size_z)
{
_ParticleState& _ps = _GetPState();
_ps.Size = pDomain(PDPoint, size_x, size_y, size_z);
}
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);
}
void pStartingAge(float age, float sigma)
{
_ParticleState& _ps = _GetPState();
_ps.Age = age;
_ps.AgeSigma = sigma;
}
void pTimeStep(float newDT)
{
_ParticleState& _ps = _GetPState();
_ps.dt = newDT;
}
////////////////////////////////////////////////////////
// Action List Calls
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; i < ind + action_list_count; i++)
{
_ps.alist_list[i] = new PAHeader[8];
_ps.alist_list[i]->actions_allocated = 8;
_ps.alist_list[i]->type = PAHeaderID;
_ps.alist_list[i]->count = 1;
}
_PUnLock();
return ind;
}
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;
}
void pEndActionList()
{
_ParticleState& _ps = _GetPState();
if (!_ps.in_new_list)
return; // ERROR
_ps.in_new_list = false;
_ps.pact = NULL;
_ps.list_id = -1;
}
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();
}
void pCallActionList(int action_list_num)
{
_ParticleState& _ps = _GetPState();
if (_ps.in_new_list)
{
// Add this call as an action to the current list.
// <<< cgc >>> commented out predeclaration
//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.
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; i < ind + p_group_count; i++)
{
_ps.group_list[i] = (ParticleGroup*)new
Particle[max_particles + 2];
_ps.group_list[i]->max_particles = max_particles;
_ps.group_list[i]->particles_allocated = max_particles;
_ps.group_list[i]->p_count = 0;
}
_PUnLock();
return ind;
}
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.
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;
}
ParticleGroup* pGetCurrentGroup(void)
{
_ParticleState& _ps = _GetPState();
ParticleGroup* pg = _ps.pgrp;
return pg;
}
// Change the maximum number of particles in the current group.
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.
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; i < ccount; i++)
{
destgrp->list[destgrp->p_count + i] =
srcgrp->list[index + i];
}
destgrp->p_count += ccount;
}
ParticleGroup* pGetParticleGroupRef(int p_group_num)
{
ParticleGroup* theGroup = NULL;
_ParticleState& _ps = _GetPState();
if (!_ps.in_new_list)
{
theGroup = _ps.GetGroupPtr(p_group_num);
}
return theGroup;
}
// Copy from the current group to application memory.
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; i < count; i++)
{
Particle& m = pg->list[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.
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;
}
}