Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

683 lines
18 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Renders a cone for spotlight entities. Only renders when the parent
  4. // entity is selected.
  5. //
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "Box3D.h"
  9. #include "fgdlib/HelperInfo.h"
  10. #include "MapDefs.h" // dvs: For COORD_NOTINIT
  11. #include "MapEntity.h"
  12. #include "MapLightCone.h"
  13. #include "Render3D.h"
  14. #include "Material.h"
  15. #include "materialsystem/IMaterialSystem.h"
  16. #include "TextureSystem.h"
  17. #include "hammer.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include <tier0/memdbgon.h>
  20. #define NUM_LIGHTCONE_ZONES 5
  21. IMPLEMENT_MAPCLASS(CMapLightCone)
  22. //-----------------------------------------------------------------------------
  23. // Purpose: Factory function. Used for creating a CMapLightCone helper from a
  24. // set of string parameters from the FGD file.
  25. // Input : *pInfo - Pointer to helper info class which gives us information
  26. // about how to create the helper.
  27. // Output : Returns a pointer to the helper, NULL if an error occurs.
  28. //-----------------------------------------------------------------------------
  29. CMapClass *CMapLightCone::Create(CHelperInfo *pHelperInfo, CMapEntity *pParent)
  30. {
  31. CMapLightCone *new1=new CMapLightCone;
  32. if( new1 != NULL )
  33. {
  34. //
  35. // The first parameter should be the inner fov key name. If it isn't
  36. // there we assume "_inner_cone".
  37. //
  38. const char *pszKeyName = pHelperInfo->GetParameter(0);
  39. if (pszKeyName != NULL)
  40. {
  41. strcpy(new1->m_szInnerConeKeyName, pszKeyName);
  42. }
  43. else
  44. {
  45. strcpy(new1->m_szInnerConeKeyName, "_inner_cone");
  46. }
  47. //
  48. // The second parameter should be the outer fov key name. If it isn't
  49. // there we assume "_cone".
  50. //
  51. pszKeyName = pHelperInfo->GetParameter(1);
  52. if (pszKeyName != NULL)
  53. {
  54. strcpy(new1->m_szOuterConeKeyName, pszKeyName);
  55. }
  56. else
  57. {
  58. strcpy(new1->m_szOuterConeKeyName, "_cone");
  59. }
  60. //
  61. // The third parameter should be the color of the light. If it isn't
  62. // there we assume "_light".
  63. //
  64. pszKeyName = pHelperInfo->GetParameter(2);
  65. if (pszKeyName != NULL)
  66. {
  67. strcpy(new1->m_szColorKeyName, pszKeyName);
  68. }
  69. else
  70. {
  71. strcpy(new1->m_szColorKeyName, "_light");
  72. }
  73. pszKeyName = pHelperInfo->GetParameter(3);
  74. if (pszKeyName != NULL)
  75. {
  76. new1->m_flPitchScale = Q_atof( pszKeyName );
  77. }
  78. else
  79. {
  80. new1->m_flPitchScale = 1.0f;
  81. }
  82. }
  83. return new1;
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose:
  87. //-----------------------------------------------------------------------------
  88. CMapLightCone::CMapLightCone(void)
  89. {
  90. m_fQuadraticAttn = 1;
  91. m_fLinearAttn = 0;
  92. m_fConstantAttn = 0;
  93. m_bPitchSet = false;
  94. m_fPitch = 0;
  95. m_fFocus = 1;
  96. m_flPitchScale = 1;
  97. m_fBrightness = 100;
  98. m_fInnerConeAngle = 0;
  99. m_fOuterConeAngle = 45;
  100. m_fFiftyPercentDistance = -1; // disabled - use attenuation
  101. m_Angles.Init();
  102. SignalUpdate( EVTYPE_LIGHTING_CHANGED );
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose: Destructor. Deletes faces allocated by BuildCone.
  106. //-----------------------------------------------------------------------------
  107. CMapLightCone::~CMapLightCone(void)
  108. {
  109. for (int i = 0; i < m_Faces.Count(); i++)
  110. {
  111. CMapFace *pFace = m_Faces.Element(i);
  112. delete pFace;
  113. }
  114. SignalUpdate( EVTYPE_LIGHTING_CHANGED );
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Builds the light cone faces in local space. Does NOT call CalcBounds,
  118. // because that CalcBounds updates the parent, which causes problems
  119. // in the undo system.
  120. //-----------------------------------------------------------------------------
  121. void CMapLightCone::BuildCone(void)
  122. {
  123. //
  124. // Delete the current face list.
  125. //
  126. for (int i = 0; i < m_Faces.Count(); i++)
  127. {
  128. CMapFace *pFace = m_Faces.Element(i);
  129. delete pFace;
  130. }
  131. m_Faces.RemoveAll();
  132. //
  133. // Make sure at least one of the lighting coefficients is nonzero.
  134. //
  135. if ((m_fQuadraticAttn == 0) && (m_fLinearAttn == 0) && (m_fConstantAttn == 0))
  136. {
  137. m_fConstantAttn = 1;
  138. }
  139. //
  140. // Solve for the lighting scale factor by which the brightness will be multiplied.
  141. //
  142. float fScaleFactor = m_fQuadraticAttn * 10000 + m_fLinearAttn * 100 + m_fConstantAttn;
  143. if (fScaleFactor == 0)
  144. {
  145. return;
  146. }
  147. //
  148. // Calculate the distances from the light origin to the various zones.
  149. //
  150. float fOffsetDist = 0;
  151. // Constant attenuation factor doesn't actually offset the cone yet. If it does, uncomment this:
  152. //SolveQuadratic(fOffsetDist, 0, m_fQuadraticAttn, m_fLinearAttn, -m_fConstantAttn);
  153. float fZoneDist[NUM_LIGHTCONE_ZONES];
  154. memset( fZoneDist, 0, sizeof( fZoneDist ) );
  155. fZoneDist[0] = 0;
  156. SolveQuadratic(fZoneDist[1], 0.25 * fScaleFactor, m_fQuadraticAttn, m_fLinearAttn, m_fConstantAttn);
  157. SolveQuadratic(fZoneDist[2], fScaleFactor, m_fQuadraticAttn, m_fLinearAttn, m_fConstantAttn);
  158. SolveQuadratic(fZoneDist[3], 4 * fScaleFactor, m_fQuadraticAttn, m_fLinearAttn, m_fConstantAttn);
  159. SolveQuadratic(fZoneDist[4], 10 * fScaleFactor, m_fQuadraticAttn, m_fLinearAttn, m_fConstantAttn);
  160. //
  161. // there's no cone if it's greater then 90 degrees
  162. //
  163. if (m_fOuterConeAngle < 90)
  164. {
  165. //
  166. // Calculate the cone radius at each zone.
  167. //
  168. float fZoneRadius[NUM_LIGHTCONE_ZONES];
  169. for (int i = 0; i < NUM_LIGHTCONE_ZONES; i++)
  170. {
  171. fZoneRadius[i] = (fOffsetDist + fZoneDist[i]) * tan(DEG2RAD(m_fOuterConeAngle));
  172. }
  173. //
  174. // Build the new face list using the new parameters.
  175. //
  176. float fStepSize = 360.0 / 15.0;
  177. for (int nZone = 0; nZone < NUM_LIGHTCONE_ZONES - 1; nZone++)
  178. {
  179. float fSin0 = 0;
  180. float fCos0 = 1;
  181. float fTopDist = fZoneDist[nZone];
  182. float fBottomDist = fZoneDist[nZone + 1];
  183. float fTopRadius = fZoneRadius[nZone];
  184. float fBottomRadius = fZoneRadius[nZone + 1];
  185. for (int fAngle = fStepSize; fAngle <= 361; fAngle += fStepSize)
  186. {
  187. float fSin1 = sin(DEG2RAD(fAngle));
  188. float fCos1 = cos(DEG2RAD(fAngle));
  189. Vector Points[4];
  190. Points[0][2] = fBottomRadius * fCos1;
  191. Points[0][1] = fBottomRadius * fSin1;
  192. Points[0][0] = fBottomDist;
  193. Points[1][2] = fBottomRadius * fCos0;
  194. Points[1][1] = fBottomRadius * fSin0;
  195. Points[1][0] = fBottomDist;
  196. Points[2][2] = fTopRadius * fCos0;
  197. Points[2][1] = fTopRadius * fSin0;
  198. Points[2][0] = fTopDist;
  199. int nPoints = 3;
  200. if (fTopRadius != 0)
  201. {
  202. Points[3][2] = fTopRadius * fCos1;
  203. Points[3][1] = fTopRadius * fSin1;
  204. Points[3][0] = fTopDist;
  205. nPoints = 4;
  206. }
  207. CMapFace *pFace = new CMapFace;
  208. pFace->SetRenderColor(r * (1 - nZone / (float)NUM_LIGHTCONE_ZONES), g * (1 - nZone / (float)NUM_LIGHTCONE_ZONES), b * (1 - nZone / (float)NUM_LIGHTCONE_ZONES));
  209. pFace->SetRenderAlpha(180);
  210. pFace->CreateFace(Points, nPoints);
  211. pFace->RenderUnlit(true);
  212. m_Faces.AddToTail(pFace);
  213. fSin0 = fSin1;
  214. fCos0 = fCos1;
  215. }
  216. }
  217. }
  218. //
  219. // Lobe's aren't defined for > 90
  220. //
  221. if (m_fOuterConeAngle > 90)
  222. return;
  223. //
  224. // Build the a face list that shows light-angle falloff
  225. //
  226. float fStepSize = 360.0 / 15.0;
  227. float fPitchStepSize = 90.0 / 15.0;
  228. float fFocusRadius0 = 0;
  229. float fFocusDist0 = fZoneDist[1];
  230. float fInnerDot = cos(DEG2RAD(m_fInnerConeAngle));
  231. float fOuterDot = cos(DEG2RAD(m_fOuterConeAngle));
  232. for (float fPitch = fPitchStepSize; fPitch < m_fOuterConeAngle + fPitchStepSize; fPitch += fPitchStepSize)
  233. {
  234. float fSin0 = 0;
  235. float fCos0 = 1;
  236. // clamp to edge of cone
  237. if (fPitch > m_fOuterConeAngle)
  238. fPitch = m_fOuterConeAngle;
  239. float fIllumination = 0;
  240. if (fPitch <= m_fInnerConeAngle)
  241. {
  242. fIllumination = 1.0;
  243. }
  244. else
  245. {
  246. float fPitchDot = cos(DEG2RAD(fPitch));
  247. fIllumination = (fPitchDot - fOuterDot) / (fInnerDot - fOuterDot);
  248. if ((m_fFocus != 1) && (m_fFocus != 0))
  249. {
  250. fIllumination = pow( fIllumination, m_fFocus );
  251. }
  252. }
  253. // cosine falloff ^ exponent
  254. // draw as lobe
  255. float fFocusDist1 = cos(DEG2RAD(fPitch)) * fIllumination * fZoneDist[1];
  256. float fFocusRadius1 = sin(DEG2RAD(fPitch)) * fIllumination * fZoneDist[1];
  257. // draw as disk
  258. // float fFocusDist1 = fZoneDist[1];
  259. // float fFocusRadius1 = sin(DEG2RAD(fPitch)) * fZoneRadius[1] / sin(DEG_RAD * m_fConeAngle);
  260. for (int fAngle = fStepSize; fAngle <= 361; fAngle += fStepSize)
  261. {
  262. float fSin1 = sin(DEG2RAD(fAngle));
  263. float fCos1 = cos(DEG2RAD(fAngle));
  264. Vector Points[4];
  265. Points[0][2] = fFocusRadius1 * fCos0;
  266. Points[0][1] = fFocusRadius1 * fSin0;
  267. Points[0][0] = fFocusDist1;
  268. Points[1][2] = fFocusRadius1 * fCos1;
  269. Points[1][1] = fFocusRadius1 * fSin1;
  270. Points[1][0] = fFocusDist1;
  271. Points[2][2] = fFocusRadius0 * fCos1;
  272. Points[2][1] = fFocusRadius0 * fSin1;
  273. Points[2][0] = fFocusDist0;
  274. int nPoints = 3;
  275. if (fFocusRadius0 != 0)
  276. {
  277. Points[3][2] = fFocusRadius0 * fCos0;
  278. Points[3][1] = fFocusRadius0 * fSin0;
  279. Points[3][0] = fFocusDist0;
  280. nPoints = 4;
  281. }
  282. CMapFace *pFace = new CMapFace;
  283. pFace->SetRenderColor(r * fIllumination, g * fIllumination, b * fIllumination);
  284. pFace->SetRenderAlpha(180);
  285. pFace->CreateFace(Points, nPoints);
  286. pFace->RenderUnlit(true);
  287. m_Faces.AddToTail(pFace);
  288. fSin0 = fSin1;
  289. fCos0 = fCos1;
  290. }
  291. fFocusRadius0 = fFocusRadius1;
  292. fFocusDist0 = fFocusDist1;
  293. }
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose:
  297. // Input : bFullUpdate -
  298. //-----------------------------------------------------------------------------
  299. void CMapLightCone::CalcBounds(BOOL bFullUpdate)
  300. {
  301. CMapClass::CalcBounds(bFullUpdate);
  302. //
  303. // HACK: Update our origin to stick to our parent.
  304. //
  305. if (m_pParent != NULL)
  306. {
  307. GetParent()->GetOrigin(m_Origin);
  308. }
  309. //
  310. // Pretend to be very small for the 2D view. Won't be necessary when 2D
  311. // rendering is done in the map classes.
  312. //
  313. m_Render2DBox.ResetBounds();
  314. m_Render2DBox.UpdateBounds(m_Origin);
  315. SetCullBoxFromFaceList( &m_Faces );
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Purpose:
  319. // Input : Angles -
  320. //-----------------------------------------------------------------------------
  321. void CMapLightCone::GetAngles(QAngle &Angles)
  322. {
  323. Angles = m_Angles;
  324. if (m_bPitchSet)
  325. {
  326. Angles[PITCH] = m_fPitch;
  327. }
  328. }
  329. //-----------------------------------------------------------------------------
  330. // Purpose:
  331. // Output : CMapClass
  332. //-----------------------------------------------------------------------------
  333. CMapClass *CMapLightCone::Copy(bool bUpdateDependencies)
  334. {
  335. CMapLightCone *pCopy = new CMapLightCone;
  336. if (pCopy != NULL)
  337. {
  338. pCopy->CopyFrom(this, bUpdateDependencies);
  339. }
  340. return(pCopy);
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose:
  344. // Input : pObject -
  345. // Output : CMapClass
  346. //-----------------------------------------------------------------------------
  347. CMapClass *CMapLightCone::CopyFrom(CMapClass *pObject, bool bUpdateDependencies)
  348. {
  349. Assert(pObject->IsMapClass(MAPCLASS_TYPE(CMapLightCone)));
  350. CMapLightCone *pFrom = (CMapLightCone *)pObject;
  351. CMapClass::CopyFrom(pObject, bUpdateDependencies);
  352. m_fBrightness = pFrom->m_fBrightness;
  353. m_fQuadraticAttn = pFrom->m_fQuadraticAttn;
  354. m_fLinearAttn = pFrom->m_fLinearAttn;
  355. m_fConstantAttn = pFrom->m_fConstantAttn;
  356. m_flPitchScale = pFrom->m_flPitchScale;
  357. m_fInnerConeAngle = pFrom->m_fInnerConeAngle;
  358. m_fOuterConeAngle = pFrom->m_fOuterConeAngle;
  359. m_Angles = pFrom->m_Angles;
  360. m_bPitchSet = pFrom->m_bPitchSet;
  361. m_fPitch = pFrom->m_fPitch;
  362. m_fFocus = pFrom->m_fFocus;
  363. m_fFiftyPercentDistance = pFrom->m_fFiftyPercentDistance;
  364. m_fZeroPercentDistance = pFrom->m_fZeroPercentDistance;
  365. m_LightColor = pFrom->m_LightColor;
  366. Q_strncpy( m_szColorKeyName, pFrom->m_szColorKeyName, sizeof( m_szColorKeyName ) );
  367. Q_strncpy( m_szInnerConeKeyName, pFrom->m_szInnerConeKeyName, sizeof( m_szInnerConeKeyName ) );
  368. Q_strncpy( m_szOuterConeKeyName, pFrom->m_szOuterConeKeyName, sizeof( m_szOuterConeKeyName ) );
  369. BuildCone();
  370. SignalUpdate( EVTYPE_LIGHTING_CHANGED );
  371. return(this);
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Purpose: Notifies that this object's parent entity has had a key value change.
  375. // Input : szKey - The key that changed.
  376. // szValue - The new value of the key.
  377. //-----------------------------------------------------------------------------
  378. void CMapLightCone::OnParentKeyChanged(const char *szKey, const char *szValue)
  379. {
  380. bool bRebuild = true;
  381. if (!stricmp(szKey, "angles"))
  382. {
  383. sscanf(szValue, "%f %f %f", &m_Angles[PITCH], &m_Angles[YAW], &m_Angles[ROLL]);
  384. }
  385. else if (!stricmp(szKey, m_szColorKeyName))
  386. {
  387. int nRed;
  388. int nGreen;
  389. int nBlue;
  390. int nBrightness;
  391. sscanf(szValue, "%d %d %d %d", &nRed, &nGreen, &nBlue, &nBrightness);
  392. r = m_LightColor.x = nRed;
  393. g = m_LightColor.y = nGreen;
  394. b = m_LightColor.z = nBlue;
  395. m_fBrightness = nBrightness;
  396. }
  397. else if (!stricmp(szKey, "pitch"))
  398. {
  399. // Pitch
  400. m_bPitchSet = true;
  401. m_fPitch = atof(szValue);
  402. }
  403. else if (!stricmp(szKey, "_constant_attn"))
  404. {
  405. // Constant attenuation
  406. m_fConstantAttn = atof(szValue);
  407. }
  408. else if (!stricmp(szKey, "_linear_attn"))
  409. {
  410. // Linear attenuation
  411. m_fLinearAttn = atof(szValue);
  412. }
  413. else if (!stricmp(szKey, "_quadratic_attn"))
  414. {
  415. // Quadratic attenuation
  416. m_fQuadraticAttn = atof(szValue);
  417. }
  418. else if (!stricmp(szKey, "_exponent"))
  419. {
  420. // Focus
  421. m_fFocus = atof(szValue);
  422. }
  423. else if (!stricmp(szKey, "_fifty_percent_distance"))
  424. {
  425. // Focus
  426. m_fFiftyPercentDistance = atof(szValue);
  427. }
  428. else if (!stricmp(szKey, "_zero_percent_distance"))
  429. {
  430. // Focus
  431. m_fZeroPercentDistance = atof(szValue);
  432. }
  433. else if (!stricmp(szKey, m_szInnerConeKeyName) || !stricmp(szKey, m_szOuterConeKeyName))
  434. {
  435. // check both of these together since they might be the same key.
  436. if( !stricmp(szKey, m_szInnerConeKeyName ))
  437. {
  438. // Inner Cone angle
  439. m_fInnerConeAngle = atof(szValue);
  440. }
  441. if( !stricmp(szKey, m_szOuterConeKeyName ))
  442. {
  443. // Outer Cone angle
  444. m_fOuterConeAngle = atof(szValue);
  445. }
  446. }
  447. else
  448. {
  449. bRebuild = false;
  450. }
  451. if (bRebuild)
  452. {
  453. SignalUpdate( EVTYPE_LIGHTING_CHANGED );
  454. BuildCone();
  455. PostUpdate(Notify_Changed);
  456. }
  457. }
  458. //-----------------------------------------------------------------------------
  459. // Purpose: Called after the entire map has been loaded. This allows the object
  460. // to perform any linking with other map objects or to do other operations
  461. // that require all world objects to be present.
  462. // Input : pWorld - The world that we are in.
  463. //-----------------------------------------------------------------------------
  464. void CMapLightCone::PostloadWorld(CMapWorld *pWorld)
  465. {
  466. CMapClass::PostloadWorld(pWorld);
  467. BuildCone();
  468. SignalUpdate( EVTYPE_LIGHTING_CHANGED );
  469. CalcBounds();
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Purpose:
  473. // Input : pRender -
  474. //-----------------------------------------------------------------------------
  475. void CMapLightCone::Render3D(CRender3D *pRender)
  476. {
  477. if (m_pParent->IsSelected())
  478. {
  479. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  480. pRenderContext->MatrixMode(MATERIAL_MODEL);
  481. pRenderContext->PushMatrix();
  482. pRenderContext->Translate(m_Origin[0], m_Origin[1], m_Origin[2]);
  483. QAngle Angles;
  484. GetAngles(Angles);
  485. pRenderContext->Rotate(Angles[YAW], 0, 0, 1);
  486. pRenderContext->Rotate(m_flPitchScale * Angles[PITCH], 0, -1, 0);
  487. pRenderContext->Rotate(Angles[ROLL], 1, 0, 0);
  488. if (
  489. (pRender->GetCurrentRenderMode() != RENDER_MODE_LIGHT_PREVIEW2) &&
  490. (pRender->GetCurrentRenderMode() != RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) &&
  491. (GetSelectionState() != SELECT_MODIFY )
  492. )
  493. {
  494. // Render the cone faces flatshaded.
  495. pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT );
  496. for (int i = 0; i < m_Faces.Count(); i++)
  497. {
  498. CMapFace *pFace = m_Faces.Element(i);
  499. pFace->Render3D(pRender);
  500. }
  501. pRender->PopRenderMode();
  502. }
  503. //
  504. // Render the cone faces in yellow wireframe (on top)
  505. //
  506. pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
  507. for (int i = 0; i < m_Faces.Count(); i++)
  508. {
  509. CMapFace *pFace = m_Faces.Element(i);
  510. pFace->Render3D(pRender);
  511. }
  512. //
  513. // Restore the default rendering mode.
  514. //
  515. pRender->PopRenderMode();
  516. pRenderContext->PopMatrix();
  517. }
  518. }
  519. //-----------------------------------------------------------------------------
  520. // Purpose:
  521. // Input : File -
  522. // bRMF -
  523. // Output : int
  524. //-----------------------------------------------------------------------------
  525. int CMapLightCone::SerializeRMF(std::fstream &File, BOOL bRMF)
  526. {
  527. return(0);
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose:
  531. // Input : File -
  532. // bRMF -
  533. // Output : int
  534. //-----------------------------------------------------------------------------
  535. int CMapLightCone::SerializeMAP(std::fstream &File, BOOL bRMF)
  536. {
  537. return(0);
  538. }
  539. //-----------------------------------------------------------------------------
  540. // Purpose: Solves a quadratic equation with the given coefficients.
  541. // Input : x - Receives solution.
  542. // y - Root to solve for.
  543. // A, B, C - Quadratic, linear, and constant coefficients.
  544. // Output : Returns true if a real solution was found, false if not.
  545. //-----------------------------------------------------------------------------
  546. bool CMapLightCone::SolveQuadratic(float &x, float y, float A, float B, float C)
  547. {
  548. C -= y;
  549. if (A == 0)
  550. {
  551. if (B != 0)
  552. {
  553. x = -C / B;
  554. return(true);
  555. }
  556. }
  557. else
  558. {
  559. float fDeterminant = B * B - 4 * A * C;
  560. if (fDeterminant > 0)
  561. {
  562. x = (-B + sqrt(fDeterminant)) / (2 * A);
  563. return(true);
  564. }
  565. }
  566. return(false);
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose: Never select anything because of this helper.
  570. //-----------------------------------------------------------------------------
  571. CMapClass *CMapLightCone::PrepareSelection(SelectMode_t eSelectMode)
  572. {
  573. return NULL;
  574. }