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 | } |
---|