2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
33 #include "Model_local.h"
35 #define LIQUID_MAX_SKIP_FRAMES 5
36 #define LIQUID_MAX_TYPES 3
40 idRenderModelLiquid::idRenderModelLiquid
43 idRenderModelLiquid::idRenderModelLiquid() {
53 shader = declManager->FindMaterial( NULL );
54 update_tics = 33; // ~30 hz
63 idRenderModelLiquid::GenerateSurface
66 modelSurface_t idRenderModelLiquid::GenerateSurface( float lerp ) {
73 inv_lerp = 1.0f - lerp;
75 for( i = 0; i < verts.Num(); i++, vert++ ) {
76 vert->xyz.z = page1[ i ] * lerp + page2[ i ] * inv_lerp;
79 tr.pc.c_deformedSurfaces++;
80 tr.pc.c_deformedVerts += deformInfo->numOutputVerts;
81 tr.pc.c_deformedIndexes += deformInfo->numIndexes;
83 tri = R_AllocStaticTriSurf();
85 // note that some of the data is references, and should not be freed
86 tri->deformedSurface = true;
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;
99 tri->numVerts = deformInfo->numOutputVerts;
100 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
101 SIMDProcessor->Memcpy( tri->verts, verts.Ptr(), deformInfo->numSourceVerts * sizeof(tri->verts[0]) );
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]];
109 R_BoundTriSurf( tri );
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 );
121 surf.shader = shader;
128 idRenderModelLiquid::WaterDrop
131 void idRenderModelLiquid::WaterDrop( int x, int y, float *page ) {
133 int left,top,right,bottom;
135 int radsquare = drop_radius * drop_radius;
136 float invlength = 1.0f / ( float )radsquare;
140 x = 1 + drop_radius + random.RandomInt( verts_x - 2 * drop_radius - 1 );
143 y = 1 + drop_radius + random.RandomInt( verts_y - 2 * drop_radius - 1 );
146 left=-drop_radius; right = drop_radius;
147 top=-drop_radius; bottom = drop_radius;
149 // Perform edge clipping...
150 if ( x - drop_radius < 1 ) {
151 left -= (x-drop_radius-1);
153 if ( y - drop_radius < 1 ) {
154 top -= (y-drop_radius-1);
156 if ( x + drop_radius > verts_x - 1 ) {
157 right -= (x+drop_radius-verts_x+1);
159 if ( y + drop_radius > verts_y - 1 ) {
160 bottom-= (y+drop_radius-verts_y+1);
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;
176 idRenderModelLiquid::IntersectBounds
179 void idRenderModelLiquid::IntersectBounds( const idBounds &bounds, float displacement ) {
181 int left,top,right,bottom;
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;
192 if ( ( right < 1 ) || ( left >= verts_x ) || ( bottom < 1 ) || ( top >= verts_x ) ) {
196 // Perform edge clipping...
200 if ( right >= verts_x ) {
206 if ( bottom >= verts_y ) {
207 bottom = verts_y - 1;
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 ) ) {
222 idRenderModelLiquid::Update
225 void idRenderModelLiquid::Update( void ) {
233 idSwap( page1, page2 );
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;
245 switch( liquid_type ) {
247 for ( y = 1; y < verts_y - 1; y++ ) {
250 for ( x = 1; x < verts_x - 1; x++ ) {
252 ( p2[ x + verts_x ] +
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 ) -
263 p1[ x ] = value * density;
269 for ( y = 1; y < verts_y - 1; y++ ) {
272 for ( x = 1; x < verts_x - 1; x++ ) {
274 ( p2[ x + verts_x ] +
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 -
284 p1[ x ] = value * density;
290 for ( y = 1; y < verts_y - 1; y++ ) {
293 for ( x = 1; x < verts_x - 1; x++ ) {
295 ( p2[ x + verts_x ] +
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 );
305 p1[ x ] = value * density;
314 idRenderModelLiquid::Reset
317 void idRenderModelLiquid::Reset() {
320 if ( pages.Num() < 2 * verts_x * verts_y ) {
326 random.SetSeed( seed );
329 page2 = page1 + verts_x * verts_y;
331 for ( i = 0, y = 0; y < verts_y; y++ ) {
332 for ( x = 0; x < verts_x; x++, i++ ) {
335 verts[ i ].xyz.z = 0.0f;
342 idRenderModelLiquid::InitFromFile
345 void idRenderModelLiquid::InitFromFile( const char *fileName ) {
348 idParser parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS );
350 float size_x, size_y;
355 if ( !parser.LoadFile( fileName ) ) {
360 size_x = scale_x * verts_x;
361 size_y = scale_y * verts_y;
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();
373 parser.Warning( "Invalid # of verts. Using default model." );
377 } else if ( !token.Icmp( "verts_y" ) ) {
378 verts_y = parser.ParseFloat();
380 parser.Warning( "Invalid # of verts. Using default model." );
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." );
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." );
411 update_tics = 1000 / rate;
413 parser.Warning( "Unknown parameter '%s'. Using default model.", token.c_str() );
419 scale_x = size_x / ( verts_x - 1 );
420 scale_y = size_y / ( verts_y - 1 );
422 pages.SetNum( 2 * verts_x * verts_y );
424 page2 = page1 + verts_x * verts_y;
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++ ) {
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 ) );
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;
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;
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 );
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 ) );
458 // set the timestamp for reloadmodels
459 fileSystem->ReadFile( name, NULL, &timeStamp );
466 idRenderModelLiquid::InstantiateDynamicModel
469 idRenderModel *idRenderModelLiquid::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
470 idRenderModelStatic *staticModel;
487 t = view->renderView.time;
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 );
496 frames = LIQUID_MAX_SKIP_FRAMES;
499 while( frames > 0 ) {
504 // create the surface
505 lerp = ( float )( t - time ) / ( float )update_tics;
506 modelSurface_t surf = GenerateSurface( lerp );
508 staticModel = new idRenderModelStatic;
509 staticModel->AddSurface( surf );
510 staticModel->bounds = surf.geometry->bounds;
517 idRenderModelLiquid::IsDynamicModel
520 dynamicModel_t idRenderModelLiquid::IsDynamicModel() const {
521 return DM_CONTINUOUS;
526 idRenderModelLiquid::Bounds
529 idBounds idRenderModelLiquid::Bounds(const struct renderEntity_s *ent) const {
530 // FIXME: need to do this better