]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/Model_liquid.cpp
hello world
[icculus/iodoom3.git] / neo / renderer / Model_liquid.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "tr_local.h"
33 #include "Model_local.h"
34
35 #define LIQUID_MAX_SKIP_FRAMES  5
36 #define LIQUID_MAX_TYPES                3
37
38 /*
39 ====================
40 idRenderModelLiquid::idRenderModelLiquid
41 ====================
42 */
43 idRenderModelLiquid::idRenderModelLiquid() {
44         verts_x         = 32;
45         verts_y         = 32;
46         scale_x         = 256.0f;
47         scale_y         = 256.0f;
48         liquid_type = 0;
49         density         = 0.97f;
50         drop_height = 4;
51         drop_radius = 4;
52         drop_delay      = 1000;
53     shader              = declManager->FindMaterial( NULL );
54         update_tics     = 33;  // ~30 hz
55         time            = 0;
56         seed            = 0;
57
58         random.SetSeed( 0 );
59 }
60
61 /*
62 ====================
63 idRenderModelLiquid::GenerateSurface
64 ====================
65 */
66 modelSurface_t idRenderModelLiquid::GenerateSurface( float lerp ) {
67         srfTriangles_t  *tri;
68         int                             i, base;
69         idDrawVert              *vert;
70         modelSurface_t  surf;
71         float                   inv_lerp;
72
73         inv_lerp = 1.0f - lerp;
74         vert = verts.Ptr();
75         for( i = 0; i < verts.Num(); i++, vert++ ) {
76                 vert->xyz.z = page1[ i ] * lerp + page2[ i ] * inv_lerp;
77         }
78
79         tr.pc.c_deformedSurfaces++;
80         tr.pc.c_deformedVerts += deformInfo->numOutputVerts;
81         tr.pc.c_deformedIndexes += deformInfo->numIndexes;
82
83         tri = R_AllocStaticTriSurf();
84
85         // note that some of the data is references, and should not be freed
86         tri->deformedSurface = true;
87
88         tri->numIndexes = deformInfo->numIndexes;
89         tri->indexes = deformInfo->indexes;
90         tri->silIndexes = deformInfo->silIndexes;
91         tri->numMirroredVerts = deformInfo->numMirroredVerts;
92         tri->mirroredVerts = deformInfo->mirroredVerts;
93         tri->numDupVerts = deformInfo->numDupVerts;
94         tri->dupVerts = deformInfo->dupVerts;
95         tri->numSilEdges = deformInfo->numSilEdges;
96         tri->silEdges = deformInfo->silEdges;
97         tri->dominantTris = deformInfo->dominantTris;
98
99         tri->numVerts = deformInfo->numOutputVerts;
100         R_AllocStaticTriSurfVerts( tri, tri->numVerts );
101         SIMDProcessor->Memcpy( tri->verts, verts.Ptr(), deformInfo->numSourceVerts * sizeof(tri->verts[0]) );
102
103         // replicate the mirror seam vertexes
104         base = deformInfo->numOutputVerts - deformInfo->numMirroredVerts;
105         for ( i = 0 ; i < deformInfo->numMirroredVerts ; i++ ) {
106                 tri->verts[base + i] = tri->verts[deformInfo->mirroredVerts[i]];
107         }
108
109         R_BoundTriSurf( tri );
110
111         // If a surface is going to be have a lighting interaction generated, it will also have to call
112         // R_DeriveTangents() to get normals, tangents, and face planes.  If it only
113         // needs shadows generated, it will only have to generate face planes.  If it only
114         // has ambient drawing, or is culled, no additional work will be necessary
115         if ( !r_useDeferredTangents.GetBool() ) {
116                 // set face planes, vertex normals, tangents
117                 R_DeriveTangents( tri );
118         }
119
120         surf.geometry   = tri;
121         surf.shader             = shader;
122
123         return surf;
124 }
125
126 /*
127 ====================
128 idRenderModelLiquid::WaterDrop
129 ====================
130 */
131 void idRenderModelLiquid::WaterDrop( int x, int y, float *page ) {
132         int             cx, cy;
133         int             left,top,right,bottom;
134         int             square;
135         int             radsquare = drop_radius * drop_radius;
136         float   invlength = 1.0f / ( float )radsquare;
137         float   dist;
138
139         if ( x < 0 ) {
140                 x = 1 + drop_radius + random.RandomInt( verts_x - 2 * drop_radius - 1 );
141         }
142         if ( y < 0 ) {
143                 y = 1 + drop_radius + random.RandomInt( verts_y - 2 * drop_radius - 1 );
144         }
145
146         left=-drop_radius; right = drop_radius;
147         top=-drop_radius; bottom = drop_radius;
148
149         // Perform edge clipping...
150         if ( x - drop_radius < 1 ) {
151                 left -= (x-drop_radius-1);
152         }
153         if ( y - drop_radius < 1 ) {
154                 top -= (y-drop_radius-1);
155         }
156         if ( x + drop_radius > verts_x - 1 ) {
157                 right -= (x+drop_radius-verts_x+1);
158         }
159         if ( y + drop_radius > verts_y - 1 ) {
160                 bottom-= (y+drop_radius-verts_y+1);
161         }
162
163         for ( cy = top; cy < bottom; cy++ ) {
164                 for ( cx = left; cx < right; cx++ ) {
165                         square = cy*cy + cx*cx;
166                         if ( square < radsquare ) {
167                                 dist = idMath::Sqrt( (float)square * invlength );
168                                 page[verts_x*(cy+y) + cx+x] += idMath::Cos16( dist * idMath::PI * 0.5f ) * drop_height;
169                         }
170                 }
171         }
172 }
173
174 /*
175 ====================
176 idRenderModelLiquid::IntersectBounds
177 ====================
178 */
179 void idRenderModelLiquid::IntersectBounds( const idBounds &bounds, float displacement ) {
180         int             cx, cy;
181         int             left,top,right,bottom;
182         float   up, down;
183         float   *pos;
184
185         left    = ( int )( bounds[ 0 ].x / scale_x );
186         right   = ( int )( bounds[ 1 ].x / scale_x );
187         top             = ( int )( bounds[ 0 ].y / scale_y );
188         bottom  = ( int )( bounds[ 1 ].y / scale_y );
189         down    = bounds[ 0 ].z;
190         up              = bounds[ 1 ].z;
191
192         if ( ( right < 1 ) || ( left >= verts_x ) || ( bottom < 1 ) || ( top >= verts_x ) ) {
193                 return;
194         }
195
196         // Perform edge clipping...
197         if ( left < 1 ) {
198                 left = 1;
199         }
200         if ( right >= verts_x ) {
201                 right = verts_x - 1;
202         }
203         if ( top < 1 ) {
204                 top = 1;
205         }
206         if ( bottom >= verts_y ) {
207                 bottom = verts_y - 1;
208         }
209
210         for ( cy = top; cy < bottom; cy++ ) {
211                 for ( cx = left; cx < right; cx++ ) {
212                         pos = &page1[ verts_x * cy + cx ];
213                         if ( *pos > down ) {//&& ( *pos < up ) ) {
214                                 *pos = down;
215                         }
216                 }
217         }
218 }
219
220 /*
221 ====================
222 idRenderModelLiquid::Update
223 ====================
224 */
225 void idRenderModelLiquid::Update( void ) {
226         int             x, y;
227         float   *p2;
228         float   *p1;
229         float   value;
230
231         time += update_tics;
232
233         idSwap( page1, page2 );
234
235         if ( time > nextDropTime ) {
236                 WaterDrop( -1, -1, page2 );
237                 nextDropTime = time + drop_delay;
238         } else if ( time < nextDropTime - drop_delay ) {
239                 nextDropTime = time + drop_delay;
240         }
241
242         p1 = page1;
243         p2 = page2;
244
245         switch( liquid_type ) {
246         case 0 :
247                 for ( y = 1; y < verts_y - 1; y++ ) {
248                         p2 += verts_x;
249                         p1 += verts_x;
250                         for ( x = 1; x < verts_x - 1; x++ ) {
251                                 value =
252                                         ( p2[ x + verts_x ] +
253                                         p2[ x - verts_x ] +
254                                         p2[ x + 1 ] +
255                                         p2[ x - 1 ] +
256                                         p2[ x - verts_x - 1 ] +
257                                         p2[ x - verts_x + 1 ] +
258                                         p2[ x + verts_x - 1 ] +
259                                         p2[ x + verts_x + 1 ] +
260                                         p2[ x ] ) * ( 2.0f / 9.0f ) -
261                                         p1[ x ];
262
263                                 p1[ x ] = value * density;
264                         }
265                 }
266                 break;
267
268         case 1 :
269                 for ( y = 1; y < verts_y - 1; y++ ) {
270                         p2 += verts_x;
271                         p1 += verts_x;
272                         for ( x = 1; x < verts_x - 1; x++ ) {
273                                 value =
274                                         ( p2[ x + verts_x ] +
275                                         p2[ x - verts_x ] +
276                                         p2[ x + 1 ] +
277                                         p2[ x - 1 ] +
278                                         p2[ x - verts_x - 1 ] +
279                                         p2[ x - verts_x + 1 ] +
280                                         p2[ x + verts_x - 1 ] +
281                                         p2[ x + verts_x + 1 ] ) * 0.25f -
282                                         p1[ x ];
283
284                                 p1[ x ] = value * density;
285                         }
286                 }
287                 break;
288
289         case 2 :
290                 for ( y = 1; y < verts_y - 1; y++ ) {
291                         p2 += verts_x;
292                         p1 += verts_x;
293                         for ( x = 1; x < verts_x - 1; x++ ) {
294                                 value =
295                                         ( p2[ x + verts_x ] +
296                                         p2[ x - verts_x ] +
297                                         p2[ x + 1 ] +
298                                         p2[ x - 1 ] +
299                                         p2[ x - verts_x - 1 ] +
300                                         p2[ x - verts_x + 1 ] +
301                                         p2[ x + verts_x - 1 ] +
302                                         p2[ x + verts_x + 1 ] + 
303                                         p2[ x ] ) * ( 1.0f / 9.0f );
304
305                                 p1[ x ] = value * density;
306                         }
307                 }
308                 break;
309         }
310 }
311
312 /*
313 ====================
314 idRenderModelLiquid::Reset
315 ====================
316 */
317 void idRenderModelLiquid::Reset() {
318         int     i, x, y;
319
320         if ( pages.Num() < 2 * verts_x * verts_y ) {
321                 return;
322         }
323
324         nextDropTime = 0;
325         time = 0;
326         random.SetSeed( seed );
327
328         page1 = pages.Ptr();
329         page2 = page1 + verts_x * verts_y;
330
331         for ( i = 0, y = 0; y < verts_y; y++ ) {
332                 for ( x = 0; x < verts_x; x++, i++ ) {
333                         page1[ i ] = 0.0f;
334                         page2[ i ] = 0.0f;
335                         verts[ i ].xyz.z = 0.0f;
336                 }
337         }
338 }
339
340 /*
341 ====================
342 idRenderModelLiquid::InitFromFile
343 ====================
344 */
345 void idRenderModelLiquid::InitFromFile( const char *fileName ) {
346         int                             i, x, y;
347         idToken                 token;
348         idParser                parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS );
349         idList<int>             tris;
350         float                   size_x, size_y;
351         float                   rate;
352
353         name = fileName;
354
355         if ( !parser.LoadFile( fileName ) ) {
356                 MakeDefaultModel();
357                 return;
358         }
359
360         size_x = scale_x * verts_x;
361         size_y = scale_y * verts_y;
362
363         while( parser.ReadToken( &token ) ) {
364                 if ( !token.Icmp( "seed" ) ) {
365                         seed = parser.ParseInt();
366                 } else if ( !token.Icmp( "size_x" ) ) {
367                         size_x = parser.ParseFloat();
368                 } else if ( !token.Icmp( "size_y" ) ) {
369                         size_y = parser.ParseFloat();
370                 } else if ( !token.Icmp( "verts_x" ) ) {
371                         verts_x = parser.ParseFloat();
372                         if ( verts_x < 2 ) {
373                                 parser.Warning( "Invalid # of verts.  Using default model." );
374                                 MakeDefaultModel();
375                                 return;
376                         }
377                 } else if ( !token.Icmp( "verts_y" ) ) {
378                         verts_y = parser.ParseFloat();
379                         if ( verts_y < 2 ) {
380                                 parser.Warning( "Invalid # of verts.  Using default model." );
381                                 MakeDefaultModel();
382                                 return;
383                         }
384                 } else if ( !token.Icmp( "liquid_type" ) ) {
385                         liquid_type = parser.ParseInt() - 1;
386                         if ( ( liquid_type < 0 ) || ( liquid_type >= LIQUID_MAX_TYPES ) ) {
387                                 parser.Warning( "Invalid liquid_type.  Using default model." );
388                                 MakeDefaultModel();
389                                 return;
390                         }
391                 } else if ( !token.Icmp( "density" ) ) {
392                         density = parser.ParseFloat();
393                 } else if ( !token.Icmp( "drop_height" ) ) {
394                         drop_height = parser.ParseFloat();
395                 } else if ( !token.Icmp( "drop_radius" ) ) {
396                         drop_radius = parser.ParseInt();
397                 } else if ( !token.Icmp( "drop_delay" ) ) {
398                         drop_delay = SEC2MS( parser.ParseFloat() );
399                 } else if ( !token.Icmp( "shader" ) ) {
400                         parser.ReadToken( &token );
401                         shader = declManager->FindMaterial( token );
402                 } else if ( !token.Icmp( "seed" ) ) {
403                         seed = parser.ParseInt();
404                 } else if ( !token.Icmp( "update_rate" ) ) {
405                         rate = parser.ParseFloat();
406                         if ( ( rate <= 0.0f ) || ( rate > 60.0f ) ) {
407                                 parser.Warning( "Invalid update_rate.  Must be between 0 and 60.  Using default model." );
408                                 MakeDefaultModel();
409                                 return;
410                         }
411                         update_tics = 1000 / rate;
412                 } else {
413                         parser.Warning( "Unknown parameter '%s'.  Using default model.", token.c_str() );
414                         MakeDefaultModel();
415                         return;
416                 }
417         }
418
419         scale_x = size_x / ( verts_x - 1 );
420         scale_y = size_y / ( verts_y - 1 );
421
422         pages.SetNum( 2 * verts_x * verts_y );
423         page1 = pages.Ptr();
424         page2 = page1 + verts_x * verts_y;
425
426         verts.SetNum( verts_x * verts_y );
427         for ( i = 0, y = 0; y < verts_y; y++ ) {
428                 for ( x = 0; x < verts_x; x++, i++ ) {
429                         page1[ i ] = 0.0f;
430                         page2[ i ] = 0.0f;
431                         verts[ i ].Clear();
432                         verts[ i ].xyz.Set( x * scale_x, y * scale_y, 0.0f );
433                         verts[ i ].st.Set( (float) x / (float)( verts_x - 1 ), (float) -y / (float)( verts_y - 1 ) );
434                 }
435         }
436
437         tris.SetNum( ( verts_x - 1 ) * ( verts_y - 1 ) * 6 );
438         for( i = 0, y = 0; y < verts_y - 1; y++ ) {
439                 for( x = 1; x < verts_x; x++, i += 6 ) {
440                         tris[ i + 0 ] = y * verts_x + x;
441                         tris[ i + 1 ] = y * verts_x + x - 1;
442                         tris[ i + 2 ] = ( y + 1 ) * verts_x + x - 1;
443
444                         tris[ i + 3 ] = ( y + 1 ) * verts_x + x - 1;
445                         tris[ i + 4 ] = ( y + 1 ) * verts_x + x;
446                         tris[ i + 5 ] = y * verts_x + x;
447                 }
448         }
449
450         // build the information that will be common to all animations of this mesh:
451         // sil edge connectivity and normal / tangent generation information
452         deformInfo = R_BuildDeformInfo( verts.Num(), verts.Ptr(), tris.Num(), tris.Ptr(), true );
453
454         bounds.Clear();
455         bounds.AddPoint( idVec3( 0.0f, 0.0f, drop_height * -10.0f ) );
456         bounds.AddPoint( idVec3( ( verts_x - 1 ) * scale_x, ( verts_y - 1 ) * scale_y, drop_height * 10.0f ) );
457
458         // set the timestamp for reloadmodels
459         fileSystem->ReadFile( name, NULL, &timeStamp );
460
461         Reset();
462 }
463
464 /*
465 ====================
466 idRenderModelLiquid::InstantiateDynamicModel
467 ====================
468 */
469 idRenderModel *idRenderModelLiquid::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
470         idRenderModelStatic     *staticModel;
471         int             frames;
472         int             t;
473         float   lerp;
474
475         if ( cachedModel ) {
476                 delete cachedModel;
477                 cachedModel = NULL;
478         }
479
480         if ( !deformInfo ) {
481                 return NULL;
482         }
483
484         if ( !view ) {
485                 t = 0;
486         } else {
487                 t = view->renderView.time;
488         }
489
490         // update the liquid model
491         frames = ( t - time ) / update_tics;
492         if ( frames > LIQUID_MAX_SKIP_FRAMES ) {
493                 // don't let time accumalate when skipping frames
494                 time += update_tics * ( frames - LIQUID_MAX_SKIP_FRAMES );
495
496                 frames = LIQUID_MAX_SKIP_FRAMES;
497         }
498         
499         while( frames > 0 ) {
500                 Update();
501                 frames--;
502         }
503
504         // create the surface
505         lerp = ( float )( t - time ) / ( float )update_tics;
506         modelSurface_t surf = GenerateSurface( lerp );
507
508         staticModel = new idRenderModelStatic;
509         staticModel->AddSurface( surf );
510         staticModel->bounds = surf.geometry->bounds;
511
512         return staticModel;
513 }
514
515 /*
516 ====================
517 idRenderModelLiquid::IsDynamicModel
518 ====================
519 */
520 dynamicModel_t idRenderModelLiquid::IsDynamicModel() const {
521         return DM_CONTINUOUS;
522 }
523
524 /*
525 ====================
526 idRenderModelLiquid::Bounds
527 ====================
528 */
529 idBounds idRenderModelLiquid::Bounds(const struct renderEntity_s *ent) const {
530         // FIXME: need to do this better
531         return bounds;
532 }