//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Client's energy wave // // $Workfile: $ // $Date: $ // //----------------------------------------------------------------------------- // $Log: $ // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "materialsystem/IMaterialSystem.h" #include "materialsystem/IMesh.h" #include "energy_wave_effect.h" #include "mathlib/VMatrix.h" #include "ClientEffectPrecacheSystem.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" CLIENTEFFECT_REGISTER_BEGIN( PrecacheEnergyWave ) CLIENTEFFECT_MATERIAL( "effects/energywave/energywave" ) CLIENTEFFECT_REGISTER_END() //----------------------------------------------------------------------------- // Energy Wave: //----------------------------------------------------------------------------- class C_EnergyWave : public C_BaseEntity { public: DECLARE_CLASS( C_EnergyWave, C_BaseEntity ); DECLARE_CLIENTCLASS(); C_EnergyWave(); ~C_EnergyWave(); void PostDataUpdate( DataUpdateType_t updateType ); int DrawModel( int flags ); void ComputePoint( float s, float t, Vector& pt, Vector& normal, float& opacity ); void DrawWireframeModel( ); CEnergyWaveEffect m_EWaveEffect; IMaterial* m_pWireframe; IMaterial* m_pEWaveMat; private: C_EnergyWave( const C_EnergyWave & ); // not defined, not accessible void ComputeEWavePoints( Vector* pt, Vector* normal, float* opacity ); void DrawEWavePoints(Vector* pt, Vector* normal, float* opacity); }; EXTERN_RECV_TABLE(DT_BaseEntity); IMPLEMENT_CLIENTCLASS_DT(C_EnergyWave, DT_EWaveEffect, CEnergyWave) END_RECV_TABLE() // ---------------------------------------------------------------------------- // Functions. // ---------------------------------------------------------------------------- C_EnergyWave::C_EnergyWave() : m_EWaveEffect(NULL, NULL) { m_pWireframe = materials->FindMaterial("shadertest/wireframevertexcolor", TEXTURE_GROUP_OTHER); m_pEWaveMat = materials->FindMaterial("effects/energywave/energywave", TEXTURE_GROUP_CLIENT_EFFECTS); m_EWaveEffect.Spawn(); } C_EnergyWave::~C_EnergyWave() { } void C_EnergyWave::PostDataUpdate( DataUpdateType_t updateType ) { MarkMessageReceived(); // Make sure that origin points to current origin, at least MoveToLastReceivedPosition(); } enum { NUM_SUBDIVISIONS = 21, }; static void ComputeIndices( int is, int it, int* idx ) { int is0 = (is > 0) ? (is - 1) : is; int it0 = (it > 0) ? (it - 1) : it; int is1 = (is < EWAVE_NUM_HORIZONTAL_POINTS - 1) ? is + 1 : is; int it1 = (it < EWAVE_NUM_HORIZONTAL_POINTS - 1) ? it + 1 : it; int is2 = is + 2; int it2 = it + 2; if (is2 >= EWAVE_NUM_HORIZONTAL_POINTS) is2 = EWAVE_NUM_HORIZONTAL_POINTS - 1; if (it2 >= EWAVE_NUM_HORIZONTAL_POINTS) it2 = EWAVE_NUM_HORIZONTAL_POINTS - 1; idx[0] = is0 + it0 * EWAVE_NUM_HORIZONTAL_POINTS; idx[1] = is + it0 * EWAVE_NUM_HORIZONTAL_POINTS; idx[2] = is1 + it0 * EWAVE_NUM_HORIZONTAL_POINTS; idx[3] = is2 + it0 * EWAVE_NUM_HORIZONTAL_POINTS; idx[4] = is0 + it * EWAVE_NUM_HORIZONTAL_POINTS; idx[5] = is + it * EWAVE_NUM_HORIZONTAL_POINTS; idx[6] = is1 + it * EWAVE_NUM_HORIZONTAL_POINTS; idx[7] = is2 + it * EWAVE_NUM_HORIZONTAL_POINTS; idx[8] = is0 + it1 * EWAVE_NUM_HORIZONTAL_POINTS; idx[9] = is + it1 * EWAVE_NUM_HORIZONTAL_POINTS; idx[10] = is1 + it1 * EWAVE_NUM_HORIZONTAL_POINTS; idx[11] = is2 + it1 * EWAVE_NUM_HORIZONTAL_POINTS; idx[12] = is0 + it2 * EWAVE_NUM_HORIZONTAL_POINTS; idx[13] = is + it2 * EWAVE_NUM_HORIZONTAL_POINTS; idx[14] = is1 + it2 * EWAVE_NUM_HORIZONTAL_POINTS; idx[15] = is2 + it2 * EWAVE_NUM_HORIZONTAL_POINTS; } void C_EnergyWave::ComputePoint( float s, float t, Vector& pt, Vector& normal, float& opacity ) { int is = (int)s; int it = (int)t; if( is >= EWAVE_NUM_HORIZONTAL_POINTS ) is -= 1; if( it >= EWAVE_NUM_VERTICAL_POINTS ) it -= 1; int idx[16]; ComputeIndices( is, it, idx ); // The patch equation is: // px = S * M * Gx * M^T * T^T // py = S * M * Gy * M^T * T^T // pz = S * M * Gz * M^T * T^T // where S = [s^3 s^2 s 1], T = [t^3 t^2 t 1] // M is the patch type matrix, in my case I'm using a catmull-rom // G is the array of control points. rows have constant t static VMatrix catmullRom( -0.5, 1.5, -1.5, 0.5, 1, -2.5, 2, -0.5, -0.5, 0, 0.5, 0, 0, 1, 0, 0 ); VMatrix controlPointsX, controlPointsY, controlPointsZ, controlPointsO; Vector pos; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { const Vector& v = m_EWaveEffect.GetPoint( idx[i * 4 + j] ); controlPointsX[j][i] = v.x; controlPointsY[j][i] = v.y; controlPointsZ[j][i] = v.z; controlPointsO[j][i] = m_EWaveEffect.ComputeOpacity( v, GetAbsOrigin() ); } } float fs = s - is; float ft = t - it; VMatrix temp, mgm[4]; MatrixTranspose( catmullRom, temp ); MatrixMultiply( controlPointsX, temp, mgm[0] ); MatrixMultiply( controlPointsY, temp, mgm[1] ); MatrixMultiply( controlPointsZ, temp, mgm[2] ); MatrixMultiply( controlPointsO, temp, mgm[3] ); MatrixMultiply( catmullRom, mgm[0], mgm[0] ); MatrixMultiply( catmullRom, mgm[1], mgm[1] ); MatrixMultiply( catmullRom, mgm[2], mgm[2] ); MatrixMultiply( catmullRom, mgm[3], mgm[3] ); Vector4D svec, tvec; float ft2 = ft * ft; tvec[0] = ft2 * ft; tvec[1] = ft2; tvec[2] = ft; tvec[3] = 1.0f; float fs2 = fs * fs; svec[0] = fs2 * fs; svec[1] = fs2; svec[2] = fs; svec[3] = 1.0f; Vector4D tmp; Vector4DMultiply( mgm[0], tvec, tmp ); pt[0] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[1], tvec, tmp ); pt[1] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[2], tvec, tmp ); pt[2] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[3], tvec, tmp ); opacity = DotProduct4D( tmp, svec ); if ((s == 0.0f) || (t == 0.0f) || (s == (EWAVE_NUM_HORIZONTAL_POINTS-1.0f)) || (t == (EWAVE_NUM_VERTICAL_POINTS-1.0f)) ) { opacity = 0.0f; } if ((s <= 0.3) || (t < 0.3)) { opacity *= 0.35f; } if ((s == (EWAVE_NUM_HORIZONTAL_POINTS-0.7f)) || (t == (EWAVE_NUM_VERTICAL_POINTS-0.7f)) ) { opacity *= 0.35f; } if (opacity < 0.0f) opacity = 0.0f; else if (opacity > 255.0f) opacity = 255.0f; // Normal computation Vector4D dsvec, dtvec; dsvec[0] = 3.0f * fs2; dsvec[1] = 2.0f * fs; dsvec[2] = 1.0f; dsvec[3] = 0.0f; dtvec[0] = 3.0f * ft2; dtvec[1] = 2.0f * ft; dtvec[2] = 1.0f; dtvec[3] = 0.0f; Vector ds, dt; Vector4DMultiply( mgm[0], tvec, tmp ); ds[0] = DotProduct4D( tmp, dsvec ); Vector4DMultiply( mgm[1], tvec, tmp ); ds[1] = DotProduct4D( tmp, dsvec ); Vector4DMultiply( mgm[2], tvec, tmp ); ds[2] = DotProduct4D( tmp, dsvec ); Vector4DMultiply( mgm[0], dtvec, tmp ); dt[0] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[1], dtvec, tmp ); dt[1] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[2], dtvec, tmp ); dt[2] = DotProduct4D( tmp, svec ); CrossProduct( ds, dt, normal ); VectorNormalize( normal ); } /*double noise2(double arg) { return (arg + random->RandomFloat(-1.5f, 1.5f)); } static void RandomizeNormal1(Vector &vec) { vec.x = 2.0f * (noise2(vec.x) - 0.5f); vec.y = 2.0f * (noise2(vec.y) - 0.5f); vec.z = 2.0f * (noise2(vec.z) - 0.5f); }*/ void C_EnergyWave::DrawWireframeModel( ) { //IMesh* pMesh = materials->GetDynamicMesh( true, NULL, NULL, m_pWireframe ); CMatRenderContextPtr pRenderContext( materials ); IMesh* pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_pWireframe ); int numLines = (EWAVE_NUM_VERTICAL_POINTS - 1) * EWAVE_NUM_HORIZONTAL_POINTS + EWAVE_NUM_VERTICAL_POINTS * (EWAVE_NUM_HORIZONTAL_POINTS - 1); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_LINES, numLines ); Vector tmp; for (int i = 0; i < EWAVE_NUM_VERTICAL_POINTS; ++i) { for (int j = 0; j < EWAVE_NUM_HORIZONTAL_POINTS; ++j) { if ( i > 0 ) { meshBuilder.Position3fv( m_EWaveEffect.GetPoint( j, i ).Base() ); meshBuilder.Color4ub( 255, 255, 255, 128 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( m_EWaveEffect.GetPoint( j, i - 1 ).Base() ); meshBuilder.Color4ub( 255, 255, 255, 128 ); meshBuilder.AdvanceVertex(); } if (j > 0) { meshBuilder.Position3fv( m_EWaveEffect.GetPoint( j, i ).Base() ); meshBuilder.Color4ub( 255, 255, 255, 128 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( m_EWaveEffect.GetPoint( j - 1, i ).Base() ); meshBuilder.Color4ub( 255, 255, 255, 128 ); meshBuilder.AdvanceVertex(); } } } /*Vector origin = tmp; int stacks = 200; int slices = 200; float radius = 5000.0f; // this sucks and stuff float x = origin.x; float y = origin.y; float z = origin.z; float stackAngle, sliceAngle; int stack, slice; Vector v[4]; float sliced, stacked, sliced1, stacked1, stacks1; float slicedsin, slicedcos, stackedsin, stackedcos; float sliced1sin, sliced1cos, stacked1sin, stacked1cos; float stacks1sin, stacks1cos; float stacksin, stackcos; // IMaterial *pMaterial = materials->FindMaterial("effects/splashwake4", 0); // CMatRenderContextPtr pRenderContext(materials); // IMesh *pMesh = pRenderContext->GetDynamicMesh(true, NULL, NULL, pMaterial); // CMeshBuilder meshBuilder; stackAngle = M_PI / (float)stacks; sliceAngle = 2.0 * M_PI / (float)slices; for (stack = 1; stack < stacks - 1; stack++) { for (slice = 0; slice < slices; slice++) { int i, j; sliced = sliceAngle * slice; stacked = stackAngle * stack; sliced1 = sliceAngle * (slice + 1); stacked1 = stackAngle * (stack + 1); SinCos(sliced, &slicedsin, &slicedcos); SinCos(stacked, &stackedsin, &stackedcos); SinCos(sliced1, &sliced1sin, &sliced1cos); SinCos(stacked1, &stacked1sin, &stacked1cos); v[0][0] = -slicedsin * stackedsin; v[0][1] = slicedcos * stackedsin; v[0][2] = stackedcos; v[1][0] = -sliced1sin * stackedsin; v[1][1] = sliced1cos * stackedsin; v[1][2] = stackedcos; v[2][0] = -sliced1sin * stacked1sin; v[2][1] = sliced1cos * stacked1sin; v[2][2] = stacked1cos; v[3][0] = -slicedsin * stacked1sin; v[3][1] = slicedcos * stacked1sin; v[3][2] = stacked1cos; for (i = 0; i < 4; i++) { for (j = 0; j < 3; j++) { v[i][j] *= radius; } v[i][0] += x; v[i][1] += y; v[i][2] += z; } #if 1 // if( drawWireframe.value ) if (1) { meshBuilder.Begin(pMesh, MATERIAL_QUADS, 1); meshBuilder.Position3fv(v[0].Base()); Vector normal; normal = v[0] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv(v[1].Base()); normal = v[1] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv(v[2].Base()); normal = v[2] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv(v[3].Base()); normal = v[3] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } else { // DrawIndexedQuad( v, 0, 1, 2, 3 ); } #endif } } // do the caps for (slice = 0; slice < slices; slice++) { int i, j; sliced = sliceAngle * slice; stacked = stackAngle * stack; sliced1 = sliceAngle * (slice + 1); stacked1 = stackAngle * (stack + 1); stacks1 = stackAngle * (stacks - 1); SinCos(sliced, &slicedsin, &slicedcos); SinCos(stacked, &stackedsin, &stackedcos); SinCos(sliced1, &sliced1sin, &sliced1cos); SinCos(stacked1, &stacked1sin, &stacked1cos); SinCos(stackAngle, &stacksin, &stackcos); SinCos(stacks1, &stacks1sin, &stacks1cos); v[0][0] = 0.0f; v[0][1] = 0.0f; v[0][2] = 1.0f; v[1][0] = -sliced1sin * stacksin; v[1][1] = sliced1cos * stacksin; v[1][2] = stackcos; v[2][0] = -slicedsin * stacksin; v[2][1] = slicedcos * stacksin; v[2][2] = stackcos; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { v[i][j] *= radius; } v[i][0] += x; v[i][1] += y; v[i][2] += z; } meshBuilder.Begin(pMesh, MATERIAL_TRIANGLES, 1); meshBuilder.Position3fv(v[0].Base()); Vector normal; normal = v[0] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv(v[1].Base()); normal = v[1] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv(v[2].Base()); normal = v[2] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); v[0][0] = 0.0f; v[0][1] = 0.0f; v[0][2] = -1.0f; v[1][0] = -sliced1sin * stacks1sin; v[1][1] = sliced1cos * stacks1sin; v[1][2] = stacks1cos; v[2][0] = -slicedsin * stacks1sin; v[2][1] = slicedcos * stacks1sin; v[2][2] = stacks1cos; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { v[i][j] *= radius; } v[i][0] += x; v[i][1] += y; v[i][2] += z; } meshBuilder.Begin(pMesh, MATERIAL_TRIANGLES, 1); meshBuilder.Position3fv(v[0].Base()); normal = v[0] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv(v[2].Base()); normal = v[2] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv(v[1].Base()); normal = v[1] - origin; VectorNormalize(normal); RandomizeNormal1(normal); VectorNormalize(normal); meshBuilder.Normal3fv(normal.Base()); meshBuilder.AdvanceVertex(); }*/ meshBuilder.End(); pMesh->Draw(); } //----------------------------------------------------------------------------- // Compute the ewave points using catmull-rom //----------------------------------------------------------------------------- void C_EnergyWave::ComputeEWavePoints( Vector* pt, Vector* normal, float* opacity ) { int i; for ( i = 0; i < NUM_SUBDIVISIONS; ++i) { float t = (EWAVE_NUM_VERTICAL_POINTS -1 ) * (float)i / (float)(NUM_SUBDIVISIONS - 1); for (int j = 0; j < NUM_SUBDIVISIONS; ++j) { float s = (EWAVE_NUM_HORIZONTAL_POINTS-1) * (float)j / (float)(NUM_SUBDIVISIONS - 1); int idx = i * NUM_SUBDIVISIONS + j; ComputePoint( s, t, pt[idx], normal[idx], opacity[idx] ); } } } //----------------------------------------------------------------------------- // Draws the base ewave //----------------------------------------------------------------------------- #define TRANSITION_REGION_WIDTH 0.5f void C_EnergyWave::DrawEWavePoints(Vector* pt, Vector* normal, float* opacity) { //IMesh* pMesh = materials->GetDynamicMesh( true, NULL, NULL, m_pEWaveMat ); CMatRenderContextPtr pRenderContext( materials ); IMesh* pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_pEWaveMat ); int numTriangles = (NUM_SUBDIVISIONS - 1) * (NUM_SUBDIVISIONS - 1) * 2; CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numTriangles ); float du = 1.0f / (float)(NUM_SUBDIVISIONS - 1); float dv = du; unsigned char color[3]; color[0] = 255; color[1] = 255; color[2] = 255; for ( int i = 0; i < NUM_SUBDIVISIONS - 1; ++i) { float v = i * dv; for (int j = 0; j < NUM_SUBDIVISIONS - 1; ++j) { int idx = i * NUM_SUBDIVISIONS + j; float u = j * du; meshBuilder.Position3fv( pt[idx].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx] ); meshBuilder.Normal3fv( normal[idx].Base() ); meshBuilder.TexCoord2f( 0, u, v ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + NUM_SUBDIVISIONS].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+NUM_SUBDIVISIONS] ); meshBuilder.Normal3fv( normal[idx + NUM_SUBDIVISIONS].Base() ); meshBuilder.TexCoord2f( 0, u, v + dv ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + 1].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+1] ); meshBuilder.Normal3fv( normal[idx+1].Base() ); meshBuilder.TexCoord2f( 0, u + du, v ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + 1].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+1] ); meshBuilder.Normal3fv( normal[idx+1].Base() ); meshBuilder.TexCoord2f( 0, u + du, v ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + NUM_SUBDIVISIONS].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+NUM_SUBDIVISIONS] ); meshBuilder.Normal3fv( normal[idx + NUM_SUBDIVISIONS].Base() ); meshBuilder.TexCoord2f( 0, u, v + dv ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + NUM_SUBDIVISIONS + 1].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+NUM_SUBDIVISIONS+1] ); meshBuilder.Normal3fv( normal[idx + NUM_SUBDIVISIONS + 1].Base() ); meshBuilder.TexCoord2f( 0, u + du, v + dv ); meshBuilder.AdvanceVertex(); } } meshBuilder.End(); pMesh->Draw(); } //----------------------------------------------------------------------------- // Main draw entry point //----------------------------------------------------------------------------- int C_EnergyWave::DrawModel( int flags ) { if ( !m_bReadyToDraw ) return 0; // NOTE: We've got a stiff spring case here, we need to simulate at // a fairly fast timestep. A better solution would be to use an // implicit method, which I'm going to not implement for the moment float dt = gpGlobals->frametime; m_EWaveEffect.SetPosition( GetAbsOrigin(), GetAbsAngles() ); m_EWaveEffect.Simulate(dt); Vector pt[NUM_SUBDIVISIONS * NUM_SUBDIVISIONS]; Vector normal[NUM_SUBDIVISIONS * NUM_SUBDIVISIONS]; float opacity[NUM_SUBDIVISIONS * NUM_SUBDIVISIONS]; ComputeEWavePoints( pt, normal, opacity ); DrawEWavePoints( pt, normal, opacity ); return 1; }