| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: W3DLaserDraw.cpp /////////////////////////////////////////////////////////////////////////
- // Author: Colin Day, May 2001
- // Desc: W3DLaserDraw
- // Updated: Kris Morness July 2002 -- made it data driven and added new features to make it flexible.
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include <stdlib.h>
- #include "Common/Thing.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Xfer.h"
- #include "GameClient/Color.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/GameClient.h"
- #include "GameClient/RayEffect.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/TerrainLogic.h"
- #include "GameLogic/Module/LaserUpdate.h"
- #include "W3DDevice/GameClient/Module/W3DLaserDraw.h"
- #include "W3DDevice/GameClient/W3DDisplay.h"
- #include "W3DDevice/GameClient/W3DScene.h"
- #include "WW3D2/RInfo.h"
- #include "WW3D2/Camera.h"
- #include "WW3D2/Segline.h"
- #include "WWMath/Vector3.h"
- #include "WW3D2/AssetMgr.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- W3DLaserDrawModuleData::W3DLaserDrawModuleData()
- {
- m_innerBeamWidth = 0.0f; //The total width of beam
- m_outerBeamWidth = 1.0f; //The total width of beam
- m_numBeams = 1; //Number of overlapping cylinders that make the beam. 1 beam will just use inner data.
- m_maxIntensityFrames = 0; //Laser stays at max intensity for specified time in ms.
- m_fadeFrames = 0; //Laser will fade and delete.
- m_scrollRate = 0.0f;
- m_tile = false;
- m_segments = 1;
- m_arcHeight = 0.0f;
- m_segmentOverlapRatio = 0.0f;
- m_tilingScalar = 1.0f;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- W3DLaserDrawModuleData::~W3DLaserDrawModuleData()
- {
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void W3DLaserDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- ModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "NumBeams", INI::parseUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_numBeams ) },
- { "InnerBeamWidth", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_innerBeamWidth ) },
- { "OuterBeamWidth", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_outerBeamWidth ) },
- { "InnerColor", INI::parseColorInt, NULL, offsetof( W3DLaserDrawModuleData, m_innerColor ) },
- { "OuterColor", INI::parseColorInt, NULL, offsetof( W3DLaserDrawModuleData, m_outerColor ) },
- { "MaxIntensityLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_maxIntensityFrames ) },
- { "FadeLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_fadeFrames ) },
- { "Texture", INI::parseAsciiString, NULL, offsetof( W3DLaserDrawModuleData, m_textureName ) },
- { "ScrollRate", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_scrollRate ) },
- { "Tile", INI::parseBool, NULL, offsetof( W3DLaserDrawModuleData, m_tile ) },
- { "Segments", INI::parseUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_segments ) },
- { "ArcHeight", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_arcHeight ) },
- { "SegmentOverlapRatio", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_segmentOverlapRatio ) },
- { "TilingScalar", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_tilingScalar ) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- W3DLaserDraw::W3DLaserDraw( Thing *thing, const ModuleData* moduleData ) :
- DrawModule( thing, moduleData ),
- m_line3D(NULL),
- m_texture(NULL),
- m_textureAspectRatio(1.0f),
- m_selfDirty(TRUE)
- {
- Vector3 dummyPos1( 0.0f, 0.0f, 0.0f );
- Vector3 dummyPos2( 1.0f, 1.0f, 1.0f );
- Int i;
- const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
- m_texture = WW3DAssetManager::Get_Instance()->Get_Texture( data->m_textureName.str() );
- if (m_texture)
- {
- if (!m_texture->Is_Initialized())
- m_texture->Init(); //make sure texture is actually loaded before accessing surface.
- SurfaceClass::SurfaceDescription surfaceDesc;
- m_texture->Get_Level_Description(surfaceDesc);
- m_textureAspectRatio = (Real)surfaceDesc.Width/(Real)surfaceDesc.Height;
- }
- //Get the color components for calculation purposes.
- Real innerRed, innerGreen, innerBlue, innerAlpha, outerRed, outerGreen, outerBlue, outerAlpha;
- GameGetColorComponentsReal( data->m_innerColor, &innerRed, &innerGreen, &innerBlue, &innerAlpha );
- GameGetColorComponentsReal( data->m_outerColor, &outerRed, &outerGreen, &outerBlue, &outerAlpha );
- //Make sure our beams range between 1 and the maximum cap.
- #ifdef I_WANT_TO_BE_FIRED
- // srj sez: this data is const for a reason. casting away the constness because we don't like the values
- // isn't an acceptable solution. if you need to constrain the values, do so at parsing time, when
- // it's still legal to modify these values. (In point of fact, there's not even really any reason to limit
- // the numBeams or segments anymore.)
- data->m_numBeams = __min( __max( 1, data->m_numBeams ), MAX_LASER_LINES );
- data->m_segments = __min( __max( 1, data->m_segments ), MAX_SEGMENTS );
- data->m_tilingScalar = __max( 0.01f, data->m_tilingScalar );
- #endif
- //Allocate an array of lines equal to the number of beams * segments
- m_line3D = NEW SegmentedLineClass *[ data->m_numBeams * data->m_segments ];
- for( int segment = 0; segment < data->m_segments; segment++ )
- {
- //We don't care about segment positioning yet until we actually set the position
-
- // create all the lines we need at the right transparency level
- for( i = data->m_numBeams - 1; i >= 0; i-- )
- {
- int index = segment * data->m_numBeams + i;
- Real red, green, blue, alpha, width;
- if( data->m_numBeams == 1 )
- {
- width = data->m_innerBeamWidth;
- alpha = innerAlpha;
- red = innerRed * innerAlpha;
- green = innerGreen * innerAlpha;
- blue = innerBlue * innerAlpha;
- }
- else
- {
- //Calculate the scale between min and max values
- //0 means use min value, 1 means use max value
- //0.2 means min value + 20% of the diff between min and max
- Real scale = i / ( data->m_numBeams - 1.0f);
-
- width = data->m_innerBeamWidth + scale * (data->m_outerBeamWidth - data->m_innerBeamWidth);
- alpha = innerAlpha + scale * (outerAlpha - innerAlpha);
- red = innerRed + scale * (outerRed - innerRed) * innerAlpha;
- green = innerGreen + scale * (outerGreen - innerGreen) * innerAlpha;
- blue = innerBlue + scale * (outerBlue - innerBlue) * innerAlpha;
- }
- m_line3D[ index ] = NEW SegmentedLineClass;
-
- SegmentedLineClass *line = m_line3D[ index ];
- if( line )
- {
- line->Set_Texture( m_texture );
- line->Set_Shader( ShaderClass::_PresetAdditiveShader ); //pick the alpha blending mode you want - see shader.h for others.
- line->Set_Width( width );
- line->Set_Color( Vector3( red, green, blue ) );
- line->Set_UV_Offset_Rate( Vector2(0.0f, data->m_scrollRate) ); //amount to scroll texture on each draw
- if( m_texture )
- {
- line->Set_Texture_Mapping_Mode(SegLineRendererClass::TILED_TEXTURE_MAP); //this tiles the texture across the line
- }
- // add to scene
- W3DDisplay::m_3DScene->Add_Render_Object( line ); //add it to our scene so it gets rendered with other objects.
- // hide the render object until the first time we come to draw it and
- // set the correct position
- line->Set_Visible( 0 );
- }
- } // end for i
- } //end segment loop
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- W3DLaserDraw::~W3DLaserDraw( void )
- {
- const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
- for( int i = 0; i < data->m_numBeams * data->m_segments; i++ )
- {
- // remove line from scene
- W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] );
- // delete line
- REF_PTR_RELEASE( m_line3D[ i ] );
- } // end for i
- delete [] m_line3D;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Real W3DLaserDraw::getLaserTemplateWidth() const
- {
- const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
- return data->m_outerBeamWidth * 0.5f;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void W3DLaserDraw::doDrawModule(const Matrix3D* transformMtx)
- {
- //UnsignedInt currentFrame = TheGameClient->getFrame();
- const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
- //Get the updatemodule that drives it...
- Drawable *draw = getDrawable();
- static NameKeyType key_LaserUpdate = NAMEKEY( "LaserUpdate" );
- LaserUpdate *update = (LaserUpdate*)draw->findClientUpdateModule( key_LaserUpdate );
- if( !update )
- {
- DEBUG_ASSERTCRASH( 0, ("W3DLaserDraw::doDrawModule() expects its owner drawable %s to have a ClientUpdate = LaserUpdate module.", draw->getTemplate()->getName().str() ));
- return;
- }
- //If the update has moved the laser, it requires a reset of the laser.
- if (update->isDirty() || m_selfDirty)
- {
- update->setDirty(false);
- m_selfDirty = false;
- Vector3 laserPoints[ 2 ];
- for( int segment = 0; segment < data->m_segments; segment++ )
- {
- if( data->m_arcHeight > 0.0f && data->m_segments > 1 )
- {
- //CALCULATE A CURVED LINE BASED ON TOTAL LENGTH AND DESIRED HEIGHT INCREASE
- //To do this we will use a portion of the cos wave ranging between -0.25PI
- //and +0.25PI. 0PI is 1.0 and 0.25PI is 0.70 -- resulting in a somewhat
- //gentle curve depending on the line height and length. We also have to make
- //the line *level* for this phase of the calculations.
- //Get the desired direct line
- Coord3D lineStart, lineEnd, lineVector;
- lineStart.set( update->getStartPos() );
- lineEnd.set( update->getEndPos() );
- //This is critical -- in the case we have sloped lines (at the end, we'll fix it)
- // lineEnd.z = lineStart.z;
- //Get the length of the line
- lineVector.set( &lineEnd );
- lineVector.sub( &lineStart );
- Real lineLength = lineVector.length();
- //Get the middle point (we'll use this to determine how far we are from
- //that to calculate our height -- middle point is the highest).
- Coord3D lineMiddle;
- lineMiddle.set( &lineStart );
- lineMiddle.add( &lineEnd );
- lineMiddle.scale( 0.5 );
- //The half length is used to scale with the distance from middle to
- //get our cos( 0 to 0.25 PI) cos value
- Real halfLength = lineLength * 0.5f;
- //Now calculate which segment we will use.
- Real startSegmentRatio = segment / ((Real)data->m_segments);
- Real endSegmentRatio = (segment + 1.0f) / ((Real)data->m_segments);
- //Offset the segment ever-so-slightly to minimize overlap -- only apply
- //to segments that are not the start/end point
- if( segment > 0 )
- {
- startSegmentRatio -= data->m_segmentOverlapRatio;
- }
- if( segment < data->m_segments - 1 )
- {
- endSegmentRatio += data->m_segmentOverlapRatio;
- }
- //Calculate our start segment position on the *ground*.
- Coord3D segmentStart, segmentEnd, vector;
- vector.set( &lineVector );
- vector.scale( startSegmentRatio );
- segmentStart.set( &lineStart );
- segmentStart.add( &vector );
- //Calculate our end segment position on the *ground*.
- vector.set( &lineVector );
- vector.scale( endSegmentRatio );
- segmentEnd.set( &lineStart );
- segmentEnd.add( &vector );
- //--------------------------------------------------------------------------------
- //Now at this point, we have our segment line in the level positions that we want.
- //Calculate the raised height for the start/end segment positions using cosine.
- //--------------------------------------------------------------------------------
- //Calculate the distance from midpoint for the start positions.
- vector.set( &lineMiddle );
- vector.sub( &segmentStart );
- Real dist = vector.length();
- Real scaledRadians = dist / halfLength * PI * 0.5f;
- Real height = cos( scaledRadians );
- height *= data->m_arcHeight;
- segmentStart.z += height;
- //Now do the same thing for the end position.
- vector.set( &lineMiddle );
- vector.sub( &segmentEnd );
- dist = vector.length();
- scaledRadians = dist / halfLength * PI * 0.5f;
- height = cos( scaledRadians );
- height *= data->m_arcHeight;
- segmentEnd.z += height;
-
- //This makes the laser skim the ground rather than penetrate it!
- laserPoints[ 0 ].Set( segmentStart.x, segmentStart.y,
- MAX( segmentStart.z, 2.0f + TheTerrainLogic->getGroundHeight(segmentStart.x, segmentStart.y) ) );
- laserPoints[ 1 ].Set( segmentEnd.x, segmentEnd.y,
- MAX( segmentEnd.z, 2.0f + TheTerrainLogic->getGroundHeight(segmentEnd.x, segmentEnd.y) ) );
-
- }
- else
- {
- //No arc -- way simpler!
- laserPoints[ 0 ].Set( update->getStartPos()->x, update->getStartPos()->y, update->getStartPos()->z );
- laserPoints[ 1 ].Set( update->getEndPos()->x, update->getEndPos()->y, update->getEndPos()->z );
- }
- //Get the color components for calculation purposes.
- Real innerRed, innerGreen, innerBlue, innerAlpha, outerRed, outerGreen, outerBlue, outerAlpha;
- GameGetColorComponentsReal( data->m_innerColor, &innerRed, &innerGreen, &innerBlue, &innerAlpha );
- GameGetColorComponentsReal( data->m_outerColor, &outerRed, &outerGreen, &outerBlue, &outerAlpha );
- for( Int i = data->m_numBeams - 1; i >= 0; i-- )
- {
- Real alpha, width;
- int index = segment * data->m_numBeams + i;
- if( data->m_numBeams == 1 )
- {
- width = data->m_innerBeamWidth * update->getWidthScale();
- alpha = innerAlpha;
- }
- else
- {
- //Calculate the scale between min and max values
- //0 means use min value, 1 means use max value
- //0.2 means min value + 20% of the diff between min and max
- Real scale = i / ( data->m_numBeams - 1.0f);
- Real ultimateScale = update->getWidthScale();
- width = (data->m_innerBeamWidth + scale * (data->m_outerBeamWidth - data->m_innerBeamWidth));
- width *= ultimateScale;
- alpha = innerAlpha + scale * (outerAlpha - innerAlpha);
- }
- //Calculate the number of times to tile the line based on the height of the texture used.
- if( m_texture && data->m_tile )
- {
- //Calculate the length of the line.
- Vector3 lineVector;
- Vector3::Subtract( laserPoints[1], laserPoints[0], &lineVector );
- Real length = lineVector.Length();
- //Adjust tile factor so texture is NOT stretched but tiled equally in both width and length.
- Real tileFactor = length/width*m_textureAspectRatio*data->m_tilingScalar;
- //Set the tile factor
- m_line3D[ index ]->Set_Texture_Tile_Factor( tileFactor ); //number of times to tile texture across each segment
- }
- m_line3D[ index ]->Set_Width( width );
- m_line3D[ index ]->Set_Points( 2, &laserPoints[0] );
- }
- }
- }
-
- return;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void W3DLaserDraw::crc( Xfer *xfer )
- {
- // extend base class
- DrawModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void W3DLaserDraw::xfer( Xfer *xfer )
- {
- // version
- const XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- DrawModule::xfer( xfer );
- // Kris says there is no data to save for these, go ask him.
- // m_selfDirty is not saved, is runtime only
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void W3DLaserDraw::loadPostProcess( void )
- {
- // extend base class
- DrawModule::loadPostProcess();
- m_selfDirty = true; // so we update the first time after reload
- } // end loadPostProcess
|