W3DLaserDraw.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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. if (!m_texture->Is_Initialized())
  118. m_texture->Init(); //make sure texture is actually loaded before accessing surface.
  119. SurfaceClass::SurfaceDescription surfaceDesc;
  120. m_texture->Get_Level_Description(surfaceDesc);
  121. m_textureAspectRatio = (Real)surfaceDesc.Width/(Real)surfaceDesc.Height;
  122. }
  123. //Get the color components for calculation purposes.
  124. Real innerRed, innerGreen, innerBlue, innerAlpha, outerRed, outerGreen, outerBlue, outerAlpha;
  125. GameGetColorComponentsReal( data->m_innerColor, &innerRed, &innerGreen, &innerBlue, &innerAlpha );
  126. GameGetColorComponentsReal( data->m_outerColor, &outerRed, &outerGreen, &outerBlue, &outerAlpha );
  127. //Make sure our beams range between 1 and the maximum cap.
  128. #ifdef I_WANT_TO_BE_FIRED
  129. // srj sez: this data is const for a reason. casting away the constness because we don't like the values
  130. // isn't an acceptable solution. if you need to constrain the values, do so at parsing time, when
  131. // it's still legal to modify these values. (In point of fact, there's not even really any reason to limit
  132. // the numBeams or segments anymore.)
  133. data->m_numBeams = __min( __max( 1, data->m_numBeams ), MAX_LASER_LINES );
  134. data->m_segments = __min( __max( 1, data->m_segments ), MAX_SEGMENTS );
  135. data->m_tilingScalar = __max( 0.01f, data->m_tilingScalar );
  136. #endif
  137. //Allocate an array of lines equal to the number of beams * segments
  138. m_line3D = NEW SegmentedLineClass *[ data->m_numBeams * data->m_segments ];
  139. for( int segment = 0; segment < data->m_segments; segment++ )
  140. {
  141. //We don't care about segment positioning yet until we actually set the position
  142. // create all the lines we need at the right transparency level
  143. for( i = data->m_numBeams - 1; i >= 0; i-- )
  144. {
  145. int index = segment * data->m_numBeams + i;
  146. Real red, green, blue, alpha, width;
  147. if( data->m_numBeams == 1 )
  148. {
  149. width = data->m_innerBeamWidth;
  150. alpha = innerAlpha;
  151. red = innerRed * innerAlpha;
  152. green = innerGreen * innerAlpha;
  153. blue = innerBlue * innerAlpha;
  154. }
  155. else
  156. {
  157. //Calculate the scale between min and max values
  158. //0 means use min value, 1 means use max value
  159. //0.2 means min value + 20% of the diff between min and max
  160. Real scale = i / ( data->m_numBeams - 1.0f);
  161. width = data->m_innerBeamWidth + scale * (data->m_outerBeamWidth - data->m_innerBeamWidth);
  162. alpha = innerAlpha + scale * (outerAlpha - innerAlpha);
  163. red = innerRed + scale * (outerRed - innerRed) * innerAlpha;
  164. green = innerGreen + scale * (outerGreen - innerGreen) * innerAlpha;
  165. blue = innerBlue + scale * (outerBlue - innerBlue) * innerAlpha;
  166. }
  167. m_line3D[ index ] = NEW SegmentedLineClass;
  168. SegmentedLineClass *line = m_line3D[ index ];
  169. if( line )
  170. {
  171. line->Set_Texture( m_texture );
  172. line->Set_Shader( ShaderClass::_PresetAdditiveShader ); //pick the alpha blending mode you want - see shader.h for others.
  173. line->Set_Width( width );
  174. line->Set_Color( Vector3( red, green, blue ) );
  175. line->Set_UV_Offset_Rate( Vector2(0.0f, data->m_scrollRate) ); //amount to scroll texture on each draw
  176. if( m_texture )
  177. {
  178. line->Set_Texture_Mapping_Mode(SegLineRendererClass::TILED_TEXTURE_MAP); //this tiles the texture across the line
  179. }
  180. // add to scene
  181. W3DDisplay::m_3DScene->Add_Render_Object( line ); //add it to our scene so it gets rendered with other objects.
  182. // hide the render object until the first time we come to draw it and
  183. // set the correct position
  184. line->Set_Visible( 0 );
  185. }
  186. } // end for i
  187. } //end segment loop
  188. }
  189. //-------------------------------------------------------------------------------------------------
  190. //-------------------------------------------------------------------------------------------------
  191. W3DLaserDraw::~W3DLaserDraw( void )
  192. {
  193. const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
  194. for( int i = 0; i < data->m_numBeams * data->m_segments; i++ )
  195. {
  196. // remove line from scene
  197. W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] );
  198. // delete line
  199. REF_PTR_RELEASE( m_line3D[ i ] );
  200. } // end for i
  201. delete [] m_line3D;
  202. }
  203. //-------------------------------------------------------------------------------------------------
  204. //-------------------------------------------------------------------------------------------------
  205. Real W3DLaserDraw::getLaserTemplateWidth() const
  206. {
  207. const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
  208. return data->m_outerBeamWidth * 0.5f;
  209. }
  210. //-------------------------------------------------------------------------------------------------
  211. //-------------------------------------------------------------------------------------------------
  212. void W3DLaserDraw::doDrawModule(const Matrix3D* transformMtx)
  213. {
  214. //UnsignedInt currentFrame = TheGameClient->getFrame();
  215. const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
  216. //Get the updatemodule that drives it...
  217. Drawable *draw = getDrawable();
  218. static NameKeyType key_LaserUpdate = NAMEKEY( "LaserUpdate" );
  219. LaserUpdate *update = (LaserUpdate*)draw->findClientUpdateModule( key_LaserUpdate );
  220. if( !update )
  221. {
  222. DEBUG_ASSERTCRASH( 0, ("W3DLaserDraw::doDrawModule() expects its owner drawable %s to have a ClientUpdate = LaserUpdate module.", draw->getTemplate()->getName().str() ));
  223. return;
  224. }
  225. //If the update has moved the laser, it requires a reset of the laser.
  226. if (update->isDirty() || m_selfDirty)
  227. {
  228. update->setDirty(false);
  229. m_selfDirty = false;
  230. Vector3 laserPoints[ 2 ];
  231. for( int segment = 0; segment < data->m_segments; segment++ )
  232. {
  233. if( data->m_arcHeight > 0.0f && data->m_segments > 1 )
  234. {
  235. //CALCULATE A CURVED LINE BASED ON TOTAL LENGTH AND DESIRED HEIGHT INCREASE
  236. //To do this we will use a portion of the cos wave ranging between -0.25PI
  237. //and +0.25PI. 0PI is 1.0 and 0.25PI is 0.70 -- resulting in a somewhat
  238. //gentle curve depending on the line height and length. We also have to make
  239. //the line *level* for this phase of the calculations.
  240. //Get the desired direct line
  241. Coord3D lineStart, lineEnd, lineVector;
  242. lineStart.set( update->getStartPos() );
  243. lineEnd.set( update->getEndPos() );
  244. //This is critical -- in the case we have sloped lines (at the end, we'll fix it)
  245. // lineEnd.z = lineStart.z;
  246. //Get the length of the line
  247. lineVector.set( &lineEnd );
  248. lineVector.sub( &lineStart );
  249. Real lineLength = lineVector.length();
  250. //Get the middle point (we'll use this to determine how far we are from
  251. //that to calculate our height -- middle point is the highest).
  252. Coord3D lineMiddle;
  253. lineMiddle.set( &lineStart );
  254. lineMiddle.add( &lineEnd );
  255. lineMiddle.scale( 0.5 );
  256. //The half length is used to scale with the distance from middle to
  257. //get our cos( 0 to 0.25 PI) cos value
  258. Real halfLength = lineLength * 0.5f;
  259. //Now calculate which segment we will use.
  260. Real startSegmentRatio = segment / ((Real)data->m_segments);
  261. Real endSegmentRatio = (segment + 1.0f) / ((Real)data->m_segments);
  262. //Offset the segment ever-so-slightly to minimize overlap -- only apply
  263. //to segments that are not the start/end point
  264. if( segment > 0 )
  265. {
  266. startSegmentRatio -= data->m_segmentOverlapRatio;
  267. }
  268. if( segment < data->m_segments - 1 )
  269. {
  270. endSegmentRatio += data->m_segmentOverlapRatio;
  271. }
  272. //Calculate our start segment position on the *ground*.
  273. Coord3D segmentStart, segmentEnd, vector;
  274. vector.set( &lineVector );
  275. vector.scale( startSegmentRatio );
  276. segmentStart.set( &lineStart );
  277. segmentStart.add( &vector );
  278. //Calculate our end segment position on the *ground*.
  279. vector.set( &lineVector );
  280. vector.scale( endSegmentRatio );
  281. segmentEnd.set( &lineStart );
  282. segmentEnd.add( &vector );
  283. //--------------------------------------------------------------------------------
  284. //Now at this point, we have our segment line in the level positions that we want.
  285. //Calculate the raised height for the start/end segment positions using cosine.
  286. //--------------------------------------------------------------------------------
  287. //Calculate the distance from midpoint for the start positions.
  288. vector.set( &lineMiddle );
  289. vector.sub( &segmentStart );
  290. Real dist = vector.length();
  291. Real scaledRadians = dist / halfLength * PI * 0.5f;
  292. Real height = cos( scaledRadians );
  293. height *= data->m_arcHeight;
  294. segmentStart.z += height;
  295. //Now do the same thing for the end position.
  296. vector.set( &lineMiddle );
  297. vector.sub( &segmentEnd );
  298. dist = vector.length();
  299. scaledRadians = dist / halfLength * PI * 0.5f;
  300. height = cos( scaledRadians );
  301. height *= data->m_arcHeight;
  302. segmentEnd.z += height;
  303. //This makes the laser skim the ground rather than penetrate it!
  304. laserPoints[ 0 ].Set( segmentStart.x, segmentStart.y,
  305. MAX( segmentStart.z, 2.0f + TheTerrainLogic->getGroundHeight(segmentStart.x, segmentStart.y) ) );
  306. laserPoints[ 1 ].Set( segmentEnd.x, segmentEnd.y,
  307. MAX( segmentEnd.z, 2.0f + TheTerrainLogic->getGroundHeight(segmentEnd.x, segmentEnd.y) ) );
  308. }
  309. else
  310. {
  311. //No arc -- way simpler!
  312. laserPoints[ 0 ].Set( update->getStartPos()->x, update->getStartPos()->y, update->getStartPos()->z );
  313. laserPoints[ 1 ].Set( update->getEndPos()->x, update->getEndPos()->y, update->getEndPos()->z );
  314. }
  315. //Get the color components for calculation purposes.
  316. Real innerRed, innerGreen, innerBlue, innerAlpha, outerRed, outerGreen, outerBlue, outerAlpha;
  317. GameGetColorComponentsReal( data->m_innerColor, &innerRed, &innerGreen, &innerBlue, &innerAlpha );
  318. GameGetColorComponentsReal( data->m_outerColor, &outerRed, &outerGreen, &outerBlue, &outerAlpha );
  319. for( Int i = data->m_numBeams - 1; i >= 0; i-- )
  320. {
  321. Real alpha, width;
  322. int index = segment * data->m_numBeams + i;
  323. if( data->m_numBeams == 1 )
  324. {
  325. width = data->m_innerBeamWidth * update->getWidthScale();
  326. alpha = innerAlpha;
  327. }
  328. else
  329. {
  330. //Calculate the scale between min and max values
  331. //0 means use min value, 1 means use max value
  332. //0.2 means min value + 20% of the diff between min and max
  333. Real scale = i / ( data->m_numBeams - 1.0f);
  334. Real ultimateScale = update->getWidthScale();
  335. width = (data->m_innerBeamWidth + scale * (data->m_outerBeamWidth - data->m_innerBeamWidth));
  336. width *= ultimateScale;
  337. alpha = innerAlpha + scale * (outerAlpha - innerAlpha);
  338. }
  339. //Calculate the number of times to tile the line based on the height of the texture used.
  340. if( m_texture && data->m_tile )
  341. {
  342. //Calculate the length of the line.
  343. Vector3 lineVector;
  344. Vector3::Subtract( laserPoints[1], laserPoints[0], &lineVector );
  345. Real length = lineVector.Length();
  346. //Adjust tile factor so texture is NOT stretched but tiled equally in both width and length.
  347. Real tileFactor = length/width*m_textureAspectRatio*data->m_tilingScalar;
  348. //Set the tile factor
  349. m_line3D[ index ]->Set_Texture_Tile_Factor( tileFactor ); //number of times to tile texture across each segment
  350. }
  351. m_line3D[ index ]->Set_Width( width );
  352. m_line3D[ index ]->Set_Points( 2, &laserPoints[0] );
  353. }
  354. }
  355. }
  356. return;
  357. }
  358. // ------------------------------------------------------------------------------------------------
  359. /** CRC */
  360. // ------------------------------------------------------------------------------------------------
  361. void W3DLaserDraw::crc( Xfer *xfer )
  362. {
  363. // extend base class
  364. DrawModule::crc( xfer );
  365. } // end crc
  366. // ------------------------------------------------------------------------------------------------
  367. /** Xfer method
  368. * Version Info:
  369. * 1: Initial version */
  370. // ------------------------------------------------------------------------------------------------
  371. void W3DLaserDraw::xfer( Xfer *xfer )
  372. {
  373. // version
  374. const XferVersion currentVersion = 1;
  375. XferVersion version = currentVersion;
  376. xfer->xferVersion( &version, currentVersion );
  377. // extend base class
  378. DrawModule::xfer( xfer );
  379. // Kris says there is no data to save for these, go ask him.
  380. // m_selfDirty is not saved, is runtime only
  381. } // end xfer
  382. // ------------------------------------------------------------------------------------------------
  383. /** Load post process */
  384. // ------------------------------------------------------------------------------------------------
  385. void W3DLaserDraw::loadPostProcess( void )
  386. {
  387. // extend base class
  388. DrawModule::loadPostProcess();
  389. m_selfDirty = true; // so we update the first time after reload
  390. } // end loadPostProcess