[1] | 1 | /* |
---|
| 2 | ----------------------------------------------------------------------------- |
---|
| 3 | This source file is part of OGRE |
---|
| 4 | (Object-oriented Graphics Rendering Engine) |
---|
| 5 | For the latest info, see http://www.ogre3d.org/ |
---|
| 6 | |
---|
| 7 | Copyright (c) 2000-2006 Torus Knot Software Ltd |
---|
| 8 | Also see acknowledgements in Readme.html |
---|
| 9 | |
---|
| 10 | You may use this sample code for anything you like, it is not covered by the |
---|
| 11 | LGPL like the rest of the engine. |
---|
| 12 | ----------------------------------------------------------------------------- |
---|
| 13 | */ |
---|
| 14 | |
---|
| 15 | |
---|
| 16 | #include "WaterMesh.h" |
---|
| 17 | |
---|
| 18 | #define ANIMATIONS_PER_SECOND 100.0f |
---|
| 19 | |
---|
| 20 | WaterMesh::WaterMesh(const String& meshName, Real planeSize, int complexity) |
---|
| 21 | { |
---|
| 22 | int x,y,b; // I prefer to initialize for() variables inside it, but VC doesn't like it ;( |
---|
| 23 | |
---|
| 24 | this->meshName = meshName ; |
---|
| 25 | this->complexity = complexity ; |
---|
| 26 | numFaces = 2 * complexity * complexity; |
---|
| 27 | numVertices = (complexity + 1) * (complexity + 1) ; |
---|
| 28 | lastTimeStamp = 0 ; |
---|
| 29 | lastAnimationTimeStamp = 0; |
---|
| 30 | lastFrameTime = 0 ; |
---|
| 31 | |
---|
| 32 | // initialize algorithm parameters |
---|
| 33 | PARAM_C = 0.3f ; // ripple speed |
---|
| 34 | PARAM_D = 0.4f ; // distance |
---|
| 35 | PARAM_U = 0.05f ; // viscosity |
---|
| 36 | PARAM_T = 0.13f ; // time |
---|
| 37 | useFakeNormals = false ; |
---|
| 38 | |
---|
| 39 | // allocate space for normal calculation |
---|
| 40 | vNormals = new Vector3[numVertices]; |
---|
| 41 | |
---|
| 42 | // create mesh and submesh |
---|
| 43 | mesh = MeshManager::getSingleton().createManual(meshName, |
---|
| 44 | ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); |
---|
| 45 | subMesh = mesh->createSubMesh(); |
---|
| 46 | subMesh->useSharedVertices=false; |
---|
| 47 | |
---|
| 48 | // Vertex buffers |
---|
| 49 | subMesh->vertexData = new VertexData(); |
---|
| 50 | subMesh->vertexData->vertexStart = 0; |
---|
| 51 | subMesh->vertexData->vertexCount = numVertices; |
---|
| 52 | |
---|
| 53 | VertexDeclaration* vdecl = subMesh->vertexData->vertexDeclaration; |
---|
| 54 | VertexBufferBinding* vbind = subMesh->vertexData->vertexBufferBinding; |
---|
| 55 | |
---|
| 56 | |
---|
| 57 | vdecl->addElement(0, 0, VET_FLOAT3, VES_POSITION); |
---|
| 58 | vdecl->addElement(1, 0, VET_FLOAT3, VES_NORMAL); |
---|
| 59 | vdecl->addElement(2, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES); |
---|
| 60 | |
---|
| 61 | // Prepare buffer for positions - todo: first attempt, slow |
---|
| 62 | posVertexBuffer = |
---|
| 63 | HardwareBufferManager::getSingleton().createVertexBuffer( |
---|
| 64 | 3*sizeof(float), |
---|
| 65 | numVertices, |
---|
| 66 | HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE); |
---|
| 67 | vbind->setBinding(0, posVertexBuffer); |
---|
| 68 | |
---|
| 69 | // Prepare buffer for normals - write only |
---|
| 70 | normVertexBuffer = |
---|
| 71 | HardwareBufferManager::getSingleton().createVertexBuffer( |
---|
| 72 | 3*sizeof(float), |
---|
| 73 | numVertices, |
---|
| 74 | HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE); |
---|
| 75 | vbind->setBinding(1, normVertexBuffer); |
---|
| 76 | |
---|
| 77 | // Prepare texture coords buffer - static one |
---|
| 78 | // todo: optimize to write directly into buffer |
---|
| 79 | float *texcoordsBufData = new float[numVertices*2]; |
---|
| 80 | for(y=0;y<=complexity;y++) { |
---|
| 81 | for(x=0;x<=complexity;x++) { |
---|
| 82 | texcoordsBufData[2*(y*(complexity+1)+x)+0] = (float)x / complexity ; |
---|
| 83 | texcoordsBufData[2*(y*(complexity+1)+x)+1] = 1.0f - ((float)y / (complexity)) ; |
---|
| 84 | } |
---|
| 85 | } |
---|
| 86 | texcoordsVertexBuffer = |
---|
| 87 | HardwareBufferManager::getSingleton().createVertexBuffer( |
---|
| 88 | 2*sizeof(float), |
---|
| 89 | numVertices, |
---|
| 90 | HardwareBuffer::HBU_STATIC_WRITE_ONLY); |
---|
| 91 | texcoordsVertexBuffer->writeData(0, |
---|
| 92 | texcoordsVertexBuffer->getSizeInBytes(), |
---|
| 93 | texcoordsBufData, |
---|
| 94 | true); // true? |
---|
| 95 | delete [] texcoordsBufData; |
---|
| 96 | vbind->setBinding(2, texcoordsVertexBuffer); |
---|
| 97 | |
---|
| 98 | // Prepare buffer for indices |
---|
| 99 | indexBuffer = |
---|
| 100 | HardwareBufferManager::getSingleton().createIndexBuffer( |
---|
| 101 | HardwareIndexBuffer::IT_16BIT, |
---|
| 102 | 3*numFaces, |
---|
| 103 | HardwareBuffer::HBU_STATIC, true); |
---|
| 104 | unsigned short *faceVertexIndices = (unsigned short*) |
---|
| 105 | indexBuffer->lock(0, numFaces*3*2, HardwareBuffer::HBL_DISCARD); |
---|
| 106 | for(y=0 ; y<complexity ; y++) { |
---|
| 107 | for(int x=0 ; x<complexity ; x++) { |
---|
| 108 | unsigned short *twoface = faceVertexIndices + (y*complexity+x)*2*3; |
---|
| 109 | int p0 = y*(complexity+1) + x ; |
---|
| 110 | int p1 = y*(complexity+1) + x + 1 ; |
---|
| 111 | int p2 = (y+1)*(complexity+1) + x ; |
---|
| 112 | int p3 = (y+1)*(complexity+1) + x + 1 ; |
---|
| 113 | twoface[0]=p2; //first tri |
---|
| 114 | twoface[1]=p1; |
---|
| 115 | twoface[2]=p0; |
---|
| 116 | twoface[3]=p2; //second tri |
---|
| 117 | twoface[4]=p3; |
---|
| 118 | twoface[5]=p1; |
---|
| 119 | } |
---|
| 120 | } |
---|
| 121 | indexBuffer->unlock(); |
---|
| 122 | // Set index buffer for this submesh |
---|
| 123 | subMesh->indexData->indexBuffer = indexBuffer; |
---|
| 124 | subMesh->indexData->indexStart = 0; |
---|
| 125 | subMesh->indexData->indexCount = 3*numFaces; |
---|
| 126 | |
---|
| 127 | /* prepare vertex positions |
---|
| 128 | * note - we use 3 vertex buffers, since algorighm uses two last phases |
---|
| 129 | * to calculate the next one |
---|
| 130 | */ |
---|
| 131 | for(b=0;b<3;b++) { |
---|
| 132 | vertexBuffers[b] = new float[numVertices * 3] ; |
---|
| 133 | for(y=0;y<=complexity;y++) { |
---|
| 134 | for(x=0;x<=complexity;x++) { |
---|
| 135 | int numPoint = y*(complexity+1) + x ; |
---|
| 136 | float* vertex = vertexBuffers[b] + 3*numPoint ; |
---|
| 137 | vertex[0]=(float)(x) / (float)(complexity) * (float) planeSize ; |
---|
| 138 | vertex[1]= 0 ; // rand() % 30 ; |
---|
| 139 | vertex[2]=(float)(y) / (float)(complexity) * (float) planeSize ; |
---|
| 140 | } |
---|
| 141 | } |
---|
| 142 | } |
---|
| 143 | |
---|
| 144 | AxisAlignedBox meshBounds(0,0,0, |
---|
| 145 | planeSize,0, planeSize); |
---|
| 146 | mesh->_setBounds(meshBounds); |
---|
| 147 | |
---|
| 148 | currentBuffNumber = 0 ; |
---|
| 149 | posVertexBuffer->writeData(0, |
---|
| 150 | posVertexBuffer->getSizeInBytes(), // size |
---|
| 151 | vertexBuffers[currentBuffNumber], // source |
---|
| 152 | true); // discard? |
---|
| 153 | |
---|
| 154 | mesh->load(); |
---|
| 155 | mesh->touch(); |
---|
| 156 | } |
---|
| 157 | /* ========================================================================= */ |
---|
| 158 | WaterMesh::~WaterMesh () |
---|
| 159 | { |
---|
| 160 | delete[] vertexBuffers[0]; |
---|
| 161 | delete[] vertexBuffers[1]; |
---|
| 162 | delete[] vertexBuffers[2]; |
---|
| 163 | |
---|
| 164 | delete[] vNormals; |
---|
| 165 | } |
---|
| 166 | /* ========================================================================= */ |
---|
| 167 | void WaterMesh::push(Real x, Real y, Real depth, bool absolute) |
---|
| 168 | { |
---|
| 169 | float *buf = vertexBuffers[currentBuffNumber]+1 ; |
---|
| 170 | // scale pressure according to time passed |
---|
| 171 | depth = depth * lastFrameTime * ANIMATIONS_PER_SECOND ; |
---|
| 172 | #define _PREP(addx,addy) { \ |
---|
| 173 | float *vertex=buf+3*((int)(y+addy)*(complexity+1)+(int)(x+addx)) ; \ |
---|
| 174 | float diffy = y - floor(y+addy); \ |
---|
| 175 | float diffx = x - floor(x+addx); \ |
---|
| 176 | float dist=sqrt(diffy*diffy + diffx*diffx) ; \ |
---|
| 177 | float power = 1 - dist ; \ |
---|
| 178 | if (power<0) \ |
---|
| 179 | power = 0; \ |
---|
| 180 | if (absolute) \ |
---|
| 181 | *vertex = depth*power ; \ |
---|
| 182 | else \ |
---|
| 183 | *vertex += depth*power ; \ |
---|
| 184 | } /* #define */ |
---|
| 185 | _PREP(0,0); |
---|
| 186 | _PREP(0,1); |
---|
| 187 | _PREP(1,0); |
---|
| 188 | _PREP(1,1); |
---|
| 189 | #undef _PREP |
---|
| 190 | } |
---|
| 191 | /* ========================================================================= */ |
---|
| 192 | Real WaterMesh::getHeight(Real x, Real y) |
---|
| 193 | { |
---|
| 194 | #define hat(_x,_y) buf[3*((int)_y*(complexity+1)+(int)(_x))] |
---|
| 195 | float *buf = vertexBuffers[currentBuffNumber] ; |
---|
| 196 | Real xa = floor(x); |
---|
| 197 | Real xb = xa + 1 ; |
---|
| 198 | Real ya = floor(y); |
---|
| 199 | Real yb = ya + 1 ; |
---|
| 200 | Real yaxavg = hat(xa,ya) * (1.0f-fabs(xa-x)) + hat(xb,ya) * (1.0f-fabs(xb-x)); |
---|
| 201 | Real ybxavg = hat(xa,yb) * (1.0f-fabs(xa-x)) + hat(xb,yb) * (1.0f-fabs(xb-x)); |
---|
| 202 | Real yavg = yaxavg * (1.0f-fabs(ya-y)) + ybxavg * (1.0f-fabs(yb-y)) ; |
---|
| 203 | return yavg ; |
---|
| 204 | } |
---|
| 205 | /* ========================================================================= */ |
---|
| 206 | void WaterMesh::calculateFakeNormals() |
---|
| 207 | { |
---|
| 208 | int x,y; |
---|
| 209 | float *buf = vertexBuffers[currentBuffNumber] + 1; |
---|
| 210 | float *pNormals = (float*) normVertexBuffer->lock( |
---|
| 211 | 0,normVertexBuffer->getSizeInBytes(), HardwareBuffer::HBL_DISCARD); |
---|
| 212 | for(y=1;y<complexity;y++) { |
---|
| 213 | float *nrow = pNormals + 3*y*(complexity+1); |
---|
| 214 | float *row = buf + 3*y*(complexity+1) ; |
---|
| 215 | float *rowup = buf + 3*(y-1)*(complexity+1) ; |
---|
| 216 | float *rowdown = buf + 3*(y+1)*(complexity+1) ; |
---|
| 217 | for(x=1;x<complexity;x++) { |
---|
| 218 | Real xdiff = row[3*x+3] - row[3*x-3] ; |
---|
| 219 | Real ydiff = rowup[3*x] - rowdown[3*x-3] ; |
---|
| 220 | Vector3 norm(xdiff,30,ydiff); |
---|
| 221 | norm.normalise(); |
---|
| 222 | nrow[3*x+0] = norm.x; |
---|
| 223 | nrow[3*x+1] = norm.y; |
---|
| 224 | nrow[3*x+2] = norm.z; |
---|
| 225 | } |
---|
| 226 | } |
---|
| 227 | normVertexBuffer->unlock(); |
---|
| 228 | } |
---|
| 229 | /* ========================================================================= */ |
---|
| 230 | void WaterMesh::calculateNormals() |
---|
| 231 | { |
---|
| 232 | int i,x,y; |
---|
| 233 | float *buf = vertexBuffers[currentBuffNumber] + 1; |
---|
| 234 | // zero normals |
---|
| 235 | for(i=0;i<numVertices;i++) { |
---|
| 236 | vNormals[i] = Vector3::ZERO; |
---|
| 237 | } |
---|
| 238 | // first, calculate normals for faces, add them to proper vertices |
---|
| 239 | buf = vertexBuffers[currentBuffNumber] ; |
---|
| 240 | unsigned short* vinds = (unsigned short*) indexBuffer->lock( |
---|
| 241 | 0, indexBuffer->getSizeInBytes(), HardwareBuffer::HBL_READ_ONLY); |
---|
| 242 | float *pNormals = (float*) normVertexBuffer->lock( |
---|
| 243 | 0, normVertexBuffer->getSizeInBytes(), HardwareBuffer::HBL_DISCARD); |
---|
| 244 | for(i=0;i<numFaces;i++) { |
---|
| 245 | int p0 = vinds[3*i] ; |
---|
| 246 | int p1 = vinds[3*i+1] ; |
---|
| 247 | int p2 = vinds[3*i+2] ; |
---|
| 248 | Vector3 v0(buf[3*p0], buf[3*p0+1], buf[3*p0+2]); |
---|
| 249 | Vector3 v1(buf[3*p1], buf[3*p1+1], buf[3*p1+2]); |
---|
| 250 | Vector3 v2(buf[3*p2], buf[3*p2+1], buf[3*p2+2]); |
---|
| 251 | Vector3 diff1 = v2 - v1 ; |
---|
| 252 | Vector3 diff2 = v0 - v1 ; |
---|
| 253 | Vector3 fn = diff1.crossProduct(diff2); |
---|
| 254 | vNormals[p0] += fn ; |
---|
| 255 | vNormals[p1] += fn ; |
---|
| 256 | vNormals[p2] += fn ; |
---|
| 257 | } |
---|
| 258 | // now normalize vertex normals |
---|
| 259 | for(y=0;y<=complexity;y++) { |
---|
| 260 | for(x=0;x<=complexity;x++) { |
---|
| 261 | int numPoint = y*(complexity+1) + x ; |
---|
| 262 | Vector3 n = vNormals[numPoint] ; |
---|
| 263 | n.normalise() ; |
---|
| 264 | float* normal = pNormals + 3*numPoint ; |
---|
| 265 | normal[0]=n.x; |
---|
| 266 | normal[1]=n.y; |
---|
| 267 | normal[2]=n.z; |
---|
| 268 | } |
---|
| 269 | } |
---|
| 270 | indexBuffer->unlock(); |
---|
| 271 | normVertexBuffer->unlock(); |
---|
| 272 | } |
---|
| 273 | /* ========================================================================= */ |
---|
| 274 | void WaterMesh::updateMesh(Real timeSinceLastFrame) |
---|
| 275 | { |
---|
| 276 | int x, y ; |
---|
| 277 | |
---|
| 278 | lastFrameTime = timeSinceLastFrame ; |
---|
| 279 | lastTimeStamp += timeSinceLastFrame ; |
---|
| 280 | |
---|
| 281 | // do rendering to get ANIMATIONS_PER_SECOND |
---|
| 282 | while(lastAnimationTimeStamp <= lastTimeStamp) { |
---|
| 283 | |
---|
| 284 | // switch buffer numbers |
---|
| 285 | currentBuffNumber = (currentBuffNumber + 1) % 3 ; |
---|
| 286 | float *buf = vertexBuffers[currentBuffNumber] + 1 ; // +1 for Y coordinate |
---|
| 287 | float *buf1 = vertexBuffers[(currentBuffNumber+2)%3] + 1 ; |
---|
| 288 | float *buf2 = vertexBuffers[(currentBuffNumber+1)%3] + 1; |
---|
| 289 | |
---|
| 290 | /* we use an algorithm from |
---|
| 291 | * http://collective.valve-erc.com/index.php?go=water_simulation |
---|
| 292 | * The params could be dynamically changed every frame ofcourse |
---|
| 293 | */ |
---|
| 294 | double C = PARAM_C; // ripple speed |
---|
| 295 | double D = PARAM_D; // distance |
---|
| 296 | double U = PARAM_U; // viscosity |
---|
| 297 | double T = PARAM_T; // time |
---|
| 298 | Real TERM1 = ( 4.0f - 8.0f*C*C*T*T/(D*D) ) / (U*T+2) ; |
---|
| 299 | Real TERM2 = ( U*T-2.0f ) / (U*T+2.0f) ; |
---|
| 300 | Real TERM3 = ( 2.0f * C*C*T*T/(D*D) ) / (U*T+2) ; |
---|
| 301 | for(y=1;y<complexity;y++) { // don't do anything with border values |
---|
| 302 | float *row = buf + 3*y*(complexity+1) ; |
---|
| 303 | float *row1 = buf1 + 3*y*(complexity+1) ; |
---|
| 304 | float *row1up = buf1 + 3*(y-1)*(complexity+1) ; |
---|
| 305 | float *row1down = buf1 + 3*(y+1)*(complexity+1) ; |
---|
| 306 | float *row2 = buf2 + 3*y*(complexity+1) ; |
---|
| 307 | for(x=1;x<complexity;x++) { |
---|
| 308 | row[3*x] = TERM1 * row1[3*x] |
---|
| 309 | + TERM2 * row2[3*x] |
---|
| 310 | + TERM3 * ( row1[3*x-3] + row1[3*x+3] + row1up[3*x]+row1down[3*x] ) ; |
---|
| 311 | } |
---|
| 312 | } |
---|
| 313 | |
---|
| 314 | lastAnimationTimeStamp += (1.0f / ANIMATIONS_PER_SECOND); |
---|
| 315 | } |
---|
| 316 | |
---|
| 317 | if (useFakeNormals) { |
---|
| 318 | calculateFakeNormals(); |
---|
| 319 | } else { |
---|
| 320 | calculateNormals(); |
---|
| 321 | } |
---|
| 322 | |
---|
| 323 | // set vertex buffer |
---|
| 324 | posVertexBuffer->writeData(0, |
---|
| 325 | posVertexBuffer->getSizeInBytes(), // size |
---|
| 326 | vertexBuffers[currentBuffNumber], // source |
---|
| 327 | true); // discard? |
---|
| 328 | } |
---|