W3DLaserDraw.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: W3DLaserDraw.cpp /////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, May 2001
  25. // Desc: W3DLaserDraw
  26. // Updated: Kris Morness July 2002 -- made it data driven and added new features to make it flexible.
  27. ///////////////////////////////////////////////////////////////////////////////////////////////////
  28. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  29. #include <stdlib.h>
  30. #include "Common/Thing.h"
  31. #include "Common/ThingTemplate.h"
  32. #include "Common/Xfer.h"
  33. #include "GameClient/Color.h"
  34. #include "GameClient/Drawable.h"
  35. #include "GameClient/GameClient.h"
  36. #include "GameClient/RayEffect.h"
  37. #include "GameLogic/GameLogic.h"
  38. #include "GameLogic/Object.h"
  39. #include "GameLogic/TerrainLogic.h"
  40. #include "GameLogic/Module/LaserUpdate.h"
  41. #include "W3DDevice/GameClient/Module/W3DLaserDraw.h"
  42. #include "W3DDevice/GameClient/W3DDisplay.h"
  43. #include "W3DDevice/GameClient/W3DScene.h"
  44. #include "WW3D2/RInfo.h"
  45. #include "WW3D2/Camera.h"
  46. #include "WW3D2/Segline.h"
  47. #include "WWMath/Vector3.h"
  48. #include "WW3D2/AssetMgr.h"
  49. #ifdef _INTERNAL
  50. // for occasional debugging...
  51. //#pragma optimize("", off)
  52. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  53. #endif
  54. // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
  55. //-------------------------------------------------------------------------------------------------
  56. //-------------------------------------------------------------------------------------------------
  57. W3DLaserDrawModuleData::W3DLaserDrawModuleData()
  58. {
  59. m_innerBeamWidth = 0.0f; //The total width of beam
  60. m_outerBeamWidth = 1.0f; //The total width of beam
  61. m_numBeams = 1; //Number of overlapping cylinders that make the beam. 1 beam will just use inner data.
  62. m_maxIntensityFrames = 0; //Laser stays at max intensity for specified time in ms.
  63. m_fadeFrames = 0; //Laser will fade and delete.
  64. m_scrollRate = 0.0f;
  65. m_tile = false;
  66. m_segments = 1;
  67. m_arcHeight = 0.0f;
  68. m_segmentOverlapRatio = 0.0f;
  69. m_tilingScalar = 1.0f;
  70. }
  71. //-------------------------------------------------------------------------------------------------
  72. //-------------------------------------------------------------------------------------------------
  73. W3DLaserDrawModuleData::~W3DLaserDrawModuleData()
  74. {
  75. }
  76. //-------------------------------------------------------------------------------------------------
  77. //-------------------------------------------------------------------------------------------------
  78. void W3DLaserDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
  79. {
  80. ModuleData::buildFieldParse(p);
  81. static const FieldParse dataFieldParse[] =
  82. {
  83. { "NumBeams", INI::parseUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_numBeams ) },
  84. { "InnerBeamWidth", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_innerBeamWidth ) },
  85. { "OuterBeamWidth", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_outerBeamWidth ) },
  86. { "InnerColor", INI::parseColorInt, NULL, offsetof( W3DLaserDrawModuleData, m_innerColor ) },
  87. { "OuterColor", INI::parseColorInt, NULL, offsetof( W3DLaserDrawModuleData, m_outerColor ) },
  88. { "MaxIntensityLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_maxIntensityFrames ) },
  89. { "FadeLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_fadeFrames ) },
  90. { "Texture", INI::parseAsciiString, NULL, offsetof( W3DLaserDrawModuleData, m_textureName ) },
  91. { "ScrollRate", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_scrollRate ) },
  92. { "Tile", INI::parseBool, NULL, offsetof( W3DLaserDrawModuleData, m_tile ) },
  93. { "Segments", INI::parseUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_segments ) },
  94. { "ArcHeight", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_arcHeight ) },
  95. { "SegmentOverlapRatio", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_segmentOverlapRatio ) },
  96. { "TilingScalar", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_tilingScalar ) },
  97. { 0, 0, 0, 0 }
  98. };
  99. p.add(dataFieldParse);
  100. }
  101. //-------------------------------------------------------------------------------------------------
  102. //-------------------------------------------------------------------------------------------------
  103. W3DLaserDraw::W3DLaserDraw( Thing *thing, const ModuleData* moduleData ) :
  104. DrawModule( thing, moduleData ),
  105. m_line3D(NULL),
  106. m_texture(NULL),
  107. m_textureAspectRatio(1.0f),
  108. m_selfDirty(TRUE)
  109. {
  110. Vector3 dummyPos1( 0.0f, 0.0f, 0.0f );
  111. Vector3 dummyPos2( 1.0f, 1.0f, 1.0f );
  112. Int i;
  113. const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
  114. m_texture = WW3DAssetManager::Get_Instance()->Get_Texture( data->m_textureName.str() );
  115. if (m_texture)
  116. {
  117. SurfaceClass::SurfaceDescription surfaceDesc;
  118. m_texture->Get_Level_Description(surfaceDesc);
  119. m_textureAspectRatio = (Real)surfaceDesc.Width/(Real)surfaceDesc.Height;
  120. }
  121. //Get the color components for calculation purposes.
  122. Real innerRed, innerGreen, innerBlue, innerAlpha, outerRed, outerGreen, outerBlue, outerAlpha;
  123. GameGetColorComponentsReal( data->m_innerColor, &innerRed, &innerGreen, &innerBlue, &innerAlpha );
  124. GameGetColorComponentsReal( data->m_outerColor, &outerRed, &outerGreen, &outerBlue, &outerAlpha );
  125. //Make sure our beams range between 1 and the maximum cap.
  126. #ifdef I_WANT_TO_BE_FIRED
  127. // srj sez: this data is const for a reason. casting away the constness because we don't like the values
  128. // isn't an acceptable solution. if you need to constrain the values, do so at parsing time, when
  129. // it's still legal to modify these values. (In point of fact, there's not even really any reason to limit
  130. // the numBeams or segments anymore.)
  131. data->m_numBeams = __min( __max( 1, data->m_numBeams ), MAX_LASER_LINES );
  132. data->m_segments = __min( __max( 1, data->m_segments ), MAX_SEGMENTS );
  133. data->m_tilingScalar = __max( 0.01f, data->m_tilingScalar );
  134. #endif
  135. //Allocate an array of lines equal to the number of beams * segments
  136. m_line3D = NEW SegmentedLineClass *[ data->m_numBeams * data->m_segments ];
  137. for( int segment = 0; segment < data->m_segments; segment++ )
  138. {
  139. //We don't care about segment positioning yet until we actually set the position
  140. // create all the lines we need at the right transparency level
  141. for( i = data->m_numBeams - 1; i >= 0; i-- )
  142. {
  143. int index = segment * data->m_numBeams + i;
  144. Real red, green, blue, alpha, width;
  145. if( data->m_numBeams == 1 )
  146. {
  147. width = data->m_innerBeamWidth;
  148. alpha = innerAlpha;
  149. red = innerRed * innerAlpha;
  150. green = innerGreen * innerAlpha;
  151. blue = innerBlue * innerAlpha;
  152. }
  153. else
  154. {
  155. //Calculate the scale between min and max values
  156. //0 means use min value, 1 means use max value
  157. //0.2 means min value + 20% of the diff between min and max
  158. Real scale = i / ( data->m_numBeams - 1.0f);
  159. width = data->m_innerBeamWidth + scale * (data->m_outerBeamWidth - data->m_innerBeamWidth);
  160. alpha = innerAlpha + scale * (outerAlpha - innerAlpha);
  161. red = innerRed + scale * (outerRed - innerRed) * innerAlpha;
  162. green = innerGreen + scale * (outerGreen - innerGreen) * innerAlpha;
  163. blue = innerBlue + scale * (outerBlue - innerBlue) * innerAlpha;
  164. }
  165. m_line3D[ index ] = NEW SegmentedLineClass;
  166. SegmentedLineClass *line = m_line3D[ index ];
  167. if( line )
  168. {
  169. line->Set_Texture( m_texture );
  170. line->Set_Shader( ShaderClass::_PresetAdditiveShader ); //pick the alpha blending mode you want - see shader.h for others.
  171. line->Set_Width( width );
  172. line->Set_Color( Vector3( red, green, blue ) );
  173. line->Set_UV_Offset_Rate( Vector2(0.0f, data->m_scrollRate) ); //amount to scroll texture on each draw
  174. if( m_texture )
  175. {
  176. line->Set_Texture_Mapping_Mode(SegLineRendererClass::TILED_TEXTURE_MAP); //this tiles the texture across the line
  177. }
  178. // add to scene
  179. W3DDisplay::m_3DScene->Add_Render_Object( line ); //add it to our scene so it gets rendered with other objects.
  180. // hide the render object until the first time we come to draw it and
  181. // set the correct position
  182. line->Set_Visible( 0 );
  183. }
  184. } // end for i
  185. } //end segment loop
  186. }
  187. //-------------------------------------------------------------------------------------------------
  188. //-------------------------------------------------------------------------------------------------
  189. W3DLaserDraw::~W3DLaserDraw( void )
  190. {
  191. const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
  192. for( int i = 0; i < data->m_numBeams * data->m_segments; i++ )
  193. {
  194. // remove line from scene
  195. W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] );
  196. // delete line
  197. REF_PTR_RELEASE( m_line3D[ i ] );
  198. } // end for i
  199. delete [] m_line3D;
  200. }
  201. //-------------------------------------------------------------------------------------------------
  202. //-------------------------------------------------------------------------------------------------
  203. Real W3DLaserDraw::getLaserTemplateWidth() const
  204. {
  205. const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
  206. return data->m_outerBeamWidth * 0.5f;
  207. }
  208. //-------------------------------------------------------------------------------------------------
  209. //-------------------------------------------------------------------------------------------------
  210. void W3DLaserDraw::doDrawModule(const Matrix3D* transformMtx)
  211. {
  212. //UnsignedInt currentFrame = TheGameClient->getFrame();
  213. const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
  214. //Get the updatemodule that drives it...
  215. Drawable *draw = getDrawable();
  216. static NameKeyType key_LaserUpdate = NAMEKEY( "LaserUpdate" );
  217. LaserUpdate *update = (LaserUpdate*)draw->findClientUpdateModule( key_LaserUpdate );
  218. if( !update )
  219. {
  220. DEBUG_ASSERTCRASH( 0, ("W3DLaserDraw::doDrawModule() expects its owner drawable %s to have a ClientUpdate = LaserUpdate module.", draw->getTemplate()->getName().str() ));
  221. return;
  222. }
  223. //If the update has moved the laser, it requires a reset of the laser.
  224. if (update->isDirty() || m_selfDirty)
  225. {
  226. update->setDirty(false);
  227. m_selfDirty = false;
  228. Vector3 laserPoints[ 2 ];
  229. for( int segment = 0; segment < data->m_segments; segment++ )
  230. {
  231. if( data->m_arcHeight > 0.0f && data->m_segments > 1 )
  232. {
  233. //CALCULATE A CURVED LINE BASED ON TOTAL LENGTH AND DESIRED HEIGHT INCREASE
  234. //To do this we will use a portion of the cos wave ranging between -0.25PI
  235. //and +0.25PI. 0PI is 1.0 and 0.25PI is 0.70 -- resulting in a somewhat
  236. //gentle curve depending on the line height and length. We also have to make
  237. //the line *level* for this phase of the calculations.
  238. //Get the desired direct line
  239. Coord3D lineStart, lineEnd, lineVector;
  240. lineStart.set( update->getStartPos() );
  241. lineEnd.set( update->getEndPos() );
  242. //This is critical -- in the case we have sloped lines (at the end, we'll fix it)
  243. // lineEnd.z = lineStart.z;
  244. //Get the length of the line
  245. lineVector.set( &lineEnd );
  246. lineVector.sub( &lineStart );
  247. Real lineLength = lineVector.length();
  248. //Get the middle point (we'll use this to determine how far we are from
  249. //that to calculate our height -- middle point is the highest).
  250. Coord3D lineMiddle;
  251. lineMiddle.set( &lineStart );
  252. lineMiddle.add( &lineEnd );
  253. lineMiddle.scale( 0.5 );
  254. //The half length is used to scale with the distance from middle to
  255. //get our cos( 0 to 0.25 PI) cos value
  256. Real halfLength = lineLength * 0.5f;
  257. //Now calculate which segment we will use.
  258. Real startSegmentRatio = segment / ((Real)data->m_segments);
  259. Real endSegmentRatio = (segment + 1.0f) / ((Real)data->m_segments);
  260. //Offset the segment ever-so-slightly to minimize overlap -- only apply
  261. //to segments that are not the start/end point
  262. if( segment > 0 )
  263. {
  264. startSegmentRatio -= data->m_segmentOverlapRatio;
  265. }
  266. if( segment < data->m_segments - 1 )
  267. {
  268. endSegmentRatio += data->m_segmentOverlapRatio;
  269. }
  270. //Calculate our start segment position on the *ground*.
  271. Coord3D segmentStart, segmentEnd, vector;
  272. vector.set( &lineVector );
  273. vector.scale( startSegmentRatio );
  274. segmentStart.set( &lineStart );
  275. segmentStart.add( &vector );
  276. //Calculate our end segment position on the *ground*.
  277. vector.set( &lineVector );
  278. vector.scale( endSegmentRatio );
  279. segmentEnd.set( &lineStart );
  280. segmentEnd.add( &vector );
  281. //--------------------------------------------------------------------------------
  282. //Now at this point, we have our segment line in the level positions that we want.
  283. //Calculate the raised height for the start/end segment positions using cosine.
  284. //--------------------------------------------------------------------------------
  285. //Calculate the distance from midpoint for the start positions.
  286. vector.set( &lineMiddle );
  287. vector.sub( &segmentStart );
  288. Real dist = vector.length();
  289. Real scaledRadians = dist / halfLength * PI * 0.5f;
  290. Real height = cos( scaledRadians );
  291. height *= data->m_arcHeight;
  292. segmentStart.z += height;
  293. //Now do the same thing for the end position.
  294. vector.set( &lineMiddle );
  295. vector.sub( &segmentEnd );
  296. dist = vector.length();
  297. scaledRadians = dist / halfLength * PI * 0.5f;
  298. height = cos( scaledRadians );
  299. height *= data->m_arcHeight;
  300. segmentEnd.z += height;
  301. //This makes the laser skim the ground rather than penetrate it!
  302. laserPoints[ 0 ].Set( segmentStart.x, segmentStart.y,
  303. MAX( segmentStart.z, 2.0f + TheTerrainLogic->getGroundHeight(segmentStart.x, segmentStart.y) ) );
  304. laserPoints[ 1 ].Set( segmentEnd.x, segmentEnd.y,
  305. MAX( segmentEnd.z, 2.0f + TheTerrainLogic->getGroundHeight(segmentEnd.x, segmentEnd.y) ) );
  306. }
  307. else
  308. {
  309. //No arc -- way simpler!
  310. laserPoints[ 0 ].Set( update->getStartPos()->x, update->getStartPos()->y, update->getStartPos()->z );
  311. laserPoints[ 1 ].Set( update->getEndPos()->x, update->getEndPos()->y, update->getEndPos()->z );
  312. }
  313. //Get the color components for calculation purposes.
  314. Real innerRed, innerGreen, innerBlue, innerAlpha, outerRed, outerGreen, outerBlue, outerAlpha;
  315. GameGetColorComponentsReal( data->m_innerColor, &innerRed, &innerGreen, &innerBlue, &innerAlpha );
  316. GameGetColorComponentsReal( data->m_outerColor, &outerRed, &outerGreen, &outerBlue, &outerAlpha );
  317. for( Int i = data->m_numBeams - 1; i >= 0; i-- )
  318. {
  319. Real alpha, width;
  320. int index = segment * data->m_numBeams + i;
  321. if( data->m_numBeams == 1 )
  322. {
  323. width = data->m_innerBeamWidth * update->getWidthScale();
  324. alpha = innerAlpha;
  325. }
  326. else
  327. {
  328. //Calculate the scale between min and max values
  329. //0 means use min value, 1 means use max value
  330. //0.2 means min value + 20% of the diff between min and max
  331. Real scale = i / ( data->m_numBeams - 1.0f);
  332. Real ultimateScale = update->getWidthScale();
  333. width = (data->m_innerBeamWidth + scale * (data->m_outerBeamWidth - data->m_innerBeamWidth));
  334. width *= ultimateScale;
  335. alpha = innerAlpha + scale * (outerAlpha - innerAlpha);
  336. }
  337. //Calculate the number of times to tile the line based on the height of the texture used.
  338. if( m_texture && data->m_tile )
  339. {
  340. //Calculate the length of the line.
  341. Vector3 lineVector;
  342. Vector3::Subtract( laserPoints[1], laserPoints[0], &lineVector );
  343. Real length = lineVector.Length();
  344. //Adjust tile factor so texture is NOT stretched but tiled equally in both width and length.
  345. Real tileFactor = length/width*m_textureAspectRatio*data->m_tilingScalar;
  346. //Set the tile factor
  347. m_line3D[ index ]->Set_Texture_Tile_Factor( tileFactor ); //number of times to tile texture across each segment
  348. }
  349. m_line3D[ index ]->Set_Width( width );
  350. m_line3D[ index ]->Set_Points( 2, &laserPoints[0] );
  351. }
  352. }
  353. }
  354. return;
  355. }
  356. // ------------------------------------------------------------------------------------------------
  357. /** CRC */
  358. // ------------------------------------------------------------------------------------------------
  359. void W3DLaserDraw::crc( Xfer *xfer )
  360. {
  361. // extend base class
  362. DrawModule::crc( xfer );
  363. } // end crc
  364. // ------------------------------------------------------------------------------------------------
  365. /** Xfer method
  366. * Version Info:
  367. * 1: Initial version */
  368. // ------------------------------------------------------------------------------------------------
  369. void W3DLaserDraw::xfer( Xfer *xfer )
  370. {
  371. // version
  372. const XferVersion currentVersion = 1;
  373. XferVersion version = currentVersion;
  374. xfer->xferVersion( &version, currentVersion );
  375. // extend base class
  376. DrawModule::xfer( xfer );
  377. // Kris says there is no data to save for these, go ask him.
  378. // m_selfDirty is not saved, is runtime only
  379. } // end xfer
  380. // ------------------------------------------------------------------------------------------------
  381. /** Load post process */
  382. // ------------------------------------------------------------------------------------------------
  383. void W3DLaserDraw::loadPostProcess( void )
  384. {
  385. // extend base class
  386. DrawModule::loadPostProcess();
  387. m_selfDirty = true; // so we update the first time after reload
  388. } // end loadPostProcess