save the following code as
mycloth.cpp to the NxTutorials code folder
/*----------------------------------------------------------------------------*\
|
| AGEIA PhysX Technology
|
|
www.ageia.com
|
\*----------------------------------------------------------------------------*/
//Test Cloth Implementation
/*
#if defined WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX //suppress windows' global min,max macros.
#include <windows.h> //needed for gl.h
#endif
*/
//#include <GL/glut.h>
//#include "nxOgre.h"
//#include <stdio.h>
#include "MyCloth.h"
//#include "nxOgre.h"
#include "Ogre.h"
/*
#include "Stream.h"
//#include "wavefront.h"
//#include "BmpLoader.h"
#include "NxCooking.h"
*/
#include "nxOgre_fluid.h"
#include "nxOgre_scene.h"
#include "nxOgre_converters.h"
#include "nxOgre_util_stream.h"
#include "NxCooking.h"
using namespace Ogre;
#define GLfloat float
#define VERTEX_BINDING 0
#define TEXCOORDBINDING 2
#define NORMAL_BINDING 1
#define TEAR_MEMORY_FACTOR 2
// -----------------------------------------------------------------------
MyCloth::MyCloth(NxScene *scene, NxClothDesc &desc, char *objFileName, NxReal scale)
{
mInitDone = false;
mTexId = 0;
mTexCoords = NULL;
NxClothMeshDesc meshDesc;
generateObjMeshDesc(meshDesc, objFileName, scale);
// todo: handle failure
init(scene, desc, meshDesc);
}
// -----------------------------------------------------------------------
MyCloth::MyCloth(NxScene *scene, NxClothDesc &desc, NxReal w, NxReal h, NxReal d, char *texFilename, bool tearLines)
{
mInitDone = false;
mTexId = 0;
mTexCoords = NULL;
NxClothMeshDesc meshDesc;
generateRegularMeshDesc(meshDesc, w, h, d, texFilename != NULL, tearLines);
init(scene, desc, meshDesc);
if (texFilename)
createTexture(texFilename);
}
// -----------------------------------------------------------------------
void MyCloth::init(NxScene *scene, NxClothDesc &desc, NxClothMeshDesc &meshDesc)
{
mScene = scene;
if (desc.flags & NX_CLF_HARDWARE) {
// cook the mesh for the usage on the PPU
meshDesc.target = NX_CLOTH_MESH_PPU_ATHENA;
// if we want tearing on the PPU we must tell the cooker
// this way it will generate some space for particles that will be generated during tearing
if (desc.flags & NX_CLF_TEARABLE)
meshDesc.flags |= NX_CLOTH_MESH_TEARABLE;
}
else {
// the cooked mesh will not be simulated on the PPU, only in software
// the software only version of the cooked mesh is smaller and takes less time to cook
meshDesc.target = NX_CLOTH_MESH_SOFTWARE;
}
cookMesh(meshDesc);
//releaseMeshDescBuffers(meshDesc);
allocateReceiveBuffers(meshDesc.numVertices, meshDesc.numTriangles);
desc.clothMesh = mClothMesh;
desc.meshData = mReceiveBuffers;
mCloth = scene->createCloth(desc);
mInitDone = true;
}
// -----------------------------------------------------------------------
MyCloth::~MyCloth()
{
if (mInitDone) {
mScene->releaseCloth(*mCloth);
mScene->getPhysicsSDK().releaseClothMesh(*mClothMesh);
releaseReceiveBuffers();
if (mTexCoords) free(mTexCoords);
}
}
// -----------------------------------------------------------------------
bool MyCloth::generateObjMeshDesc(NxClothMeshDesc &desc, char *filename, NxReal scale)
{
//not jet implemeted
return true;
}
// -----------------------------------------------------------------------
void MyCloth::generateRegularMeshDesc(NxClothMeshDesc &desc, NxReal w, NxReal h, NxReal d, bool texCoords, bool tearLines)
{
int numX = (int)(w / d) + 1;
int numY = (int)(h / d) + 1;
desc.numVertices = (numX+1) * (numY+1);
desc.numTriangles = numX*numY*2;
desc.pointStrideBytes = sizeof(NxVec3);
desc.triangleStrideBytes = 3*sizeof(NxU32);
desc.vertexMassStrideBytes = sizeof(NxReal);
desc.vertexFlagStrideBytes = sizeof(NxU32);
desc.points = (NxVec3*)malloc(sizeof(NxVec3)*desc.numVertices);
desc.triangles = (NxU32*)malloc(sizeof(NxU32)*desc.numTriangles*3);
desc.vertexMasses = 0;
desc.vertexFlags = 0;
desc.flags = 0;
mNumvertY = numY;
mNumvertX = numX;
mVertexCount = desc.numVertices;
mVec3Buffsize = sizeof(NxVec3) * mVertexCount;
mDirectDistance = d;
meshName = "FlagMesh";
// create mesh and sub-meshes
mMesh = Ogre::MeshManager::getSingleton().createManual(meshName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME) ;
//Init index buffer
mIndexCount = 6 * 2 * (mNumvertX)*(mNumvertY);
HardwareIndexBufferSharedPtr indexHardBuffer =
HardwareBufferManager::getSingleton().createIndexBuffer(
HardwareIndexBuffer::IT_32BIT,
mIndexCount,
HardwareBuffer::HBU_STATIC,
false); // use shadow buffer
mIndex = new uint [mIndexCount];
int i,j;
NxU32 *id = (NxU32*)mIndex;
for (i = 0; i < numY; i++) {
for (j = 0; j < numX; j++) {
NxU32 i0 = i * (numX+1) + j;
NxU32 i1 = i0 + 1;
NxU32 i2 = i0 + (numX+1);
NxU32 i3 = i2 + 1;
if ((j+i)%2) {
*id++ = i0; *id++ = i2; *id++ = i1;
*id++ = i1; *id++ = i2; *id++ = i3;
}
else {
*id++ = i0; *id++ = i2; *id++ = i3;
*id++ = i0; *id++ = i3; *id++ = i1;
}
}
}
indexHardBuffer->writeData(0,
sizeof(ushort) * mIndexCount, // size
mIndex, // source
true); // discard?
IndexData* mIndexData = new IndexData;
mIndexData->indexStart = 0;
mIndexData->indexCount = mIndexCount;
mIndexData->indexBuffer = indexHardBuffer;
//Init vertex buffer
mVertexData= new VertexData;
mVertexData->vertexStart = 0;
mVertexData->vertexCount = mVertexCount;
VertexDeclaration* decl = mVertexData->vertexDeclaration;
VertexBufferBinding* bind = mVertexData->vertexBufferBinding;
decl->addElement(VERTEX_BINDING, 0, VET_FLOAT3, VES_POSITION);
decl->addElement(NORMAL_BINDING, 0, VET_FLOAT3, VES_NORMAL);
decl->addElement(TEXCOORDBINDING, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES);
HardwareVertexBufferSharedPtr posVertexHardBuffer;
HardwareVertexBufferSharedPtr texoordHardBuffer;
HardwareVertexBufferSharedPtr normVertexHardBuffer;
// Create shared vertex buffer
posVertexHardBuffer =
HardwareBufferManager::getSingleton().createVertexBuffer(
sizeof(Vector3),//decl->getVertexSize(VERTEX_BINDING),
mVertexCount,
//HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);
HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE,
false); // use shadow buffer
bind->setBinding(VERTEX_BINDING, posVertexHardBuffer);
// Prepare buffer for normals - write only
normVertexHardBuffer =
HardwareBufferManager::getSingleton().createVertexBuffer(
sizeof(Vector3),//decl->getVertexSize(NORMAL_BINDING),
mVertexCount,
HardwareBuffer::HBU_STATIC, //HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE,
false); // use shadow buffer
bind->setBinding(NORMAL_BINDING, normVertexHardBuffer);
texoordHardBuffer =
HardwareBufferManager::getSingleton().createVertexBuffer(
sizeof(Vector2),//decl->getVertexSize(TEXCOORDBINDING),
mVertexCount,
HardwareBuffer::HBU_STATIC,
false); // use shadow buffer
bind->setBinding(TEXCOORDBINDING, texoordHardBuffer);
//Init vertex buffer:
mVertexBuffer = new Vector3[mVertexCount];
mNormalBuffer = new Vector3[mVertexCount];
mtexoordsBuffer = new Vector2[mVertexCount];
uint ArrayPos = 0;
Real y_distance = 0.0f;
Vector3 Vertex;
for (uint j = 0; j <= mNumvertY; j++)
{
Real x_distance = 0.0f;
const Real y_coord = static_cast<Real> (j) / (mNumvertY);
for (uint i = 0; i <= mNumvertX; i++)
{
Vertex = Vector3 (x_distance,0.0f,y_distance);
mVertexBuffer[ArrayPos] = Vertex;
mNormalBuffer[ArrayPos] = Vertex.crossProduct(Vertex);
mNormalBuffer[ArrayPos].normalise();
mtexoordsBuffer[ArrayPos] = Vector2 (1 - (static_cast<Real> (i) / (mNumvertX)),
1 - y_coord);
x_distance += mDirectDistance;
ArrayPos++;
}
y_distance += mDirectDistance;
}
posVertexHardBuffer->writeData(0,
mVec3Buffsize, // size
mVertexBuffer, // source
true); // discard?
normVertexHardBuffer->writeData(0,
mVec3Buffsize, // size
mNormalBuffer, // source
true); // discard?
texoordHardBuffer->writeData(0,
sizeof(Vector2) * mVertexCount, // size
mtexoordsBuffer, // source
true); // discard?
mFlagsubMesh= mMesh->createSubMesh();
mFlagsubMesh->useSharedVertices = false;
mFlagsubMesh->vertexData = mVertexData;
mFlagsubMesh->indexData = mIndexData;
mMaterialName = "nx.flag";
mFlagsubMesh->setMaterialName (mMaterialName);
mFlagsubMesh->operationType = RenderOperation::OT_TRIANGLE_LIST;
AxisAlignedBox meshBounds(-10.0f,
- 750.0f -10.0f,
-10.0f,
mNumvertX*mDirectDistance + 10.0f,
mNumvertY*mDirectDistance + 10.0f,
10.0f);
mMesh->_setBounds(meshBounds);
mMesh->load();
mMesh->touch();
desc.points = (NxVec3*)mVertexBuffer;
desc.triangles = (NxU32*)mIndex;
// mNumTexCoords = desc.numVertices;
// generate tear lines if necessary
if(tearLines)
generateTearLines(desc, numX + 1, numY + 1);
}
// -----------------------------------------------------------------------
void MyCloth::generateTearLines(NxClothMeshDesc& desc, NxU32 w, NxU32 h)
{
// allocate flag buffer
if(desc.vertexFlags == 0)
desc.vertexFlags = malloc(sizeof(NxU32)*desc.numVertices);
// create tear lines
NxU32* flags = (NxU32*)desc.vertexFlags;
NxU32 y;
for(y = 0; y < h; y++)
{
NxU32 x;
for(x = 0; x < w; x++)
{
if(((x + y) % 16 == 0) || ((x - y + 16) % 16 == 0))
flags[y * w + x] = NX_CLOTH_VERTEX_TEARABLE;
else
flags[y * w + x] = 0;
}
}
}
// -----------------------------------------------------------------------
void MyCloth::releaseMeshDescBuffers(const NxClothMeshDesc& desc)
{
NxVec3* p = (NxVec3*)desc.points;
NxU32* t = (NxU32*)desc.triangles;
NxReal* m = (NxReal*)desc.vertexMasses;
NxU32* f = (NxU32*)desc.vertexFlags;
free(p);
free(t);
free(m);
free(f);
}
// -----------------------------------------------------------------------
bool MyCloth::cookMesh(NxClothMeshDesc& desc)
{
// we cook the mesh on the fly through a memory stream
// we could also use a file stream and pre-cook the mesh
MemoryWriteBuffer wb;
// NxInitCooking();
if (!NxCookClothMesh(desc, wb)) //!! problem im debug mod vermutlich alloc im nxogrestream...
return false;
MemoryReadBuffer rb(wb.data);
mClothMesh = mScene->getPhysicsSDK().createClothMesh(rb);
return true;
}
// -----------------------------------------------------------------------
void MyCloth::allocateReceiveBuffers(int numVertices, int numTriangles)
{
// here we setup the buffers through which the SDK returns the dynamic cloth data
// we reserve more memory for vertices than the initial mesh takes
// because tearing creates new vertices
// the SDK only tears cloth as long as there is room in these buffers
NxU32 maxVertices = TEAR_MEMORY_FACTOR * numVertices;
mReceiveBuffers.verticesPosBegin = (NxVec3*)malloc(sizeof(NxVec3)*maxVertices);
mReceiveBuffers.verticesNormalBegin = (NxVec3*)malloc(sizeof(NxVec3)*maxVertices);
mReceiveBuffers.verticesPosByteStride = sizeof(NxVec3);
mReceiveBuffers.verticesNormalByteStride = sizeof(NxVec3);
mReceiveBuffers.maxVertices = maxVertices;
mReceiveBuffers.numVerticesPtr = (NxU32*)malloc(sizeof(NxU32));
// the number of triangles is constant, even if the cloth is torn
NxU32 maxIndices = 3*numTriangles;
mReceiveBuffers.indicesBegin = (NxU32*)malloc(sizeof(NxU32)*maxIndices);
mReceiveBuffers.indicesByteStride = sizeof(NxU32);
mReceiveBuffers.maxIndices = maxIndices;
mReceiveBuffers.numIndicesPtr = (NxU32*)malloc(sizeof(NxU32));
// the parent index information would be needed if we used textured cloth
NxU32 maxParentIndices = maxVertices;
mReceiveBuffers.parentIndicesBegin = (NxU32*)malloc(sizeof(NxU32)*maxParentIndices);
mReceiveBuffers.parentIndicesByteStride = sizeof(NxU32);
mReceiveBuffers.maxParentIndices = maxParentIndices;
mReceiveBuffers.numParentIndicesPtr = (NxU32*)malloc(sizeof(NxU32));
// init the buffers in case we want to draw the mesh
// before the SDK as filled in the correct values
*mReceiveBuffers.numVerticesPtr = 0;
*mReceiveBuffers.numIndicesPtr = 0;
}
// -----------------------------------------------------------------------
void MyCloth::releaseReceiveBuffers()
{
NxVec3* vp;
NxU32* up;
vp = (NxVec3*)mReceiveBuffers.verticesPosBegin; free(vp);
vp = (NxVec3*)mReceiveBuffers.verticesNormalBegin; free(vp);
up = (NxU32*)mReceiveBuffers.numVerticesPtr; free(up);
up = (NxU32*)mReceiveBuffers.indicesBegin; free(up);
up = (NxU32*)mReceiveBuffers.numIndicesPtr; free(up);
up = (NxU32*)mReceiveBuffers.parentIndicesBegin; free(up);
up = (NxU32*)mReceiveBuffers.numParentIndicesPtr; free(up);
}
// -----------------------------------------------------------------------
void MyCloth::draw(bool shadows)
{
NxU32 numVertices = *mReceiveBuffers.numVerticesPtr;
NxU32 numTriangles = *mReceiveBuffers.numIndicesPtr / 3;
// copy positions and indices
NxVec3 *vSrc = (NxVec3*)mReceiveBuffers.verticesPosBegin;
NxVec3 *vDest = (NxVec3*)mVertexBuffer;
for (int i = 0; i < mVertexCount; i++, vDest++, vSrc++)
*vDest = (*vSrc);
/*
// memcpy((NxU32*)desc.triangles, wo.mIndices, sizeof(NxU32)*desc.numTriangles*3);
NxVec3 *vSrc = (NxVec3*)mReceiveBuffers.verticesPosBegin;
NxVec3 *vDest = (NxVec3*)desc.points;
for (int i = 0; i < desc.mVertexCount; i++, vDest++, vSrc++)
*vDest = (*vSrc);
*/
VertexBufferBinding* bind = mVertexData->vertexBufferBinding;
HardwareVertexBufferSharedPtr HardBuffer= bind->getBuffer (VERTEX_BINDING);
HardBuffer->writeData(0,
mVec3Buffsize, // size
mVertexBuffer, // source
true); // discard?
/* here one should calculate the normals again and then put the normals to the hardware buffers
but then you nshould initialise the buffer with HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE instead of HBU_STATIC
HardBuffer = bind->getBuffer (NORMAL_BINDING);
HardBuffer->writeData(0,
mVec3Buffsize, // size
mNormalBuffer, // source
true); // discard?
*/
}
// -----------------------------------------------------------------------
bool MyCloth::createTexture(char *filename)
{
// obsolet
return true;
}
// -----------------------------------------------------------------------
void MyCloth::updateTextureCoordinates()
{
// this is important if i want to support tear lines
// not used until now
NxU32 numVertices = *mReceiveBuffers.numVerticesPtr;
NxU32 *parent = (NxU32 *)mReceiveBuffers.parentIndicesBegin + mNumTexCoords;
for (NxU32 i = mNumTexCoords; i < numVertices; i++) {
mTexCoords[2*i] = mTexCoords[2*(*parent)];
mTexCoords[2*i+1] = mTexCoords[2*(*parent)+1];
}
mNumTexCoords = numVertices;
}