added OpenCL cloth demo, contributed by AMD.
updated GpuSoftBodySolvers updated DirectCompute cloth demo
This commit is contained in:
470
Demos/OpenCLClothDemo/cl_cloth_demo.cpp
Normal file
470
Demos/OpenCLClothDemo/cl_cloth_demo.cpp
Normal file
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
Bullet Continuous Collision Detection and Physics Library
|
||||
Copyright (c) 2008 Advanced Micro Devices
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it freely,
|
||||
subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <GL/glew.h>
|
||||
#endif
|
||||
|
||||
#include "clstuff.h"
|
||||
#include "gl_win.h"
|
||||
#include "cloth.h"
|
||||
|
||||
#define USE_GPU_SOLVER
|
||||
|
||||
|
||||
const int numFlags = 5;
|
||||
const int clothWidth = 40;
|
||||
const int clothHeight = 60;//60;
|
||||
float _windAngle = 1.0;//0.4;
|
||||
float _windStrength = 15;
|
||||
|
||||
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
|
||||
#include "btBulletDynamicsCommon.h"
|
||||
#include "LinearMath/btHashMap.h"
|
||||
#include "BulletSoftBody/btSoftRigidDynamicsWorld.h"
|
||||
#include "vectormath/vmInclude.h"
|
||||
#include "BulletMultiThreaded/GpuSoftBodySolvers/CPU/btSoftBodySolver_CPU.h"
|
||||
#include "BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/btSoftBodySolver_OpenCL.h"
|
||||
|
||||
using Vectormath::Aos::Vector3;
|
||||
|
||||
class piece_of_cloth;
|
||||
class btBroadphaseInterface;
|
||||
class btCollisionShape;
|
||||
class btOverlappingPairCache;
|
||||
class btCollisionDispatcher;
|
||||
class btConstraintSolver;
|
||||
struct btCollisionAlgorithmCreateFunc;
|
||||
class btDefaultCollisionConfiguration;
|
||||
|
||||
namespace Vectormath
|
||||
{
|
||||
namespace Aos
|
||||
{
|
||||
class Transform3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
btAlignedObjectArray<btCollisionShape*> m_collisionShapes;
|
||||
btBroadphaseInterface* m_broadphase;
|
||||
btCollisionDispatcher* m_dispatcher;
|
||||
btConstraintSolver* m_solver;
|
||||
btDefaultCollisionConfiguration* m_collisionConfiguration;
|
||||
|
||||
btCPUSoftBodySolver *g_cpuSolver = NULL;
|
||||
btOpenCLSoftBodySolver *g_openCLSolver = NULL;
|
||||
|
||||
btSoftBodySolver *g_solver = NULL;
|
||||
|
||||
btAlignedObjectArray<btSoftBody *> m_flags;
|
||||
btSoftRigidDynamicsWorld* m_dynamicsWorld;
|
||||
btAlignedObjectArray<piece_of_cloth> cloths;
|
||||
|
||||
extern cl_context g_cxMainContext;
|
||||
extern cl_device_id g_cdDevice;
|
||||
extern cl_command_queue g_cqCommandQue;
|
||||
|
||||
|
||||
const float flagSpacing = 30.f;
|
||||
|
||||
|
||||
// Helper to test and add links correctly.
|
||||
// Records links that have already been generated
|
||||
static bool testAndAddLink( btAlignedObjectArray<int> &trianglesForLinks, btSoftBody *softBody, int triangle, int *triangleVertexIndexArray, int numVertices, int vertex0, int vertex1, int nonLinkVertex, btSoftBody::Material *structuralMaterial, bool createBendLinks, btSoftBody::Material *bendMaterial )
|
||||
{
|
||||
if( trianglesForLinks[ numVertices * vertex0 + vertex1 ] >= 0 && createBendLinks)
|
||||
{
|
||||
// Already have link so find other triangle and generate cross link
|
||||
|
||||
int otherTriangle = trianglesForLinks[numVertices * vertex0 + vertex1];
|
||||
int otherIndices[3] = {triangleVertexIndexArray[otherTriangle * 3], triangleVertexIndexArray[otherTriangle * 3 + 1], triangleVertexIndexArray[otherTriangle * 3 + 2]};
|
||||
|
||||
int nodeA;
|
||||
// Test all links of the other triangle against this link. The one that's not part of it is what we want.
|
||||
if( otherIndices[0] != vertex0 && otherIndices[0] != vertex1 )
|
||||
nodeA = otherIndices[0];
|
||||
if( otherIndices[1] != vertex0 && otherIndices[1] != vertex1 )
|
||||
nodeA = otherIndices[1];
|
||||
if( otherIndices[2] != vertex0 && otherIndices[2] != vertex1 )
|
||||
nodeA = otherIndices[2];
|
||||
|
||||
softBody->appendLink( nodeA, nonLinkVertex, bendMaterial );
|
||||
} else {
|
||||
// Don't yet have link so create it
|
||||
softBody->appendLink( vertex0, vertex1, structuralMaterial );
|
||||
|
||||
// If we added a new link, set the triangle array
|
||||
trianglesForLinks[numVertices * vertex0 + vertex1] = triangle;
|
||||
trianglesForLinks[numVertices * vertex1 + vertex0] = triangle;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
btSoftBody *createFromIndexedMesh( btVector3 *vertexArray, int numVertices, int *triangleVertexIndexArray, int numTriangles, bool createBendLinks )
|
||||
{
|
||||
btSoftBody* softBody = new btSoftBody(&(m_dynamicsWorld->getWorldInfo()), numVertices, vertexArray, 0);
|
||||
btSoftBody::Material * structuralMaterial = softBody->appendMaterial();
|
||||
btSoftBody::Material * bendMaterial;
|
||||
if( createBendLinks )
|
||||
{
|
||||
bendMaterial = softBody->appendMaterial();
|
||||
bendMaterial->m_kLST = 0.7;
|
||||
} else {
|
||||
bendMaterial = NULL;
|
||||
}
|
||||
structuralMaterial->m_kLST = 1.0;
|
||||
|
||||
|
||||
// List of values for each link saying which triangle is associated with that link
|
||||
// -1 to start. Once a value is entered we know the "other" triangle
|
||||
// and can add a link across the link
|
||||
btAlignedObjectArray<int> triangleForLinks;
|
||||
triangleForLinks.resize( numVertices * numVertices, -1 );
|
||||
int numLinks = 0;
|
||||
for( int triangle = 0; triangle < numTriangles; ++triangle )
|
||||
{
|
||||
int index[3] = {triangleVertexIndexArray[triangle * 3], triangleVertexIndexArray[triangle * 3 + 1], triangleVertexIndexArray[triangle * 3 + 2]};
|
||||
softBody->appendFace( index[0], index[1], index[2] );
|
||||
|
||||
// Generate the structural links directly from the triangles
|
||||
testAndAddLink( triangleForLinks, softBody, triangle, triangleVertexIndexArray, numVertices, index[0], index[1], index[2], structuralMaterial, createBendLinks, bendMaterial );
|
||||
testAndAddLink( triangleForLinks, softBody, triangle, triangleVertexIndexArray, numVertices, index[1], index[2], index[0], structuralMaterial, createBendLinks, bendMaterial );
|
||||
testAndAddLink( triangleForLinks, softBody, triangle, triangleVertexIndexArray, numVertices, index[2], index[0], index[1], structuralMaterial, createBendLinks, bendMaterial);
|
||||
}
|
||||
|
||||
return softBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sequence of flag objects and add them to the world.
|
||||
*/
|
||||
void createFlag( btSoftBodySolver &solver, int width, int height, btAlignedObjectArray<btSoftBody *> &flags )
|
||||
{
|
||||
// First create a triangle mesh to represent a flag
|
||||
|
||||
using Vectormath::Aos::Matrix3;
|
||||
using Vectormath::Aos::Vector3;
|
||||
|
||||
// Allocate a simple mesh consisting of a vertex array and a triangle index array
|
||||
btIndexedMesh mesh;
|
||||
mesh.m_numVertices = width*height;
|
||||
mesh.m_numTriangles = 2*(width-1)*(height-1);
|
||||
|
||||
btVector3 *vertexArray = new btVector3[mesh.m_numVertices];
|
||||
|
||||
mesh.m_vertexBase = reinterpret_cast<const unsigned char*>(vertexArray);
|
||||
int *triangleVertexIndexArray = new int[3*mesh.m_numTriangles];
|
||||
mesh.m_triangleIndexBase = reinterpret_cast<const unsigned char*>(triangleVertexIndexArray);
|
||||
mesh.m_triangleIndexStride = sizeof(int)*3;
|
||||
mesh.m_vertexStride = sizeof(Vector3);
|
||||
|
||||
// Generate normalised object space vertex coordinates for a rectangular flag
|
||||
float zCoordinate = 0.0f;
|
||||
|
||||
Matrix3 defaultScale(Vector3(5.f, 0.f, 0.f), Vector3(0.f, 20.f, 0.f), Vector3(0.f, 0.f, 1.f));
|
||||
for( int y = 0; y < height; ++y )
|
||||
{
|
||||
float yCoordinate = y*2.0f/float(height) - 1.0f;
|
||||
for( int x = 0; x < width; ++x )
|
||||
{
|
||||
float xCoordinate = x*2.0f/float(width) - 1.0f;
|
||||
|
||||
Vector3 vertex(xCoordinate, yCoordinate, zCoordinate);
|
||||
Vector3 transformedVertex = defaultScale*vertex;
|
||||
|
||||
vertexArray[y*width + x] = btVector3(transformedVertex.getX(), transformedVertex.getY(), transformedVertex.getZ() );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Generate vertex indices for triangles
|
||||
for( int y = 0; y < (height-1); ++y )
|
||||
{
|
||||
for( int x = 0; x < (width-1); ++x )
|
||||
{
|
||||
// Triangle 0
|
||||
// Top left of square on mesh
|
||||
{
|
||||
int vertex0 = y*width + x;
|
||||
int vertex1 = vertex0 + 1;
|
||||
int vertex2 = vertex0 + width;
|
||||
int triangleIndex = 2*y*(width-1) + 2*x;
|
||||
triangleVertexIndexArray[(mesh.m_triangleIndexStride*triangleIndex)/sizeof(int)] = vertex0;
|
||||
triangleVertexIndexArray[(mesh.m_triangleIndexStride*triangleIndex+1)/sizeof(int)+1] = vertex1;
|
||||
triangleVertexIndexArray[(mesh.m_triangleIndexStride*triangleIndex+2)/sizeof(int)+2] = vertex2;
|
||||
}
|
||||
|
||||
// Triangle 1
|
||||
// Bottom right of square on mesh
|
||||
{
|
||||
int vertex0 = y*width + x + 1;
|
||||
int vertex1 = vertex0 + width;
|
||||
int vertex2 = vertex1 - 1;
|
||||
int triangleIndex = 2*y*(width-1) + 2*x + 1;
|
||||
triangleVertexIndexArray[(mesh.m_triangleIndexStride*triangleIndex)/sizeof(int)] = vertex0;
|
||||
triangleVertexIndexArray[(mesh.m_triangleIndexStride*triangleIndex)/sizeof(int)+1] = vertex1;
|
||||
triangleVertexIndexArray[(mesh.m_triangleIndexStride*triangleIndex)/sizeof(int)+2] = vertex2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float rotateAngleRoundZ = 0.5;
|
||||
float rotateAngleRoundX = 0.5;
|
||||
btMatrix3x3 defaultRotate;
|
||||
defaultRotate[0] = btVector3(cos(rotateAngleRoundZ), sin(rotateAngleRoundZ), 0.f);
|
||||
defaultRotate[1] = btVector3(-sin(rotateAngleRoundZ), cos(rotateAngleRoundZ), 0.f);
|
||||
defaultRotate[2] = btVector3(0.f, 0.f, 1.f);
|
||||
btMatrix3x3 defaultRotateX;
|
||||
defaultRotateX[0] = btVector3(1.f, 0.f, 0.f);
|
||||
defaultRotateX[1] = btVector3( 0.f, cos(rotateAngleRoundX), sin(rotateAngleRoundX));
|
||||
defaultRotateX[2] = btVector3(0.f, -sin(rotateAngleRoundX), cos(rotateAngleRoundX));
|
||||
|
||||
btMatrix3x3 defaultRotateAndScale( (defaultRotateX*defaultRotate) );
|
||||
|
||||
|
||||
// Construct the sequence flags applying a slightly different translation to each one to arrange them
|
||||
// appropriately in the scene.
|
||||
for( int i = 0; i < numFlags; ++i )
|
||||
{
|
||||
float zTranslate = flagSpacing * (i-numFlags/2);
|
||||
|
||||
btVector3 defaultTranslate(0.f, 20.f, zTranslate);
|
||||
|
||||
btTransform transform( defaultRotateAndScale, defaultTranslate );
|
||||
|
||||
|
||||
btSoftBody *softBody = createFromIndexedMesh( vertexArray, mesh.m_numVertices, triangleVertexIndexArray, mesh.m_numTriangles, true );
|
||||
|
||||
|
||||
for( int i = 0; i < mesh.m_numVertices; ++i )
|
||||
{
|
||||
softBody->setMass(i, 10.f/mesh.m_numVertices);
|
||||
}
|
||||
softBody->setMass((height-1)*(width), 0.f);
|
||||
softBody->setMass((height-1)*(width) + width - 1, 0.f);
|
||||
softBody->setMass((height-1)*width + width/2, 0.f);
|
||||
softBody->m_cfg.collisions = btSoftBody::fCollision::CL_SS+btSoftBody::fCollision::CL_RS;
|
||||
|
||||
|
||||
flags.push_back( softBody );
|
||||
|
||||
softBody->transform( transform );
|
||||
|
||||
m_dynamicsWorld->addSoftBody( softBody );
|
||||
}
|
||||
|
||||
delete [] vertexArray;
|
||||
delete [] triangleVertexIndexArray;
|
||||
}
|
||||
|
||||
|
||||
void updatePhysicsWorld()
|
||||
{
|
||||
static int counter = 0;
|
||||
|
||||
// Change wind velocity a bit based on a frame counter
|
||||
if( (counter % 400) == 0 )
|
||||
{
|
||||
_windAngle = (_windAngle + 0.05f);
|
||||
if( _windAngle > (2*3.141) )
|
||||
_windAngle = 0;
|
||||
|
||||
for( int flagIndex = 0; flagIndex < m_flags.size(); ++flagIndex )
|
||||
{
|
||||
btSoftBody *cloth = 0;
|
||||
|
||||
cloth = m_flags[flagIndex];
|
||||
|
||||
float localWind = _windAngle + 0.5*(((float(rand())/RAND_MAX))-0.1);
|
||||
float xCoordinate = cos(localWind)*_windStrength;
|
||||
float zCoordinate = sin(localWind)*_windStrength;
|
||||
|
||||
cloth->setWindVelocity( btVector3(xCoordinate, 0, zCoordinate) );
|
||||
}
|
||||
}
|
||||
|
||||
//btVector3 origin( capCollider->getWorldTransform().getOrigin() );
|
||||
//origin.setX( origin.getX() + 0.05 );
|
||||
//capCollider->getWorldTransform().setOrigin( origin );
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
void initBullet(void)
|
||||
{
|
||||
|
||||
#ifdef USE_GPU_SOLVER
|
||||
g_openCLSolver = new btOpenCLSoftBodySolver( g_cqCommandQue, g_cxMainContext);
|
||||
g_solver = g_openCLSolver;
|
||||
#else
|
||||
g_cpuSolver = new btCPUSoftBodySolver;
|
||||
g_solver = g_cpuSolver;
|
||||
#endif
|
||||
|
||||
m_collisionConfiguration = new btDefaultCollisionConfiguration();
|
||||
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
|
||||
m_broadphase = new btDbvtBroadphase();
|
||||
btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver;
|
||||
m_solver = sol;
|
||||
|
||||
m_dynamicsWorld = new btSoftRigidDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration, g_solver);
|
||||
|
||||
m_dynamicsWorld->setGravity(btVector3(0,-10,0));
|
||||
btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(50.),btScalar(50.),btScalar(50.)));
|
||||
m_collisionShapes.push_back(groundShape);
|
||||
btTransform groundTransform;
|
||||
groundTransform.setIdentity();
|
||||
groundTransform.setOrigin(btVector3(0,-50,0));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
m_dynamicsWorld->getWorldInfo().air_density = (btScalar)1.2;
|
||||
m_dynamicsWorld->getWorldInfo().water_density = 0;
|
||||
m_dynamicsWorld->getWorldInfo().water_offset = 0;
|
||||
m_dynamicsWorld->getWorldInfo().water_normal = btVector3(0,0,0);
|
||||
m_dynamicsWorld->getWorldInfo().m_gravity.setValue(0,-10,0);
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
{
|
||||
btScalar mass(0.);
|
||||
|
||||
//rigidbody is dynamic if and only if mass is non zero, otherwise static
|
||||
bool isDynamic = (mass != 0.f);
|
||||
|
||||
btVector3 localInertia(0,0,0);
|
||||
if (isDynamic)
|
||||
groundShape->calculateLocalInertia(mass,localInertia);
|
||||
|
||||
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
|
||||
btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform);
|
||||
btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,groundShape,localInertia);
|
||||
btRigidBody* body = new btRigidBody(rbInfo);
|
||||
|
||||
//add the body to the dynamics world
|
||||
m_dynamicsWorld->addRigidBody(body);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_GPU_SOLVER
|
||||
createFlag( *g_openCLSolver, clothWidth, clothHeight, m_flags );
|
||||
#else
|
||||
createFlag( *g_cpuSolver, clothWidth, clothHeight, m_flags );
|
||||
#endif
|
||||
|
||||
// Create output buffer descriptions for ecah flag
|
||||
// These describe where the simulation should send output data to
|
||||
for( int flagIndex = 0; flagIndex < m_flags.size(); ++flagIndex )
|
||||
{
|
||||
// m_flags[flagIndex]->setWindVelocity( Vectormath::Aos::Vector3( 0.f, 0.f, 15.f ) );
|
||||
|
||||
// In this case we have a DX11 output buffer with a vertex at index 0, 8, 16 and so on as well as a normal at 3, 11, 19 etc.
|
||||
// Copies will be performed GPU-side directly into the output buffer
|
||||
|
||||
btCPUVertexBufferDescriptor *vertexBufferDescriptor = new btCPUVertexBufferDescriptor(reinterpret_cast< float* >(cloths[flagIndex].cpu_buffer), 0, 8, 3, 8);
|
||||
cloths[flagIndex].m_vertexBufferDescriptor = vertexBufferDescriptor;
|
||||
}
|
||||
|
||||
|
||||
g_solver->optimize( m_dynamicsWorld->getSoftBodyArray() );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
btClock m_clock;
|
||||
|
||||
void doFlags()
|
||||
{
|
||||
//float ms = getDeltaTimeMicroseconds();
|
||||
btScalar dt = (btScalar)m_clock.getTimeMicroseconds();
|
||||
m_clock.reset();
|
||||
|
||||
///step the simulation
|
||||
if( m_dynamicsWorld )
|
||||
{
|
||||
m_dynamicsWorld->stepSimulation(dt/1000000.);
|
||||
static int frameCount = 0;
|
||||
frameCount++;
|
||||
if (frameCount==100)
|
||||
{
|
||||
m_dynamicsWorld->stepSimulation(1./60.,0);
|
||||
CProfileManager::dumpAll();
|
||||
}
|
||||
updatePhysicsWorld();
|
||||
}
|
||||
|
||||
|
||||
for( int flagIndex = 0; flagIndex < m_flags.size(); ++flagIndex )
|
||||
{
|
||||
g_solver->copySoftBodyToVertexBuffer( m_flags[flagIndex], cloths[flagIndex].m_vertexBufferDescriptor );
|
||||
cloths[flagIndex].draw();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
|
||||
initCL();
|
||||
|
||||
cloths.resize(numFlags);
|
||||
|
||||
for( int flagIndex = 0; flagIndex < numFlags; ++flagIndex )
|
||||
{
|
||||
cloths[flagIndex].create_buffers(clothWidth, clothHeight);
|
||||
}
|
||||
|
||||
initBullet();
|
||||
m_dynamicsWorld->stepSimulation(1./60.,0);
|
||||
|
||||
preInitGL(argc, argv);
|
||||
|
||||
std::string flagTexs[] = {
|
||||
"atiFlag.bmp",
|
||||
"amdFlag.bmp",
|
||||
};
|
||||
int numFlagTexs = 2;
|
||||
|
||||
for( int flagIndex = 0; flagIndex < numFlags; ++flagIndex )
|
||||
{
|
||||
cloths[flagIndex].create_texture(flagTexs[flagIndex % numFlagTexs]);
|
||||
cloths[flagIndex].x_offset = 0;
|
||||
cloths[flagIndex].y_offset = 0;
|
||||
cloths[flagIndex].z_offset = 0;
|
||||
}
|
||||
|
||||
goGL();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user