572 lines
15 KiB
C++
572 lines
15 KiB
C++
/*
|
|
* Copyright 1993-2007 NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* NOTICE TO USER:
|
|
*
|
|
* This source code is subject to NVIDIA ownership rights under U.S. and
|
|
* international Copyright laws.
|
|
*
|
|
* NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE
|
|
* CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR
|
|
* IMPLIED WARRANTY OF ANY KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
* IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
|
|
* OR PERFORMANCE OF THIS SOURCE CODE.
|
|
*
|
|
* U.S. Government End Users. This source code is a "commercial item" as
|
|
* that term is defined at 48 C.F.R. 2.101 (OCT 1995), consisting of
|
|
* "commercial computer software" and "commercial computer software
|
|
* documentation" as such terms are used in 48 C.F.R. 12.212 (SEPT 1995)
|
|
* and is provided to the U.S. Government only as a commercial end item.
|
|
* Consistent with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through
|
|
* 227.7202-4 (JUNE 1995), all U.S. Government End Users acquire the
|
|
* source code with only those rights set forth herein.
|
|
*/
|
|
|
|
/*
|
|
Particle system example with collisions using uniform grid
|
|
*/
|
|
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <algorithm>
|
|
#include <math.h>
|
|
//#include <cutil.h>
|
|
|
|
#include <GL/glew.h>
|
|
|
|
#if defined(__APPLE__) || defined(MACOSX)
|
|
#include <GLUT/glut.h>
|
|
#else
|
|
#include <GL/glut.h>
|
|
#endif
|
|
|
|
#include "LinearMath/btQuickprof.h"
|
|
|
|
#include "particleSystem.h"
|
|
#include "render_particles.h"
|
|
#include "paramgl.h"
|
|
|
|
// view params
|
|
int ox, oy;
|
|
int buttonState = 0;
|
|
float camera_trans[] = {0, 0, -3};
|
|
float camera_rot[] = {0, 0, 0};
|
|
float camera_trans_lag[] = {0, 0, -3};
|
|
float camera_rot_lag[] = {0, 0, 0};
|
|
const float inertia = 0.1;
|
|
ParticleRenderer::DisplayMode displayMode = ParticleRenderer::PARTICLE_SPHERES;
|
|
|
|
int mode = 0;
|
|
bool displayEnabled = true;
|
|
bool bPause = false;
|
|
bool displaySliders = false;
|
|
bool wireframe = false;
|
|
|
|
enum { M_VIEW = 0, M_MOVE };
|
|
|
|
uint numParticles = 0;
|
|
uint3 gridSize;
|
|
int numIterations = 0; // run until exit
|
|
|
|
// simulation parameters
|
|
float timestep = 0.5f;
|
|
float damping = 1.0f;
|
|
float gravity = 0.0003f;
|
|
int iterations = 1;
|
|
int ballr = 10;
|
|
|
|
float collideSpring = 0.5f;;
|
|
float collideDamping = 0.02f;;
|
|
float collideShear = 0.1f;
|
|
float collideAttraction = 0.0f;
|
|
|
|
ParticleSystem *psystem = 0;
|
|
|
|
// fps
|
|
static int fpsCount = 0;
|
|
static int fpsLimit = 1;
|
|
unsigned int timer;
|
|
|
|
ParticleRenderer *renderer = 0;
|
|
|
|
float modelView[16];
|
|
|
|
ParamListGL *params;
|
|
|
|
extern "C" void cudaInit(int argc, char **argv);
|
|
|
|
void init(int numParticles, uint3 gridSize)
|
|
{
|
|
psystem = new ParticleSystem(numParticles, gridSize);
|
|
psystem->reset(ParticleSystem::CONFIG_GRID);
|
|
|
|
renderer = new ParticleRenderer;
|
|
renderer->setParticleRadius(psystem->getParticleRadius());
|
|
renderer->setColorBuffer(psystem->getColorBuffer());
|
|
|
|
// CUT_SAFE_CALL(cutCreateTimer(&timer));
|
|
}
|
|
|
|
void initGL()
|
|
{
|
|
glewInit();
|
|
if (!glewIsSupported("GL_VERSION_2_0 GL_VERSION_1_5 GL_ARB_multitexture GL_ARB_vertex_buffer_object")) {
|
|
fprintf(stderr, "Required OpenGL extensions missing.");
|
|
exit(-1);
|
|
}
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClearColor(0.25, 0.25, 0.25, 1.0);
|
|
|
|
glutReportErrors();
|
|
}
|
|
|
|
|
|
void display()
|
|
{
|
|
// update the simulation
|
|
if (!bPause)
|
|
{
|
|
psystem->setIterations(iterations);
|
|
psystem->setDamping(damping);
|
|
psystem->setGravity(-gravity);
|
|
psystem->setCollideSpring(collideSpring);
|
|
psystem->setCollideDamping(collideDamping);
|
|
psystem->setCollideShear(collideShear);
|
|
psystem->setCollideAttraction(collideAttraction);
|
|
|
|
psystem->update(timestep);
|
|
renderer->setVertexBuffer(psystem->getCurrentReadBuffer(), psystem->getNumParticles());
|
|
}
|
|
|
|
// render
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// view transform
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
for (int c = 0; c < 3; ++c)
|
|
{
|
|
camera_trans_lag[c] += (camera_trans[c] - camera_trans_lag[c]) * inertia;
|
|
camera_rot_lag[c] += (camera_rot[c] - camera_rot_lag[c]) * inertia;
|
|
}
|
|
glTranslatef(camera_trans_lag[0], camera_trans_lag[1], camera_trans_lag[2]);
|
|
glRotatef(camera_rot_lag[0], 1.0, 0.0, 0.0);
|
|
glRotatef(camera_rot_lag[1], 0.0, 1.0, 0.0);
|
|
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, modelView);
|
|
|
|
// cube
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
glutWireCube(2.0);
|
|
|
|
// collider
|
|
glPushMatrix();
|
|
float4 p = psystem->getColliderPos();
|
|
glTranslatef(p.x, p.y, p.z);
|
|
glColor3f(1.0, 0.0, 0.0);
|
|
glutSolidSphere(psystem->getColliderRadius(), 20, 10);
|
|
glPopMatrix();
|
|
|
|
if (displayEnabled)
|
|
{
|
|
renderer->display(displayMode);
|
|
}
|
|
|
|
if (displaySliders) {
|
|
glDisable(GL_DEPTH_TEST);
|
|
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); // invert color
|
|
glEnable(GL_BLEND);
|
|
params->Render(0, 0);
|
|
glDisable(GL_BLEND);
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
psystem->debugDraw();
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
float offsX = 10.f;
|
|
float offsY = 10.f;
|
|
renderer->showProfileInfo(offsX, offsY, 20.f);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
|
|
glutSwapBuffers();
|
|
|
|
{
|
|
char fps[256];
|
|
//float ifps = 1.f / (cutGetAverageTimerValue(timer) / 1000.f);
|
|
switch (psystem->getSimulationMode())
|
|
{
|
|
case ParticleSystem::SIMULATION_CUDA:
|
|
{
|
|
sprintf(fps, "CUDA particles (%d particles)", numParticles);
|
|
break;
|
|
}
|
|
case ParticleSystem::SIMULATION_BULLET_CPU:
|
|
{
|
|
sprintf(fps, "Bullet btCudaBroadphase (%d btSphereShapes)", numParticles);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
sprintf(fps, "Unknown simulation mode");
|
|
}
|
|
}
|
|
glutSetWindowTitle(fps);
|
|
}
|
|
|
|
glutReportErrors();
|
|
|
|
}
|
|
|
|
void reshape(int w, int h)
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective(60.0, (float) w / (float) h, 0.1, 10.0);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glViewport(0, 0, w, h);
|
|
|
|
renderer->setWindowSize(w, h);
|
|
renderer->setFOV(60.0);
|
|
}
|
|
|
|
void mouse(int button, int state, int x, int y)
|
|
{
|
|
int mods;
|
|
|
|
if (state == GLUT_DOWN)
|
|
buttonState |= 1<<button;
|
|
else if (state == GLUT_UP)
|
|
buttonState = 0;
|
|
|
|
mods = glutGetModifiers();
|
|
if (mods & GLUT_ACTIVE_SHIFT) {
|
|
buttonState = 2;
|
|
} else if (mods & GLUT_ACTIVE_CTRL) {
|
|
buttonState = 3;
|
|
}
|
|
|
|
ox = x; oy = y;
|
|
|
|
if (displaySliders) {
|
|
if (params->Mouse(x, y, button, state)) {
|
|
glutPostRedisplay();
|
|
return;
|
|
}
|
|
}
|
|
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
// transfrom vector by matrix
|
|
void xform(float *v, float *r, GLfloat *m)
|
|
{
|
|
r[0] = v[0]*m[0] + v[1]*m[4] + v[2]*m[8] + m[12];
|
|
r[1] = v[0]*m[1] + v[1]*m[5] + v[2]*m[9] + m[13];
|
|
r[2] = v[0]*m[2] + v[1]*m[6] + v[2]*m[10] + m[14];
|
|
}
|
|
|
|
// transform vector by transpose of matrix
|
|
void ixform(float *v, float *r, GLfloat *m)
|
|
{
|
|
r[0] = v[0]*m[0] + v[1]*m[1] + v[2]*m[2];
|
|
r[1] = v[0]*m[4] + v[1]*m[5] + v[2]*m[6];
|
|
r[2] = v[0]*m[8] + v[1]*m[9] + v[2]*m[10];
|
|
}
|
|
|
|
void ixformPoint(float *v, float *r, GLfloat *m)
|
|
{
|
|
float x[4];
|
|
x[0] = v[0] - m[12];
|
|
x[1] = v[1] - m[13];
|
|
x[2] = v[2] - m[14];
|
|
x[3] = 1.0f;
|
|
ixform(x, r, m);
|
|
}
|
|
|
|
void motion(int x, int y)
|
|
{
|
|
float dx, dy;
|
|
dx = x - ox;
|
|
dy = y - oy;
|
|
|
|
if (displaySliders) {
|
|
if (params->Motion(x, y)) {
|
|
ox = x; oy = y;
|
|
glutPostRedisplay();
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch(mode)
|
|
{
|
|
case M_VIEW:
|
|
if (buttonState == 3) {
|
|
// left+middle = zoom
|
|
camera_trans[2] += (dy / 100.0) * 0.5 * fabs(camera_trans[2]);
|
|
}
|
|
else if (buttonState & 2) {
|
|
// middle = translate
|
|
camera_trans[0] += dx / 100.0;
|
|
camera_trans[1] -= dy / 100.0;
|
|
}
|
|
else if (buttonState & 1) {
|
|
// left = rotate
|
|
camera_rot[0] += dy / 5.0;
|
|
camera_rot[1] += dx / 5.0;
|
|
}
|
|
break;
|
|
|
|
case M_MOVE:
|
|
{
|
|
float translateSpeed = 0.003f;
|
|
float4 p = psystem->getColliderPos();
|
|
if (buttonState==1) {
|
|
float v[3], r[3];
|
|
v[0] = dx*translateSpeed;
|
|
v[1] = -dy*translateSpeed;
|
|
v[2] = 0.0f;
|
|
ixform(v, r, modelView);
|
|
p.x += r[0];
|
|
p.y += r[1];
|
|
p.z += r[2];
|
|
} else if (buttonState==2) {
|
|
float v[3], r[3];
|
|
v[0] = 0.0f;
|
|
v[1] = 0.0f;
|
|
v[2] = dy*translateSpeed;
|
|
ixform(v, r, modelView);
|
|
p.x += r[0];
|
|
p.y += r[1];
|
|
p.z += r[2];
|
|
}
|
|
psystem->setColliderPos(p);
|
|
}
|
|
break;
|
|
}
|
|
|
|
ox = x; oy = y;
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
inline float frand()
|
|
{
|
|
return rand() / (float) RAND_MAX;
|
|
}
|
|
|
|
// commented out to remove unused parameter warnings in Linux
|
|
void key(unsigned char key, int /*x*/, int /*y*/)
|
|
{
|
|
#ifndef BT_NO_PROFILE
|
|
if (key >= 0x31 && key < 0x37)
|
|
{
|
|
int child = key-0x31;
|
|
renderer->m_profileIterator->Enter_Child(child);
|
|
}
|
|
if (key==0x30)
|
|
{
|
|
renderer->m_profileIterator->Enter_Parent();
|
|
}
|
|
#endif //BT_NO_PROFILE
|
|
|
|
switch (key)
|
|
{
|
|
case ' ':
|
|
bPause = !bPause;
|
|
break;
|
|
case 13:
|
|
psystem->update(timestep);
|
|
renderer->setVertexBuffer(psystem->getCurrentReadBuffer(), psystem->getNumParticles());
|
|
break;
|
|
case '\033':
|
|
case 'q':
|
|
exit(0);
|
|
break;
|
|
case 'v':
|
|
mode = M_VIEW;
|
|
break;
|
|
case 'm':
|
|
mode = M_MOVE;
|
|
break;
|
|
case 'p':
|
|
displayMode = (ParticleRenderer::DisplayMode)
|
|
((displayMode + 1) % ParticleRenderer::PARTICLE_NUM_MODES);
|
|
break;
|
|
case 'd':
|
|
psystem->dumpGrid();
|
|
break;
|
|
case 'u':
|
|
psystem->dumpParticles(0, 1);
|
|
break;
|
|
|
|
case 'r':
|
|
displayEnabled = !displayEnabled;
|
|
break;
|
|
|
|
case 'g':
|
|
psystem->reset(ParticleSystem::CONFIG_GRID);
|
|
break;
|
|
case 'a':
|
|
psystem->reset(ParticleSystem::CONFIG_RANDOM);
|
|
break;
|
|
case 'e':
|
|
{
|
|
// inject a sphere of particles
|
|
float pr = psystem->getParticleRadius();
|
|
float tr = pr+(pr*2.0f)*ballr;
|
|
float pos[4], vel[4];
|
|
pos[0] = -1.0 + tr + frand()*(2.0f - tr*2.0f);
|
|
pos[1] = 1.0f - tr;
|
|
pos[2] = -1.0 + tr + frand()*(2.0f - tr*2.0f);
|
|
pos[3] = 0.0f;
|
|
vel[0] = vel[1] = vel[2] = vel[3] = 0.0f;
|
|
psystem->addSphere(0, pos, vel, ballr, pr*2.0f);
|
|
}
|
|
break;
|
|
case 'b':
|
|
{
|
|
// shoot ball from camera
|
|
float pr = psystem->getParticleRadius();
|
|
float vel[4], velw[4], pos[4], posw[4];
|
|
vel[0] = 0.0f;
|
|
vel[1] = 0.0f;
|
|
vel[2] = -0.05f;
|
|
vel[3] = 0.0f;
|
|
ixform(vel, velw, modelView);
|
|
|
|
pos[0] = 0.0f;
|
|
pos[1] = 0.0f;
|
|
pos[2] = -2.5f;
|
|
pos[3] = 1.0;
|
|
ixformPoint(pos, posw, modelView);
|
|
posw[3] = 0.0f;
|
|
|
|
psystem->addSphere(0, posw, velw, ballr, pr*2.0f);
|
|
}
|
|
break;
|
|
|
|
case 'w':
|
|
wireframe = !wireframe;
|
|
break;
|
|
|
|
case 'h':
|
|
displaySliders = !displaySliders;
|
|
break;
|
|
case 's':
|
|
psystem->setSimulationMode((ParticleSystem::SimulationMode) ((psystem->getSimulationMode() + 1) % ParticleSystem::SIMULATION_NUM_MODES));
|
|
CProfileManager::CleanupMemory();
|
|
break;
|
|
}
|
|
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
void special(int k, int x, int y)
|
|
{
|
|
if (displaySliders) {
|
|
params->Special(k, x, y);
|
|
}
|
|
}
|
|
|
|
void idle(void)
|
|
{
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
void initParams()
|
|
{
|
|
// create a new parameter list
|
|
params = new ParamListGL("misc");
|
|
params->AddParam(new Param<float>("time step", timestep, 0.0, 1.0, 0.01, ×tep));
|
|
params->AddParam(new Param<int>("iterations", iterations, 0, 10, 1, &iterations));
|
|
params->AddParam(new Param<float>("damping", damping, 0.0, 1.0, 0.001, &damping));
|
|
params->AddParam(new Param<float>("gravity", gravity, 0.0, 0.001, 0.0001, &gravity));
|
|
params->AddParam(new Param<int>("ball r", ballr, 1, 20, 1, &ballr));
|
|
|
|
params->AddParam(new Param<float>("collide spring", collideSpring, 0.0, 1.0, 0.001, &collideSpring));
|
|
params->AddParam(new Param<float>("collide damping", collideDamping, 0.0, 0.1, 0.001, &collideDamping));
|
|
params->AddParam(new Param<float>("collide shear", collideShear, 0.0, 0.1, 0.001, &collideShear));
|
|
params->AddParam(new Param<float>("collide attract", collideAttraction, 0.0, 0.1, 0.001, &collideAttraction));
|
|
}
|
|
|
|
void mainMenu(int i)
|
|
{
|
|
key((unsigned char) i, 0, 0);
|
|
}
|
|
|
|
void initMenus()
|
|
{
|
|
glutCreateMenu(mainMenu);
|
|
glutAddMenuEntry("Reset block [g]", 'g');
|
|
glutAddMenuEntry("Reset random [a]", 'a');
|
|
glutAddMenuEntry("Add sphere [e]", 'e');
|
|
glutAddMenuEntry("Shoot ball [b]", 'b');
|
|
glutAddMenuEntry("View mode [v]", 'v');
|
|
glutAddMenuEntry("Move cursor mode [m]", 'm');
|
|
glutAddMenuEntry("Toggle point rendering [p]", 'p');
|
|
glutAddMenuEntry("Toggle Bullet simulation[s]", 's');
|
|
glutAddMenuEntry("Toggle animation [ ]", ' ');
|
|
glutAddMenuEntry("Step animation [ret]", 13);
|
|
glutAddMenuEntry("Toggle sliders [h]", 'h');
|
|
glutAddMenuEntry("Quit (esc)", '\033');
|
|
glutAttachMenu(GLUT_RIGHT_BUTTON);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Program main
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
// numParticles = 65536*2;
|
|
// numParticles = 65536;
|
|
// numParticles = 32768;
|
|
// numParticles = 8192;
|
|
// numParticles = 4096;
|
|
numParticles = 2048;
|
|
// numParticles = 1024;
|
|
// numParticles = 256;
|
|
// numParticles = 32;
|
|
// numParticles = 2;
|
|
uint gridDim = 64;
|
|
numIterations = 0;
|
|
|
|
gridSize.x = gridSize.y = gridSize.z = gridDim;
|
|
printf("grid: %d x %d x %d = %d cells\n", gridSize.x, gridSize.y, gridSize.z, gridSize.x*gridSize.y*gridSize.z);
|
|
|
|
cudaInit(argc, argv);
|
|
|
|
glutInit(&argc, argv);
|
|
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
|
|
glutInitWindowSize(640, 480);
|
|
glutCreateWindow("CUDA particles");
|
|
|
|
initGL();
|
|
init(numParticles, gridSize);
|
|
initParams();
|
|
initMenus();
|
|
|
|
glutDisplayFunc(display);
|
|
glutReshapeFunc(reshape);
|
|
glutMouseFunc(mouse);
|
|
glutMotionFunc(motion);
|
|
glutKeyboardFunc(key);
|
|
glutSpecialFunc(special);
|
|
glutIdleFunc(idle);
|
|
|
|
glutMainLoop();
|
|
|
|
if (psystem)
|
|
delete psystem;
|
|
|
|
return 0;
|
|
}
|