//----------------------------------------------------------------------------- // Purpose: // Input : &data - //----------------------------------------------------------------------------- void RippleCallback( const CEffectData &data ) { float flScale = data.m_flScale / 8.0f; Vector color; float luminosity; // Get our lighting information FX_GetSplashLighting( data.m_vOrigin + ( Vector(0,0,1) * 4.0f ), &color, &luminosity ); FX_WaterRipple( data.m_vOrigin, flScale, &color, 1.5f, luminosity ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_WaterExplosionEffect::Create( const Vector &position, float force, float scale, int flags ) { m_vecOrigin = position; // Find our water surface by tracing up till we're out of the water trace_t tr; Vector vecTrace( 0, 0, MAX_WATER_SURFACE_DISTANCE ); UTIL_TraceLine( m_vecOrigin, m_vecOrigin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr ); // If we didn't start in water, we're above it if ( tr.startsolid == false ) { // Look downward to find the surface vecTrace.Init( 0, 0, -MAX_WATER_SURFACE_DISTANCE ); UTIL_TraceLine( m_vecOrigin, m_vecOrigin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr ); // If we hit it, setup the explosion if ( tr.fraction < 1.0f ) { m_vecWaterSurface = tr.endpos; m_flDepth = 0.0f; } else { //NOTENOTE: We somehow got into a water explosion without being near water? Assert( 0 ); m_vecWaterSurface = m_vecOrigin; m_flDepth = 0.0f; } } else if ( tr.fractionleftsolid ) { // Otherwise we came out of the water at this point m_vecWaterSurface = m_vecOrigin + (vecTrace * tr.fractionleftsolid); m_flDepth = MAX_WATER_SURFACE_DISTANCE * tr.fractionleftsolid; } else { // Use default values, we're really deep m_vecWaterSurface = m_vecOrigin; m_flDepth = MAX_WATER_SURFACE_DISTANCE; } // Get our lighting information FX_GetSplashLighting( m_vecOrigin + Vector( 0, 0, 32 ), &m_vecColor, &m_flLuminosity ); BaseClass::Create( position, force, scale, flags ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_WaterExplosionEffect::CreateCore( void ) { if ( m_fFlags & TE_EXPLFLAG_NOFIREBALL ) return; // Get our lighting information for the water surface Vector color; float luminosity; FX_GetSplashLighting( m_vecWaterSurface + Vector( 0, 0, 8 ), &color, &luminosity ); float lifetime = random->RandomFloat( 0.8f, 1.0f ); // Ground splash FX_AddQuad( m_vecWaterSurface + Vector(0,0,2), Vector(0,0,1), 64, 64 * 4.0f, 0.85f, luminosity, 0.0f, 0.25f, random->RandomInt( 0, 360 ), random->RandomFloat( -4, 4 ), color, 2.0f, "effects/splashwake1", (FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA) ); Vector vRight, vUp; VectorVectors( Vector(0,0,1) , vRight, vUp ); Vector start, end; float radius = 50.0f; unsigned int flags; // Base vertical shaft FXLineData_t lineData; start = m_vecWaterSurface; end = start + ( Vector( 0, 0, 1 ) * random->RandomFloat( radius, radius*1.5f ) ); if ( random->RandomInt( 0, 1 ) ) { flags |= FXSTATICLINE_FLIP_HORIZONTAL; } else { flags = 0; } lineData.m_flDieTime = lifetime * 0.5f; lineData.m_flStartAlpha= luminosity; lineData.m_flEndAlpha = 0.0f; lineData.m_flStartScale = radius*0.5f; lineData.m_flEndScale = radius*2; lineData.m_pMaterial = materials->FindMaterial( "effects/splash3", 0, 0 ); lineData.m_vecStart = start; lineData.m_vecStartVelocity = vec3_origin; lineData.m_vecEnd = end; lineData.m_vecEndVelocity = Vector(0,0,random->RandomFloat( 650, 750 )); FX_AddLine( lineData ); // Inner filler shaft start = m_vecWaterSurface; end = start + ( Vector(0,0,1) * random->RandomFloat( 32, 64 ) ); if ( random->RandomInt( 0, 1 ) ) { flags |= FXSTATICLINE_FLIP_HORIZONTAL; } else { flags = 0; } lineData.m_flDieTime = lifetime * 0.5f; lineData.m_flStartAlpha= luminosity; lineData.m_flEndAlpha = 0.0f; lineData.m_flStartScale = radius; lineData.m_flEndScale = radius*2; lineData.m_pMaterial = materials->FindMaterial( "effects/splash3", 0, 0 ); lineData.m_vecStart = start; lineData.m_vecStartVelocity = vec3_origin; lineData.m_vecEnd = end; lineData.m_vecEndVelocity = Vector(0,0,1) * random->RandomFloat( 64, 128 ); FX_AddLine( lineData ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_WaterExplosionEffect::CreateDebris( void ) { if ( m_fFlags & TE_EXPLFLAG_NOPARTICLES ) return; // Must be in deep enough water if ( m_flDepth <= 128 ) return; Vector offset; int i; //Spread constricts as force rises float force = m_flForce; //Cap our force if ( force < EXPLOSION_FORCE_MIN ) force = EXPLOSION_FORCE_MIN; if ( force > EXPLOSION_FORCE_MAX ) force = EXPLOSION_FORCE_MAX; float spread = 1.0f - (0.15f*force); SimpleParticle *pParticle; CSmartPtr<CWaterExplosionParticle> pSimple = CWaterExplosionParticle::Create( "waterexp_bubbles" ); pSimple->SetSortOrigin( m_vecOrigin ); pSimple->SetNearClip( 64, 128 ); //FIXME: Better sampling area offset = m_vecOrigin + ( m_vecDirection * 64.0f ); //Find area ambient light color and use it to tint bubbles Vector worldLight; FX_GetSplashLighting( offset, &worldLight, NULL ); // // Smoke // CParticleSubTexture *pMaterial[2]; pMaterial[0] = pSimple->GetPMaterial( "effects/splash1" ); pMaterial[1] = pSimple->GetPMaterial( "effects/splash2" ); for ( i = 0; i < 16; i++ ) { offset.Random( -32.0f, 32.0f ); offset += m_vecOrigin; pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pMaterial[random->RandomInt(0,1)], offset ); if ( pParticle != NULL ) { pParticle->m_flLifetime = 0.0f; #ifdef INVASION_CLIENT_DLL pParticle->m_flDieTime = random->RandomFloat( 0.5f, 1.0f ); #else pParticle->m_flDieTime = random->RandomFloat( 2.0f, 3.0f ); #endif pParticle->m_vecVelocity.Random( -spread, spread ); pParticle->m_vecVelocity += ( m_vecDirection * random->RandomFloat( 1.0f, 6.0f ) ); VectorNormalize( pParticle->m_vecVelocity ); float fForce = 1500 * force; //Scale the force down as we fall away from our main direction ScaleForceByDeviation( pParticle->m_vecVelocity, m_vecDirection, spread, &fForce ); pParticle->m_vecVelocity *= fForce; #if __EXPLOSION_DEBUG debugoverlay->AddLineOverlay( m_vecOrigin, m_vecOrigin + pParticle->m_vecVelocity, 255, 0, 0, false, 3 ); #endif pParticle->m_uchColor[0] = m_vecColor.x * 255; pParticle->m_uchColor[1] = m_vecColor.y * 255; pParticle->m_uchColor[2] = m_vecColor.z * 255; pParticle->m_uchStartSize = random->RandomInt( 32, 64 ); pParticle->m_uchEndSize = pParticle->m_uchStartSize * 2; pParticle->m_uchStartAlpha = m_flLuminosity; pParticle->m_uchEndAlpha = 0; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = random->RandomFloat( -8.0f, 8.0f ); } } }
//----------------------------------------------------------------------------- // Purpose: // Input : *pFluid - // *pObject - // *pEntity - //----------------------------------------------------------------------------- void PhysicsSplash( IPhysicsFluidController *pFluid, IPhysicsObject *pObject, CBaseEntity *pEntity ) { //FIXME: For now just allow ragdolls for E3 - jdw if ( ( pObject->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL ) == false ) return; Vector velocity; pObject->GetVelocity( &velocity, NULL ); float impactSpeed = velocity.Length(); if ( impactSpeed < 25.0f ) return; Vector normal; float dist; pFluid->GetSurfacePlane( &normal, &dist ); matrix3x4_t &matrix = pEntity->EntityToWorldTransform(); // Find the local axis that best matches the water surface normal int bestAxis = BestAxisMatchingNormal( matrix, normal ); Vector tangent, binormal; MatrixGetColumn( matrix, (bestAxis+1)%3, tangent ); binormal = CrossProduct( normal, tangent ); VectorNormalize( binormal ); tangent = CrossProduct( binormal, normal ); VectorNormalize( tangent ); // Now we have a basis tangent to the surface that matches the object's local orientation as well as possible // compute an OBB using this basis // Get object extents in basis Vector tanPts[2], binPts[2]; tanPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -tangent ); tanPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), tangent ); binPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -binormal ); binPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), binormal ); // now compute the centered bbox float mins[2], maxs[2], center[2], extents[2]; mins[0] = DotProduct( tanPts[0], tangent ); maxs[0] = DotProduct( tanPts[1], tangent ); mins[1] = DotProduct( binPts[0], binormal ); maxs[1] = DotProduct( binPts[1], binormal ); center[0] = 0.5 * (mins[0] + maxs[0]); center[1] = 0.5 * (mins[1] + maxs[1]); extents[0] = maxs[0] - center[0]; extents[1] = maxs[1] - center[1]; Vector centerPoint = center[0] * tangent + center[1] * binormal + dist * normal; Vector axes[2]; axes[0] = (maxs[0] - center[0]) * tangent; axes[1] = (maxs[1] - center[1]) * binormal; // visualize OBB hit /* Vector corner1 = centerPoint - axes[0] - axes[1]; Vector corner2 = centerPoint + axes[0] - axes[1]; Vector corner3 = centerPoint + axes[0] + axes[1]; Vector corner4 = centerPoint - axes[0] + axes[1]; NDebugOverlay::Line( corner1, corner2, 0, 0, 255, false, 10 ); NDebugOverlay::Line( corner2, corner3, 0, 0, 255, false, 10 ); NDebugOverlay::Line( corner3, corner4, 0, 0, 255, false, 10 ); NDebugOverlay::Line( corner4, corner1, 0, 0, 255, false, 10 ); */ Vector corner[4]; corner[0] = centerPoint - axes[0] - axes[1]; corner[1] = centerPoint + axes[0] - axes[1]; corner[2] = centerPoint + axes[0] + axes[1]; corner[3] = centerPoint - axes[0] + axes[1]; int contents = enginetrace->GetPointContents( centerPoint-Vector(0,0,2), MASK_WATER ); bool bInSlime = ( contents & CONTENTS_SLIME ) ? true : false; Vector color = vec3_origin; float luminosity = 1.0f; if ( !bInSlime ) { // Get our lighting information FX_GetSplashLighting( centerPoint + ( normal * 8.0f ), &color, &luminosity ); } if ( impactSpeed > 150 ) { if ( bInSlime ) { FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) ); } else { FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) ); } } else if ( !bInSlime ) { FX_WaterRipple( centerPoint, 1.5f, &color, 1.5f, luminosity ); } int splashes = 4; Vector point; for ( int i = 0; i < splashes; i++ ) { point = RandomVector( -32.0f, 32.0f ); point[2] = 0.0f; point += corner[i]; if ( impactSpeed > 150 ) { if ( bInSlime ) { FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) ); } else { FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) ); } } else if ( !bInSlime ) { FX_WaterRipple( point, random->RandomFloat( 0.25f, 0.5f ), &color, luminosity, random->RandomFloat( 0.5f, 1.0f ) ); } } }
//----------------------------------------------------------------------------- // Purpose: // Input : &origin - // &normal - // scale - // *pColor - //----------------------------------------------------------------------------- void FX_GunshotSlimeSplash( const Vector &origin, const Vector &normal, float scale ) { if ( cl_show_splashes.GetBool() == false ) return; VPROF_BUDGET( "FX_GunshotSlimeSplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); float colorRamp; float flScale = MIN( 1.0f, scale / 8.0f ); PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/slime1" ); PMaterialHandle hMaterial2 = ParticleMgr()->GetPMaterial( "effects/splash4" ); Vector color; float luminosity; // Get our lighting information FX_GetSplashLighting( origin + ( normal * scale ), &color, &luminosity ); Vector offDir; Vector offset; TrailParticle *tParticle; CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "splash" ); if ( !sparkEmitter ) return; sparkEmitter->SetSortOrigin( origin ); sparkEmitter->m_ParticleCollision.SetGravity( 800.0f ); sparkEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN ); sparkEmitter->SetVelocityDampen( 2.0f ); if ( IsXbox() ) { sparkEmitter->GetBinding().SetBBox( origin - Vector( 32, 32, 64 ), origin + Vector( 32, 32, 64 ) ); } //Dump out drops for ( int i = 0; i < 24; i++ ) { offset = origin; offset[0] += random->RandomFloat( -16.0f, 16.0f ) * flScale; offset[1] += random->RandomFloat( -16.0f, 16.0f ) * flScale; tParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset ); if ( tParticle == NULL ) break; tParticle->m_flLifetime = 0.0f; tParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f ); offDir = normal + RandomVector( -0.6f, 0.6f ); tParticle->m_vecVelocity = offDir * random->RandomFloat( SPLASH_MIN_SPEED * flScale * 3.0f, SPLASH_MAX_SPEED * flScale * 3.0f ); tParticle->m_vecVelocity[2] += random->RandomFloat( 32.0f, 64.0f ) * flScale; tParticle->m_flWidth = random->RandomFloat( 3.0f, 6.0f ) * flScale; tParticle->m_flLength = random->RandomFloat( 0.025f, 0.05f ) * flScale; colorRamp = random->RandomFloat( 0.75f, 1.25f ); tParticle->m_color.r = MIN( 1.0f, color.x * colorRamp ) * 255; tParticle->m_color.g = MIN( 1.0f, color.y * colorRamp ) * 255; tParticle->m_color.b = MIN( 1.0f, color.z * colorRamp ) * 255; tParticle->m_color.a = 255 * luminosity; } // Setup splash emitter CSmartPtr<CSplashParticle> pSimple = CSplashParticle::Create( "splish" ); pSimple->SetSortOrigin( origin ); pSimple->SetClipHeight( origin.z ); pSimple->SetParticleCullRadius( scale * 2.0f ); if ( IsXbox() ) { pSimple->GetBinding().SetBBox( origin - Vector( 32, 32, 64 ), origin + Vector( 32, 32, 64 ) ); } SimpleParticle *pParticle; // Tint colorRamp = random->RandomFloat( 0.75f, 1.0f ); color = Vector( 1.0f, 0.8f, 0.0f ) * color * colorRamp; //Main gout for ( int i = 0; i < 8; i++ ) { pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial2, origin ); if ( pParticle == NULL ) break; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = 2.0f; //NOTENOTE: We use a clip plane to realistically control our lifespan pParticle->m_vecVelocity.Random( -0.2f, 0.2f ); pParticle->m_vecVelocity += ( normal * random->RandomFloat( 4.0f, 6.0f ) ); VectorNormalize( pParticle->m_vecVelocity ); pParticle->m_vecVelocity *= 50 * flScale * (8-i); colorRamp = random->RandomFloat( 0.75f, 1.25f ); pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; pParticle->m_uchStartSize = 24 * flScale * RemapValClamped( i, 7, 0, 1, 0.5f ); pParticle->m_uchEndSize = MIN( 255, pParticle->m_uchStartSize * 2 ); pParticle->m_uchStartAlpha = RemapValClamped( i, 7, 0, 255, 32 ) * luminosity; pParticle->m_uchEndAlpha = 0; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f ); } //Play a sound CLocalPlayerFilter filter; EmitSound_t ep; ep.m_nChannel = CHAN_VOICE; ep.m_pSoundName = "Physics.WaterSplash"; ep.m_flVolume = 1.0f; ep.m_SoundLevel = SNDLVL_NORM; ep.m_pOrigin = &origin; C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep ); }