Team Fortress 2 Source Code as on 22/4/2020
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.

685 lines
18 KiB

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