DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Model_liquid.cpp (13391B)


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