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.

3471 lines
92 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ====
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include "collisionutils.h"
  8. #include "mainfrm.h"
  9. #include "MapDefs.h"
  10. #include "MapFace.h"
  11. #include "MapDisp.h"
  12. #include "MapWorld.h"
  13. #include "fgdlib/WCKeyValues.h"
  14. #include "GlobalFunctions.h"
  15. #include "Render3D.h"
  16. #include "Render2D.h"
  17. #include "SaveInfo.h"
  18. #include "TextureSystem.h"
  19. #include "MapDoc.h"
  20. #include "materialsystem/IMesh.h"
  21. #include "Material.h"
  22. #include "UtlRBTree.h"
  23. #include "mathlib/vector.h"
  24. #include "camera.h"
  25. #include "options.h"
  26. #include "hammer.h"
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include <tier0/memdbgon.h>
  29. //#define DEBUGPTS
  30. #define TEXTURE_AXIS_LENGTH 10 // Rendered texture axis length in world units.
  31. //
  32. // Components of the texture axes are rounded to integers within this tolerance. This tolerance corresponds
  33. // to an angle of about 0.06 degrees.
  34. //
  35. #define TEXTURE_AXIS_ROUND_EPSILON 0.001
  36. //
  37. // For passing into LoadKeyCallback. Collects key value data while loading.
  38. //
  39. struct LoadFace_t
  40. {
  41. CMapFace *pFace;
  42. char szTexName[MAX_PATH];
  43. };
  44. BOOL CheckFace(Vector *Points, int nPoints, Vector *normal, float dist, CCheckFaceInfo *pInfo);
  45. LPCTSTR GetDefaultTextureName();
  46. #pragma warning(disable:4244)
  47. //
  48. // Static member data initialization.
  49. //
  50. bool CMapFace::m_bShowFaceSelection = true;
  51. IEditorTexture *CMapFace::m_pLightmapGrid = NULL;
  52. //-----------------------------------------------------------------------------
  53. // Purpose: Constructor. Initializes data members and sets the texture to the
  54. // default texture.
  55. //-----------------------------------------------------------------------------
  56. CMapFace::CMapFace(void)
  57. {
  58. memset(&texture, 0, sizeof(texture));
  59. memset(&plane, 0, sizeof(plane));
  60. m_pTexture = NULL;
  61. m_pTangentAxes = NULL;
  62. m_DispHandle = EDITDISPHANDLE_INVALID;
  63. Points = NULL;
  64. nPoints = 0;
  65. m_nFaceID = 0;
  66. m_pTextureCoords = NULL;
  67. m_pLightmapCoords = NULL;
  68. m_uchAlpha = 255;
  69. m_pDetailObjects = NULL;
  70. texture.nLightmapScale = g_pGameConfig->GetDefaultLightmapScale();
  71. texture.scale[0] = g_pGameConfig->GetDefaultTextureScale();
  72. texture.scale[1] = g_pGameConfig->GetDefaultTextureScale();
  73. SetTexture(GetNullTextureName());
  74. if (m_pLightmapGrid == NULL)
  75. {
  76. m_pLightmapGrid = g_Textures.FindActiveTexture("Debug/debugluxelsnoalpha");
  77. }
  78. m_bIgnoreLighting = false;
  79. m_fSmoothingGroups = SMOOTHING_GROUP_DEFAULT;
  80. UpdateFaceFlags();
  81. SignalUpdate( EVTYPE_FACE_CHANGED );
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Destructor. Frees points and texture coordinates.
  85. //-----------------------------------------------------------------------------
  86. CMapFace::~CMapFace(void)
  87. {
  88. SignalUpdate( EVTYPE_FACE_CHANGED );
  89. delete [] Points;
  90. Points = NULL;
  91. delete [] m_pTextureCoords;
  92. m_pTextureCoords = NULL;
  93. delete [] m_pLightmapCoords;
  94. m_pLightmapCoords = NULL;
  95. delete m_pDetailObjects;
  96. m_pDetailObjects = NULL;
  97. FreeTangentSpaceAxes();
  98. if( HasDisp() )
  99. {
  100. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
  101. if( pDispMgr )
  102. {
  103. pDispMgr->RemoveFromWorld( GetDisp() );
  104. }
  105. // destroy handle
  106. SetDisp( EDITDISPHANDLE_INVALID );
  107. }
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Purpose: Attempts to fix this face. This is called by the check for problems
  111. // code when a face is reported as invalid.
  112. // Output : Returns TRUE on success, FALSE on failure.
  113. //-----------------------------------------------------------------------------
  114. BOOL CMapFace::Fix(void)
  115. {
  116. CalcPlane();
  117. CalcTextureCoords();
  118. // Create any detail objects if appropriate
  119. DetailObjects::BuildAnyDetailObjects(this);
  120. return(CheckFace());
  121. }
  122. //-----------------------------------------------------------------------------
  123. // Purpose: Returns the short texture name in 'pszName'. Places an empty string
  124. // in 'pszName' if the face has no texture.
  125. //-----------------------------------------------------------------------------
  126. void CMapFace::GetTextureName(char *pszName) const
  127. {
  128. Assert(pszName != NULL);
  129. if (pszName != NULL)
  130. {
  131. if (m_pTexture != NULL)
  132. {
  133. m_pTexture->GetShortName(pszName);
  134. }
  135. else
  136. {
  137. pszName[0] = '\0';
  138. }
  139. }
  140. }
  141. static char *InvisToolTextures[]={
  142. "playerclip",
  143. "occluder",
  144. "areaportal",
  145. "invisible",
  146. "skip",
  147. "trigger",
  148. "hint",
  149. "fog",
  150. "origin",
  151. "toolsnodraw",
  152. };
  153. void CMapFace::UpdateFaceFlags( void )
  154. {
  155. char tname[2048];
  156. GetTextureName( tname );
  157. m_nFaceFlags = 0;
  158. if (strstr(tname,"tools"))
  159. {
  160. if ( strstr( tname, "blocklight" ) )
  161. {
  162. m_nFaceFlags |= FACE_FLAGS_NODRAW_IN_LPREVIEW;
  163. }
  164. if ( strstr( tname, "skybox") )
  165. {
  166. m_nFaceFlags |= FACE_FLAGS_NOSHADOW;
  167. }
  168. for(int i=0;i<NELEMS(InvisToolTextures); i++)
  169. if (strstr( tname, InvisToolTextures[i] ) )
  170. {
  171. m_nFaceFlags |= FACE_FLAGS_NODRAW_IN_LPREVIEW | FACE_FLAGS_NOSHADOW;
  172. break;
  173. }
  174. }
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose: Populates this face with another face's information.
  178. // Input : pFrom - The face to copy.
  179. // Output : CMapFace
  180. //-----------------------------------------------------------------------------
  181. CMapFace *CMapFace::CopyFrom(const CMapFace *pObject, DWORD dwFlags, bool bUpdateDependencies)
  182. {
  183. SignalUpdate( EVTYPE_FACE_CHANGED );
  184. const CMapFace *pFrom = dynamic_cast<const CMapFace *>(pObject);
  185. Assert(pFrom != NULL);
  186. if (pFrom != NULL)
  187. {
  188. //
  189. // Free our points first.
  190. //
  191. if (Points != NULL)
  192. {
  193. delete [] Points;
  194. Points = NULL;
  195. }
  196. if (m_pTextureCoords != NULL)
  197. {
  198. delete [] m_pTextureCoords;
  199. m_pTextureCoords = NULL;
  200. }
  201. if (m_pLightmapCoords != NULL)
  202. {
  203. delete [] m_pLightmapCoords;
  204. m_pLightmapCoords = NULL;
  205. }
  206. FreeTangentSpaceAxes();
  207. nPoints = 0;
  208. //
  209. // Copy the member data.
  210. //
  211. m_nFaceID = pFrom->m_nFaceID;
  212. m_eSelectionState = pFrom->GetSelectionState();
  213. texture = pFrom->texture;
  214. m_pTexture = pFrom->m_pTexture;
  215. m_bIsCordonFace = pFrom->m_bIsCordonFace;
  216. //
  217. // Allocate points memory.
  218. //
  219. if (dwFlags & COPY_FACE_POINTS)
  220. {
  221. Points = NULL;
  222. nPoints = pFrom->nPoints;
  223. if (pFrom->Points && nPoints)
  224. {
  225. AllocatePoints(nPoints);
  226. AllocTangentSpaceAxes( nPoints );
  227. memcpy(Points, pFrom->Points, sizeof(Vector) * nPoints);
  228. memcpy(m_pTextureCoords, pFrom->m_pTextureCoords, sizeof(Vector2D) * nPoints);
  229. memcpy(m_pLightmapCoords, pFrom->m_pLightmapCoords, sizeof(Vector2D) * nPoints);
  230. memcpy(m_pTangentAxes, pFrom->m_pTangentAxes, sizeof(TangentSpaceAxes_t) * nPoints);
  231. }
  232. }
  233. else
  234. {
  235. Points = NULL;
  236. m_pTextureCoords = 0;
  237. m_pLightmapCoords = 0;
  238. m_pTangentAxes = 0;
  239. nPoints = 0;
  240. }
  241. //
  242. // Copy the plane. You shouldn't copy the points without copying the plane,
  243. // so we do it if either bit is set.
  244. //
  245. if ((dwFlags & COPY_FACE_POINTS) || (dwFlags & COPY_FACE_PLANE))
  246. {
  247. plane = pFrom->plane;
  248. }
  249. else
  250. {
  251. memset(&plane, 0, sizeof(plane));
  252. }
  253. //
  254. // copy the displacement info.
  255. //
  256. // If we do have displacement, then we'll never be asked to become a copy of
  257. // a face that does not have a displacement, because you cannot undo a Generate
  258. // Displacement operation.
  259. //
  260. if( pFrom->HasDisp() )
  261. {
  262. //
  263. // Allocate a new displacement info if we don't already have one.
  264. //
  265. if( !HasDisp() )
  266. {
  267. SetDisp( EditDispMgr()->Create() );
  268. }
  269. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  270. pDisp->SetParent( this );
  271. CMapDisp *pFromDisp = EditDispMgr()->GetDisp( pFrom->m_DispHandle );
  272. pDisp->CopyFrom( pFromDisp, bUpdateDependencies );
  273. }
  274. else
  275. {
  276. SetDisp( EDITDISPHANDLE_INVALID );
  277. }
  278. // Copy CMapAtom fields. dvs: this should be done in CMapAtom::CopyFrom!
  279. r = pFrom->r;
  280. g = pFrom->g;
  281. b = pFrom->b;
  282. m_uchAlpha = pFrom->m_uchAlpha;
  283. m_bIgnoreLighting = pFrom->m_bIgnoreLighting;
  284. // Copy the smoothing group data.
  285. m_fSmoothingGroups = pFrom->m_fSmoothingGroups;
  286. // Delete any existing and build any new detail objects
  287. delete m_pDetailObjects;
  288. m_pDetailObjects = NULL;
  289. DetailObjects::BuildAnyDetailObjects(this);
  290. }
  291. UpdateFaceFlags();
  292. return(this);
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Called any time this object is modified due to an Undo or Redo.
  296. //-----------------------------------------------------------------------------
  297. void CMapFace::OnUndoRedo()
  298. {
  299. // It's not valid to have selected faces outside of face edit mode.
  300. // If the user modified this face, then closed the texture application
  301. // dialog, then did an Undo, clear our selection state.
  302. if ( !GetMainWnd()->IsInFaceEditMode() )
  303. {
  304. m_eSelectionState = SELECT_NONE;
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose: Creates a face from a list of points.
  309. // Input : pPoints - An array of points.
  310. // _nPoints - Number of points. If nPoints < 0, reverse points.
  311. //-----------------------------------------------------------------------------
  312. void CMapFace::CreateFace(Vector *pPoints, int _nPoints, bool bIsCordonFace)
  313. {
  314. SignalUpdate( EVTYPE_FACE_CHANGED );
  315. if (_nPoints > 0)
  316. {
  317. AllocatePoints(_nPoints);
  318. Assert(nPoints > 0);
  319. if (nPoints > 0)
  320. {
  321. memcpy(Points, pPoints, nPoints * sizeof(Vector));
  322. }
  323. }
  324. else
  325. {
  326. AllocatePoints(-_nPoints);
  327. Assert(nPoints > 0);
  328. if (nPoints > 0)
  329. {
  330. int j = 0;
  331. for (int i = nPoints - 1; i >= 0; i--)
  332. {
  333. Points[j++] = pPoints[i];
  334. }
  335. }
  336. }
  337. SetCordonFace( bIsCordonFace );
  338. #ifdef DEBUGPTS
  339. DebugPoints();
  340. #endif
  341. CalcPlaneFromFacePoints();
  342. CalcTextureCoords();
  343. // Create any detail objects if appropriate
  344. DetailObjects::BuildAnyDetailObjects(this);
  345. #if 0
  346. //
  347. // create the displacement map -- if need be
  348. //
  349. if( m_pMapDisp )
  350. {
  351. m_pMapDisp->InitSurfData( this, false );
  352. m_pMapDisp->Create();
  353. }
  354. #endif
  355. }
  356. Vector FaceNormals[6] =
  357. {
  358. Vector(0, 0, 1), // floor
  359. Vector(0, 0, -1), // ceiling
  360. Vector(0, -1, 0), // north wall
  361. Vector(0, 1, 0), // south wall
  362. Vector(-1, 0, 0), // east wall
  363. Vector(1, 0, 0), // west wall
  364. };
  365. Vector DownVectors[6] =
  366. {
  367. Vector(0, -1, 0), // floor
  368. Vector(0, -1, 0), // ceiling
  369. Vector(0, 0, -1), // north wall
  370. Vector(0, 0, -1), // south wall
  371. Vector(0, 0, -1), // east wall
  372. Vector(0, 0, -1), // west wall
  373. };
  374. Vector RightVectors[6] =
  375. {
  376. Vector(1, 0, 0), // floor
  377. Vector(1, 0, 0), // ceiling
  378. Vector(1, 0, 0), // north wall
  379. Vector(1, 0, 0), // south wall
  380. Vector(0, 1, 0), // east wall
  381. Vector(0, 1, 0), // west wall
  382. };
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. // Input : index -
  386. // downVect -
  387. //-----------------------------------------------------------------------------
  388. void CMapFace::GetDownVector( int index, Vector& downVect )
  389. {
  390. downVect = DownVectors[index];
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Purpose:
  394. // Input : Center -
  395. //-----------------------------------------------------------------------------
  396. void CMapFace::GetCenter(Vector& Center)
  397. {
  398. Assert(nPoints > 0);
  399. Center.Init();
  400. if (nPoints != 0)
  401. {
  402. for (int i = 0; i < nPoints; i++)
  403. {
  404. Center[0] += Points[i][0];
  405. Center[1] += Points[i][1];
  406. Center[2] += Points[i][2];
  407. }
  408. Center[0] /= nPoints;
  409. Center[1] /= nPoints;
  410. Center[2] /= nPoints;
  411. }
  412. }
  413. //-----------------------------------------------------------------------------
  414. // Purpose: Determines the general orientation of a face based on its normal vector.
  415. // Output : FaceOrientation_t
  416. //-----------------------------------------------------------------------------
  417. FaceOrientation_t CMapFace::GetOrientation(void) const
  418. {
  419. // The normal must have a nonzero length!
  420. if ((plane.normal[0] == 0) && (plane.normal[1] == 0) && (plane.normal[2] == 0))
  421. {
  422. return(FACE_ORIENTATION_INVALID);
  423. }
  424. //
  425. // Find the axis that the surface normal has the greatest projection onto.
  426. //
  427. float fDot;
  428. float fMaxDot;
  429. Vector Normal;
  430. FaceOrientation_t eOrientation = FACE_ORIENTATION_INVALID;
  431. Normal = plane.normal;
  432. VectorNormalize(Normal);
  433. fMaxDot = 0;
  434. for (int i = 0; i < 6; i++)
  435. {
  436. fDot = DotProduct(Normal, FaceNormals[i]);
  437. if (fDot >= fMaxDot)
  438. {
  439. fMaxDot = fDot;
  440. eOrientation = (FaceOrientation_t)i;
  441. }
  442. }
  443. return(eOrientation);
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Purpose:
  447. // Input : eAlignment -
  448. // dwFlags -
  449. //-----------------------------------------------------------------------------
  450. void CMapFace::InitializeTextureAxes(TextureAlignment_t eAlignment, DWORD dwFlags)
  451. {
  452. FaceOrientation_t eOrientation;
  453. //
  454. // If the texture axis information has been initialized, don't reinitialize unless
  455. // the FORCE flag is set.
  456. //
  457. if ((!(dwFlags & INIT_TEXTURE_FORCE)) &&
  458. ((texture.UAxis[0] != 0) || (texture.UAxis[1] != 0) || (texture.UAxis[2] != 0) ||
  459. (texture.VAxis[0] != 0) || (texture.VAxis[1] != 0) || (texture.VAxis[2] != 0)))
  460. {
  461. return;
  462. }
  463. if (dwFlags & INIT_TEXTURE_ROTATION)
  464. {
  465. texture.rotate = 0;
  466. }
  467. if (dwFlags & INIT_TEXTURE_SHIFT)
  468. {
  469. texture.UAxis[3] = 0;
  470. texture.VAxis[3] = 0;
  471. }
  472. if (dwFlags & INIT_TEXTURE_SCALE)
  473. {
  474. texture.scale[0] = g_pGameConfig->GetDefaultTextureScale();
  475. texture.scale[1] = g_pGameConfig->GetDefaultTextureScale();
  476. }
  477. if (dwFlags & INIT_TEXTURE_AXES)
  478. {
  479. // don't reset the shift component [3]
  480. texture.UAxis.AsVector3D().Init();
  481. texture.VAxis.AsVector3D().Init();
  482. // Determine the general orientation of this face (floor, ceiling, n wall, etc.)
  483. eOrientation = GetOrientation();
  484. if (eOrientation == FACE_ORIENTATION_INVALID)
  485. {
  486. CalcTextureCoords();
  487. return;
  488. }
  489. // Pick a world axis aligned V axis based on the face orientation.
  490. texture.VAxis.AsVector3D() = DownVectors[eOrientation];
  491. //
  492. // If we are using face aligned textures, calculate the texture axes.
  493. //
  494. if (eAlignment == TEXTURE_ALIGN_FACE)
  495. {
  496. // Using that axis-aligned V axis, calculate the true U axis
  497. CrossProduct(plane.normal, texture.VAxis.AsVector3D(), texture.UAxis.AsVector3D());
  498. VectorNormalize(texture.UAxis.AsVector3D());
  499. // Now use the true U axis to calculate the true V axis.
  500. CrossProduct(texture.UAxis.AsVector3D(), plane.normal, texture.VAxis.AsVector3D());
  501. VectorNormalize(texture.VAxis.AsVector3D());
  502. }
  503. //
  504. // If we are using world (or "natural") aligned textures, use the V axis as is
  505. // and pick the corresponding U axis from the table.
  506. //
  507. else if (eAlignment == TEXTURE_ALIGN_WORLD)
  508. {
  509. texture.UAxis.AsVector3D() = RightVectors[eOrientation];
  510. }
  511. //
  512. // Quake-style texture alignment used a different axis convention.
  513. //
  514. else
  515. {
  516. InitializeQuakeStyleTextureAxes(texture.UAxis, texture.VAxis);
  517. }
  518. if (texture.rotate != 0)
  519. {
  520. RotateTextureAxes(texture.rotate);
  521. }
  522. }
  523. CalcTextureCoords();
  524. // Create any detail objects if appropriate
  525. DetailObjects::BuildAnyDetailObjects(this);
  526. }
  527. //-----------------------------------------------------------------------------
  528. // Purpose: Checks for a texture axis perpendicular to the face.
  529. // Output : Returns TRUE on success, FALSE on failure.
  530. //-----------------------------------------------------------------------------
  531. BOOL CMapFace::IsTextureAxisValid(void) const
  532. {
  533. //
  534. // Generate the texture normal axis, which may be different from the
  535. // face normal, depending on texture alignment.
  536. //
  537. Vector TexNormalAxis;
  538. CrossProduct(texture.VAxis.AsVector3D(), texture.UAxis.AsVector3D(), TexNormalAxis);
  539. return(DotProduct(plane.normal, TexNormalAxis) != 0);
  540. }
  541. //-----------------------------------------------------------------------------
  542. // Purpose: Normalize the U/V shift values to be less than the texture width/height.
  543. //-----------------------------------------------------------------------------
  544. void CMapFace::NormalizeTextureShifts(void)
  545. {
  546. //
  547. // HACK: this should really be elsewhere, but it can live here for now.
  548. // Round all components of our texture axes within an epsilon.
  549. //
  550. for (int nDim = 0; nDim < 4; nDim++)
  551. {
  552. int nValue = rint(texture.UAxis[nDim]);
  553. if (fabs(texture.UAxis[nDim] - nValue) < TEXTURE_AXIS_ROUND_EPSILON)
  554. {
  555. texture.UAxis[nDim] = nValue;
  556. }
  557. nValue = rint(texture.VAxis[nDim]);
  558. if (fabs(texture.VAxis[nDim] - nValue) < TEXTURE_AXIS_ROUND_EPSILON)
  559. {
  560. texture.VAxis[nDim] = nValue;
  561. }
  562. }
  563. if (m_pTexture == NULL)
  564. {
  565. return;
  566. }
  567. if (m_pTexture->GetMappingWidth() != 0)
  568. {
  569. texture.UAxis[3] = fmod(texture.UAxis[3], m_pTexture->GetMappingWidth());
  570. }
  571. if (m_pTexture->GetMappingHeight() != 0)
  572. {
  573. texture.VAxis[3] = fmod(texture.VAxis[3], m_pTexture->GetMappingHeight());
  574. }
  575. }
  576. //-----------------------------------------------------------------------------
  577. // Purpose: Determines the bounding box of a face in world space.
  578. // Input : pfMins - Receives the face X, Y, Z minima.
  579. // pfMaxs - Receives the face X, Y, Z maxima.
  580. //-----------------------------------------------------------------------------
  581. void CMapFace::GetFaceBounds(Vector& pfMins, Vector& pfMaxs) const
  582. {
  583. for (int nPoint = 0; nPoint < nPoints; nPoint++)
  584. {
  585. if ((Points[nPoint][0] < pfMins[0]) || (nPoint == 0))
  586. {
  587. pfMins[0] = Points[nPoint][0];
  588. }
  589. if ((Points[nPoint][1] < pfMins[1]) || (nPoint == 0))
  590. {
  591. pfMins[1] = Points[nPoint][1];
  592. }
  593. if ((Points[nPoint][2] < pfMins[2]) || (nPoint == 0))
  594. {
  595. pfMins[2] = Points[nPoint][2];
  596. }
  597. if ((Points[nPoint][0] > pfMaxs[0]) || (nPoint == 0))
  598. {
  599. pfMaxs[0] = Points[nPoint][0];
  600. }
  601. if ((Points[nPoint][1] > pfMaxs[1]) || (nPoint == 0))
  602. {
  603. pfMaxs[1] = Points[nPoint][1];
  604. }
  605. if ((Points[nPoint][2] > pfMaxs[2]) || (nPoint == 0))
  606. {
  607. pfMaxs[2] = Points[nPoint][2];
  608. }
  609. }
  610. }
  611. //-----------------------------------------------------------------------------
  612. // Purpose: Finds the top left and bottom right points on the face in texture space.
  613. // These points are returned in texture space, not world space.
  614. // Input : TopLeft -
  615. // BottomRight -
  616. //-----------------------------------------------------------------------------
  617. void CMapFace::GetFaceTextureExtents(Vector2D & TopLeft, Vector2D & BottomRight) const
  618. {
  619. BOOL bFirst = TRUE;
  620. for (int nPoint = 0; nPoint < nPoints; nPoint++)
  621. {
  622. Vector2D Test;
  623. Test[0] = DotProduct(Points[nPoint], texture.UAxis.AsVector3D()) / texture.scale[0];
  624. Test[1] = DotProduct(Points[nPoint], texture.VAxis.AsVector3D()) / texture.scale[1];
  625. if ((Test[0] < TopLeft[0]) || (bFirst))
  626. {
  627. TopLeft[0] = Test[0];
  628. }
  629. if ((Test[1] < TopLeft[1]) || (bFirst))
  630. {
  631. TopLeft[1] = Test[1];
  632. }
  633. if ((Test[0] > BottomRight[0]) || (bFirst))
  634. {
  635. BottomRight[0] = Test[0];
  636. }
  637. if ((Test[1] > BottomRight[1]) || (bFirst))
  638. {
  639. BottomRight[1] = Test[1];
  640. }
  641. bFirst = FALSE;
  642. }
  643. }
  644. //-----------------------------------------------------------------------------
  645. // Purpose: Returns the distance along the face normal of a given point. The
  646. // distance will be negative if the point is behind the face, positive
  647. // if the point is in front of the face.
  648. // Input : fPoint - Point to calculate normal distance.
  649. //-----------------------------------------------------------------------------
  650. float CMapFace::GetNormalDistance(Vector& fPoint)
  651. {
  652. float fDot = DotProduct(fPoint, plane.normal);
  653. return(fDot - plane.dist);
  654. }
  655. //-----------------------------------------------------------------------------
  656. // Purpose: Determines the texture alignment(s) of this face. The alignments are
  657. // are returned as TextureAlignment_t values OR'ed together.
  658. //
  659. // Output : Returns an integer with any of the following flags set:
  660. //
  661. // TEXTURE_ALIGN_FACE - the texture axes are face aligned.
  662. // TEXTURE_ALIGN_WORLD - the texture axes are world aligned.
  663. //
  664. // If the returned value is zero (TEXTURE_ALIGN_NONE), the texture axes
  665. // are neither face aligned nor world aligned.
  666. //-----------------------------------------------------------------------------
  667. int CMapFace::GetTextureAlignment(void) const
  668. {
  669. Vector TexNormalAxis;
  670. int nAlignment = TEXTURE_ALIGN_NONE;
  671. //
  672. // Generate the texture normal axis, which may be different from the
  673. // face normal, depending on texture alignment.
  674. //
  675. CrossProduct(texture.VAxis.AsVector3D(), texture.UAxis.AsVector3D(), TexNormalAxis);
  676. VectorNormalize(TexNormalAxis);
  677. //
  678. // Check for face alignment.
  679. //
  680. if (DotProduct(TexNormalAxis, plane.normal) > 0.9999)
  681. {
  682. nAlignment |= TEXTURE_ALIGN_FACE;
  683. }
  684. //
  685. // Check for world alignment.
  686. //
  687. FaceOrientation_t eOrientation = GetOrientation();
  688. if (eOrientation != FACE_ORIENTATION_INVALID)
  689. {
  690. Vector WorldTexNormal;
  691. CrossProduct(DownVectors[eOrientation], RightVectors[eOrientation], WorldTexNormal);
  692. if (DotProduct(TexNormalAxis, WorldTexNormal) > 0.9999)
  693. {
  694. nAlignment |= TEXTURE_ALIGN_WORLD;
  695. }
  696. }
  697. return(nAlignment);
  698. }
  699. //-----------------------------------------------------------------------------
  700. // Purpose: Finds the top left and bottom right points of the given world extents
  701. // in texture space. These points are returned in texture space, not world space,
  702. // so a simple rectangle will suffice.
  703. // Input : Extents -
  704. // TopLeft -
  705. // BottomRight -
  706. //-----------------------------------------------------------------------------
  707. void CMapFace::GetTextureExtents(Extents_t Extents, Vector2D & TopLeft, Vector2D & BottomRight) const
  708. {
  709. BOOL bFirst = TRUE;
  710. for (int nPoint = 0; nPoint < NUM_EXTENTS_DIMS; nPoint++)
  711. {
  712. Vector2D Test;
  713. Test[0] = DotProduct(Extents[nPoint], texture.UAxis.AsVector3D()) / texture.scale[0];
  714. Test[1] = DotProduct(Extents[nPoint], texture.VAxis.AsVector3D()) / texture.scale[1];
  715. if ((Test[0] < TopLeft[0]) || (bFirst))
  716. {
  717. TopLeft[0] = Test[0];
  718. }
  719. if ((Test[1] < TopLeft[1]) || (bFirst))
  720. {
  721. TopLeft[1] = Test[1];
  722. }
  723. if ((Test[0] > BottomRight[0]) || (bFirst))
  724. {
  725. BottomRight[0] = Test[0];
  726. }
  727. if ((Test[1] > BottomRight[1]) || (bFirst))
  728. {
  729. BottomRight[1] = Test[1];
  730. }
  731. bFirst = FALSE;
  732. }
  733. }
  734. //-----------------------------------------------------------------------------
  735. // Purpose: Determines the world extents of the face. Different from a bounding
  736. // box in that each point in the returned extents is actually on the face.
  737. // Input : Extents -
  738. //-----------------------------------------------------------------------------
  739. void CMapFace::GetFaceExtents(Extents_t Extents) const
  740. {
  741. BOOL bFirst = TRUE;
  742. for (int nPoint = 0; nPoint < nPoints; nPoint++)
  743. {
  744. if ((Points[nPoint][0] < Extents[EXTENTS_XMIN][0]) || (bFirst))
  745. {
  746. Extents[EXTENTS_XMIN] = Points[nPoint];
  747. }
  748. if ((Points[nPoint][0] > Extents[EXTENTS_XMAX][0]) || (bFirst))
  749. {
  750. Extents[EXTENTS_XMAX] = Points[nPoint];
  751. }
  752. if ((Points[nPoint][1] < Extents[EXTENTS_YMIN][1]) || (bFirst))
  753. {
  754. Extents[EXTENTS_YMIN] = Points[nPoint];
  755. }
  756. if ((Points[nPoint][1] > Extents[EXTENTS_YMAX][1]) || (bFirst))
  757. {
  758. Extents[EXTENTS_YMAX] = Points[nPoint];
  759. }
  760. if ((Points[nPoint][2] < Extents[EXTENTS_ZMIN][2]) || (bFirst))
  761. {
  762. Extents[EXTENTS_ZMIN] = Points[nPoint];
  763. }
  764. if ((Points[nPoint][2] > Extents[EXTENTS_ZMAX][2]) || (bFirst))
  765. {
  766. Extents[EXTENTS_ZMAX] = Points[nPoint];
  767. }
  768. bFirst = FALSE;
  769. }
  770. }
  771. //-----------------------------------------------------------------------------
  772. // Purpose:
  773. // Input : eJustification -
  774. // Extents -
  775. //-----------------------------------------------------------------------------
  776. void CMapFace::JustifyTextureUsingExtents(TextureJustification_t eJustification, Extents_t Extents)
  777. {
  778. Vector2D Center;
  779. if (!texture.scale[0])
  780. {
  781. texture.scale[0] = g_pGameConfig->GetDefaultTextureScale();
  782. }
  783. if (!texture.scale[1])
  784. {
  785. texture.scale[1] = g_pGameConfig->GetDefaultTextureScale();
  786. }
  787. // Skip all the mucking about for a justification of NONE.
  788. if (eJustification == TEXTURE_JUSTIFY_NONE)
  789. {
  790. texture.UAxis[3] = 0;
  791. texture.VAxis[3] = 0;
  792. CalcTextureCoords();
  793. return;
  794. }
  795. // For fit justification, use a scale of 1 for initial calculations.
  796. if (eJustification == TEXTURE_JUSTIFY_FIT)
  797. {
  798. texture.scale[0] = 1.0;
  799. texture.scale[1] = 1.0;
  800. }
  801. Vector2D TopLeft;
  802. Vector2D BottomRight;
  803. GetTextureExtents(Extents, TopLeft, BottomRight);
  804. // Find the face center in U/V space.
  805. Center[0] = (TopLeft[0] + BottomRight[0]) / 2;
  806. Center[1] = (TopLeft[1] + BottomRight[1]) / 2;
  807. //
  808. // Perform the justification.
  809. //
  810. switch (eJustification)
  811. {
  812. // Align the top left corner of the texture with the top left corner of the face.
  813. case TEXTURE_JUSTIFY_TOP:
  814. {
  815. texture.VAxis[3] = -TopLeft[1];
  816. break;
  817. }
  818. // Align the top left corner of the texture with the top left corner of the face.
  819. case TEXTURE_JUSTIFY_BOTTOM:
  820. {
  821. texture.VAxis[3] = -BottomRight[1] + m_pTexture->GetMappingHeight();
  822. break;
  823. }
  824. // Align the left side of the texture with the left side of the face.
  825. case TEXTURE_JUSTIFY_LEFT:
  826. {
  827. texture.UAxis[3] = -TopLeft[0];
  828. break;
  829. }
  830. // Align the right side of the texture with the right side of the face.
  831. case TEXTURE_JUSTIFY_RIGHT:
  832. {
  833. texture.UAxis[3] = -BottomRight[0] + m_pTexture->GetMappingWidth();
  834. break;
  835. }
  836. // Center the texture on the face.
  837. case TEXTURE_JUSTIFY_CENTER:
  838. {
  839. texture.UAxis[3] = -Center[0] + (m_pTexture->GetMappingWidth() / 2);
  840. texture.VAxis[3] = -Center[1] + (m_pTexture->GetMappingHeight() / 2);
  841. break;
  842. }
  843. // Scale the texture to exactly fit the face.
  844. case TEXTURE_JUSTIFY_FIT:
  845. {
  846. // Calculate the appropriate scale.
  847. if (m_pTexture && m_pTexture->GetMappingWidth() && m_pTexture->GetMappingHeight())
  848. {
  849. texture.scale[0] = (BottomRight[0] - TopLeft[0]) / m_pTexture->GetMappingWidth();
  850. texture.scale[1] = (BottomRight[1] - TopLeft[1]) / m_pTexture->GetMappingHeight();
  851. }
  852. else
  853. {
  854. texture.scale[0] = g_pGameConfig->GetDefaultTextureScale();
  855. texture.scale[1] = g_pGameConfig->GetDefaultTextureScale();
  856. }
  857. // Justify top left.
  858. JustifyTextureUsingExtents(TEXTURE_JUSTIFY_TOP, Extents);
  859. JustifyTextureUsingExtents(TEXTURE_JUSTIFY_LEFT, Extents);
  860. break;
  861. }
  862. }
  863. NormalizeTextureShifts();
  864. CalcTextureCoords();
  865. }
  866. //-----------------------------------------------------------------------------
  867. // Purpose:
  868. // Input : eJustification -
  869. //-----------------------------------------------------------------------------
  870. void CMapFace::JustifyTexture(TextureJustification_t eJustification)
  871. {
  872. Extents_t Extents;
  873. GetFaceExtents(Extents);
  874. JustifyTextureUsingExtents(eJustification, Extents);
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Purpose: Offsets a texture due to texture locking when moving a face.
  878. // Input : Delta - The x, y, z translation that was applied to the face points.
  879. //-----------------------------------------------------------------------------
  880. void CMapFace::OffsetTexture(const Vector &Delta)
  881. {
  882. //
  883. // Find the projection in U/V space of this movement
  884. // and shift the textures by that.
  885. //
  886. texture.UAxis[3] -= DotProduct(Delta, texture.UAxis.AsVector3D()) / texture.scale[0];
  887. texture.VAxis[3] -= DotProduct(Delta, texture.VAxis.AsVector3D()) / texture.scale[1];
  888. NormalizeTextureShifts();
  889. }
  890. //-----------------------------------------------------------------------------
  891. // Purpose: Rotates the texture axes fDegrees counterclockwise around the
  892. // texture normal axis.
  893. // Input : fDegrees - Degrees to rotate the texture axes.
  894. //-----------------------------------------------------------------------------
  895. void CMapFace::RotateTextureAxes(float fDegrees)
  896. {
  897. VMatrix Matrix;
  898. Vector TexNormalAxis;
  899. Vector4D UAxis;
  900. Vector4D VAxis;
  901. // Generate the texture normal axis, which may be different from the
  902. // face normal, depending on texture alignment.
  903. CrossProduct(texture.VAxis.AsVector3D(), texture.UAxis.AsVector3D(), TexNormalAxis);
  904. // Rotate the texture axes around the texture normal.
  905. AxisAngleMatrix(Matrix, TexNormalAxis, fDegrees);
  906. Matrix.V4Mul( texture.UAxis, UAxis );
  907. Matrix.V4Mul( texture.VAxis, VAxis );
  908. texture.UAxis = UAxis;
  909. texture.VAxis = VAxis;
  910. }
  911. //-----------------------------------------------------------------------------
  912. // Purpose: Rebuilds the plane normal and distance from the plane points.
  913. //-----------------------------------------------------------------------------
  914. void CMapFace::CalcPlane(void)
  915. {
  916. //
  917. // Build the plane normal and distance from the three plane points.
  918. //
  919. plane.normal = GetNormalFromPoints( plane.planepts[0], plane.planepts[1], plane.planepts[2] );
  920. plane.dist = DotProduct(plane.planepts[0], plane.normal);
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Purpose: Rebuilds the plane points from our face points.
  924. //-----------------------------------------------------------------------------
  925. void CMapFace::CalcPlaneFromFacePoints(void)
  926. {
  927. if ((nPoints >= 3) && (Points != NULL))
  928. {
  929. //
  930. // Use the face points as a preliminary set of plane points.
  931. //
  932. memcpy(plane.planepts, Points, sizeof(Vector) * 3);
  933. //
  934. // Generate the plane normal and distance from the plane points.
  935. //
  936. CalcPlane();
  937. //
  938. // Now project large coordinates onto the plane to generate new
  939. // plane points that will be less prone to error creep.
  940. //
  941. // UNDONE: push out the points along the plane for better precision
  942. }
  943. }
  944. void CMapFace::AddShadowingTriangles( CUtlVector<Vector> &tri_list )
  945. {
  946. // create a fan
  947. if (! (m_nFaceFlags & FACE_FLAGS_NOSHADOW ))
  948. for(int i=2;i<nPoints;i++)
  949. {
  950. tri_list.AddToTail( Points[0] );
  951. tri_list.AddToTail( Points[i-1] );
  952. tri_list.AddToTail( Points[i] );
  953. }
  954. }
  955. #ifdef DEBUGPTS
  956. void CMapFace::DebugPoints(void)
  957. {
  958. // check for dup points
  959. for(i = 0; i < nPoints; i++)
  960. {
  961. for(int j = 0; j < nPoints; j++)
  962. {
  963. if(j == i)
  964. continue;
  965. if(Points[j][0] == Points[i][0] &&
  966. Points[j][1] == Points[i][1] &&
  967. Points[j][2] == Points[i][2])
  968. {
  969. AfxMessageBox("Dup Points in CMapFace::Create(winding_t*)");
  970. break;
  971. }
  972. }
  973. }
  974. }
  975. #endif
  976. //-----------------------------------------------------------------------------
  977. // Purpose: Create the face from a winding type.
  978. // w - Winding from which to create the face.
  979. // nFlags -
  980. // CREATE_FACE_PRESERVE_PLANE:
  981. // CREATE_FACE_CLIPPING: the new face is a clipped version of this face
  982. //-----------------------------------------------------------------------------
  983. void CMapFace::CreateFace(winding_t *w, int nFlags)
  984. {
  985. SignalUpdate( EVTYPE_FACE_CHANGED );
  986. AllocatePoints(w->numpoints);
  987. for (int i = 0; i < nPoints; i++)
  988. {
  989. Points[i][0] = w->p[i][0];
  990. Points[i][1] = w->p[i][1];
  991. Points[i][2] = w->p[i][2];
  992. }
  993. if (!(nFlags & CREATE_FACE_PRESERVE_PLANE))
  994. {
  995. CalcPlaneFromFacePoints();
  996. }
  997. //
  998. // Create a new displacement surface if the clipped surfaces is a quad.
  999. //
  1000. // This assumes it is being called by the clipper!!! (Bad assumption).
  1001. //
  1002. if( HasDisp() && ( nFlags & CREATE_FACE_CLIPPING ) )
  1003. {
  1004. if ( nPoints == 4 )
  1005. {
  1006. // Setup new displacement surface.
  1007. EditDispHandle_t hClipDisp = EditDispMgr()->Create();
  1008. CMapDisp *pClipDisp = EditDispMgr()->GetDisp( hClipDisp );
  1009. // Get older displacement surface.
  1010. EditDispHandle_t hDisp = GetDisp();
  1011. CMapDisp *pDisp = EditDispMgr()->GetDisp( hDisp );
  1012. // Init new displacement surface.
  1013. pClipDisp->SetParent( this );
  1014. // Apply the new displacement to this face, but keep the old one
  1015. // around -- we need it for the split operation.
  1016. SetDisp( hClipDisp, false );
  1017. pClipDisp->InitData( pDisp->GetPower() );
  1018. // Calculate texture coordinates before splitting because we
  1019. // need the texture coords during the split.
  1020. CalcTextureCoords();
  1021. // Split the old displacement and put the results into hClipDisp.
  1022. pDisp->Split( hClipDisp );
  1023. // Delete the old displacement that was on this face.
  1024. EditDispMgr()->Destroy( hDisp );
  1025. }
  1026. else
  1027. {
  1028. SetDisp( EDITDISPHANDLE_INVALID );
  1029. }
  1030. }
  1031. else
  1032. {
  1033. CalcTextureCoords();
  1034. }
  1035. #ifdef ENSUREDETAILS
  1036. // Create any detail objects if appropriate
  1037. DetailObjects::BuildAnyDetailObjects(this);
  1038. #endif
  1039. #ifdef DEBUGPTS
  1040. DebugPoints();
  1041. #endif
  1042. }
  1043. //-----------------------------------------------------------------------------
  1044. // Purpose: Allocates space in Points array for nPoints worth of Vectors and
  1045. // the corresponding texture and lightmap coordinates (Vector2D's). Frees
  1046. // current points if there are any.
  1047. // Input : _nPoints - number of points needed.
  1048. // Output : Total size of memory used by the points, texture, and lightmap coordinates.
  1049. //-----------------------------------------------------------------------------
  1050. size_t CMapFace::AllocatePoints(int _nPoints)
  1051. {
  1052. //
  1053. // If we have already allocated this many points, do nothing.
  1054. //
  1055. if ((Points != NULL) && (_nPoints == nPoints))
  1056. {
  1057. return(nPoints * (sizeof(Vector) + sizeof(Vector2D) + sizeof(Vector2D)));
  1058. }
  1059. //
  1060. // If we have the wrong number of points allocated, free the memory.
  1061. //
  1062. if (Points != NULL)
  1063. {
  1064. delete [] Points;
  1065. Points = NULL;
  1066. delete [] m_pTextureCoords;
  1067. m_pTextureCoords = NULL;
  1068. delete [] m_pLightmapCoords;
  1069. m_pLightmapCoords = NULL;
  1070. }
  1071. Assert( nPoints == 0 || nPoints > 2 );
  1072. nPoints = _nPoints;
  1073. if (!_nPoints)
  1074. {
  1075. return(0);
  1076. }
  1077. //
  1078. // Allocate the correct number of points, texture coords, and lightmap coords.
  1079. //
  1080. Points = new Vector[nPoints];
  1081. m_pTextureCoords = new Vector2D[nPoints];
  1082. m_pLightmapCoords = new Vector2D[nPoints];
  1083. // dvs: check for failure here and report an out of memory error
  1084. Assert(Points != NULL);
  1085. Assert(m_pTextureCoords != NULL);
  1086. Assert(m_pLightmapCoords != NULL);
  1087. return(nPoints * (sizeof(Vector) + sizeof(Vector2D) + sizeof(Vector2D)));
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose:
  1091. // Input : *pTexture -
  1092. //-----------------------------------------------------------------------------
  1093. void CMapFace::SetTexture(IEditorTexture *pTexture, bool bRescaleTextureCoordinates)
  1094. {
  1095. SignalUpdate( EVTYPE_FACE_CHANGED );
  1096. if ( m_pTexture && pTexture && bRescaleTextureCoordinates )
  1097. {
  1098. float flXFactor = (float)m_pTexture->GetMappingWidth() / pTexture->GetMappingWidth();
  1099. float flYFactor = (float)m_pTexture->GetMappingHeight() / pTexture->GetMappingHeight();
  1100. texture.scale[0] *= flXFactor;
  1101. texture.scale[1] *= flYFactor;
  1102. texture.UAxis[3] /= flXFactor;
  1103. texture.VAxis[3] /= flYFactor;
  1104. }
  1105. m_pTexture = pTexture;
  1106. // Copy other things from m_pTexture.
  1107. m_pTexture->GetShortName(texture.texture);
  1108. texture.q2surface = m_pTexture->GetSurfaceAttributes();
  1109. texture.q2contents = m_pTexture->GetSurfaceContents();
  1110. BOOL bTexValid = FALSE;
  1111. if (m_pTexture != NULL)
  1112. {
  1113. // Insure that the texture is loaded.
  1114. m_pTexture->Load();
  1115. bTexValid = !(
  1116. m_pTexture->GetPreviewImageWidth() == 0 ||
  1117. m_pTexture->GetPreviewImageHeight() == 0 ||
  1118. m_pTexture->GetMappingWidth() == 0 ||
  1119. m_pTexture->GetMappingHeight() == 0 ||
  1120. !m_pTexture->HasData()
  1121. );
  1122. }
  1123. if (bTexValid)
  1124. {
  1125. CalcTextureCoords();
  1126. }
  1127. UpdateFaceFlags();
  1128. }
  1129. //-----------------------------------------------------------------------------
  1130. // Purpose: Sets this face's texture by name.
  1131. // Input : pszNewTex - Short name of texture to apply to this face.
  1132. //-----------------------------------------------------------------------------
  1133. void CMapFace::SetTexture(const char *pszNewTex, bool bRescaleTextureCoordinates)
  1134. {
  1135. SignalUpdate( EVTYPE_FACE_CHANGED );
  1136. IEditorTexture *pTexture = g_Textures.FindActiveTexture(pszNewTex);
  1137. SetTexture(pTexture, bRescaleTextureCoordinates);
  1138. }
  1139. //-----------------------------------------------------------------------------
  1140. //-----------------------------------------------------------------------------
  1141. void CMapFace::CalcTextureCoordAtPoint( const Vector& pt, Vector2D &texCoord )
  1142. {
  1143. // sanity check
  1144. if( m_pTexture == NULL )
  1145. return;
  1146. //
  1147. // projected s, t (u, v) texture coordinates
  1148. //
  1149. float s = DotProduct( texture.UAxis.AsVector3D(), pt ) / texture.scale[0] + texture.UAxis[3];
  1150. float t = DotProduct( texture.VAxis.AsVector3D(), pt ) / texture.scale[1] + texture.VAxis[3];
  1151. //
  1152. // "normalize" the texture coordinates
  1153. //
  1154. if ( m_pTexture->GetMappingWidth() )
  1155. texCoord[0] = s / ( float )m_pTexture->GetMappingWidth();
  1156. else
  1157. texCoord[0] = 0.0;
  1158. if ( m_pTexture->GetMappingHeight() )
  1159. texCoord[1] = t / ( float )m_pTexture->GetMappingHeight();
  1160. else
  1161. texCoord[1] = 0.0;
  1162. }
  1163. //-----------------------------------------------------------------------------
  1164. //-----------------------------------------------------------------------------
  1165. void CMapFace::CalcLightmapCoordAtPoint( const Vector& pt, Vector2D &lightCoord )
  1166. {
  1167. lightCoord[0] = DotProduct( texture.UAxis.AsVector3D(), pt ) / texture.nLightmapScale + 0.5f;
  1168. lightCoord[1] = DotProduct( texture.VAxis.AsVector3D(), pt ) / texture.nLightmapScale + 0.5f;
  1169. }
  1170. //-----------------------------------------------------------------------------
  1171. // Purpose: Calculates the U,V texture coordinates of all points on this face.
  1172. //-----------------------------------------------------------------------------
  1173. void CMapFace::CalcTextureCoords(void)
  1174. {
  1175. float s, t;
  1176. int i;
  1177. if (m_pTexture == NULL)
  1178. {
  1179. return;
  1180. }
  1181. //
  1182. // Make sure that scales are nonzero.
  1183. //
  1184. if (texture.scale[0] == 0)
  1185. {
  1186. texture.scale[0] = g_pGameConfig->GetDefaultTextureScale();
  1187. }
  1188. if (texture.scale[1] == 0)
  1189. {
  1190. texture.scale[1] = g_pGameConfig->GetDefaultTextureScale();
  1191. }
  1192. //
  1193. // Recalculate U,V coordinates for all points.
  1194. //
  1195. for (i = 0; i < nPoints; i++)
  1196. {
  1197. //
  1198. // Generate texture coordinates.
  1199. //
  1200. s = DotProduct(texture.UAxis.AsVector3D(), Points[i]) / texture.scale[0] + texture.UAxis[3];
  1201. t = DotProduct(texture.VAxis.AsVector3D(), Points[i]) / texture.scale[1] + texture.VAxis[3];
  1202. if (m_pTexture->GetMappingWidth())
  1203. m_pTextureCoords[i][0] = s / (float)m_pTexture->GetMappingWidth();
  1204. else
  1205. m_pTextureCoords[i][0] = 0.0f;
  1206. if (m_pTexture->GetMappingHeight())
  1207. m_pTextureCoords[i][1] = t / (float)m_pTexture->GetMappingHeight();
  1208. else
  1209. m_pTextureCoords[i][1] = 0.0f;
  1210. //
  1211. // Generate lightmap coordinates. Lightmap coordinates for displacements happens below.
  1212. //
  1213. if ( m_DispHandle == EDITDISPHANDLE_INVALID )
  1214. {
  1215. float shiftScaleU = texture.scale[0] / (float)texture.nLightmapScale;
  1216. float shiftScaleV = texture.scale[1] / (float)texture.nLightmapScale;
  1217. m_pLightmapCoords[i][0] = DotProduct(texture.UAxis.AsVector3D(), Points[i]) / texture.nLightmapScale + texture.UAxis[3] * shiftScaleU + 0.5;
  1218. m_pLightmapCoords[i][1] = DotProduct(texture.VAxis.AsVector3D(), Points[i]) / texture.nLightmapScale + texture.VAxis[3] * shiftScaleV + 0.5;
  1219. }
  1220. }
  1221. //
  1222. // update the displacement map with new texture coordinates and calculate lightmap coordinates
  1223. //
  1224. if( ( m_DispHandle != EDITDISPHANDLE_INVALID ) && nPoints == 4 )
  1225. {
  1226. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  1227. pDisp->InitDispSurfaceData( this, false );
  1228. pDisp->Create();
  1229. }
  1230. // re-calculate the tangent space
  1231. CalcTangentSpaceAxes();
  1232. }
  1233. //-----------------------------------------------------------------------------
  1234. // Returns the max lightmap size for this face
  1235. //-----------------------------------------------------------------------------
  1236. int CMapFace::MaxLightmapSize() const
  1237. {
  1238. return HasDisp() ? MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER : MAX_BRUSH_LIGHTMAP_DIM_WITHOUT_BORDER;
  1239. }
  1240. //-----------------------------------------------------------------------------
  1241. // Purpose: Checks the validity of this face.
  1242. // Input : pInfo -
  1243. // Output : Returns TRUE on success, FALSE on failure.
  1244. //-----------------------------------------------------------------------------
  1245. BOOL CMapFace::CheckFace(CCheckFaceInfo *pInfo)
  1246. {
  1247. if (!::CheckFace(Points, nPoints, &plane.normal, plane.dist, pInfo))
  1248. {
  1249. return(FALSE);
  1250. }
  1251. //
  1252. // Check for duplicate plane points. All three plane points must be unique
  1253. // or it isn't a valid plane.
  1254. //
  1255. for (int nPlane = 0; nPlane < 3; nPlane++)
  1256. {
  1257. for (int nPlaneCheck = 0; nPlaneCheck < 3; nPlaneCheck++)
  1258. {
  1259. if (nPlane != nPlaneCheck)
  1260. {
  1261. if (VectorCompare(plane.planepts[nPlane], plane.planepts[nPlaneCheck]))
  1262. {
  1263. if (pInfo != NULL)
  1264. {
  1265. strcpy(pInfo->szDescription, "face has duplicate plane points");
  1266. }
  1267. return(FALSE);
  1268. }
  1269. }
  1270. }
  1271. }
  1272. return(TRUE);
  1273. }
  1274. //-----------------------------------------------------------------------------
  1275. // Purpose: Included for loading old (quake-style) maps. This sets up the texture axes
  1276. // the same way QCSG and pre-2.2 Hammer did.
  1277. // Input : UAxis -
  1278. // VAxis -
  1279. //-----------------------------------------------------------------------------
  1280. void CMapFace::InitializeQuakeStyleTextureAxes(Vector4D& UAxis, Vector4D& VAxis)
  1281. {
  1282. static Vector baseaxis[18] =
  1283. {
  1284. Vector(0,0,1), Vector(1,0,0), Vector(0,-1,0), // floor
  1285. Vector(0,0,-1), Vector(1,0,0), Vector(0,-1,0), // ceiling
  1286. Vector(1,0,0), Vector(0,1,0), Vector(0,0,-1), // west wall
  1287. Vector(-1,0,0), Vector(0,1,0), Vector(0,0,-1), // east wall
  1288. Vector(0,1,0), Vector(1,0,0), Vector(0,0,-1), // south wall
  1289. Vector(0,-1,0), Vector(1,0,0), Vector(0,0,-1) // north wall
  1290. };
  1291. int bestaxis;
  1292. vec_t dot,best;
  1293. int i;
  1294. best = 0;
  1295. bestaxis = 0;
  1296. for (i=0 ; i<6 ; i++)
  1297. {
  1298. dot = DotProduct(plane.normal, baseaxis[i*3]);
  1299. if (dot > best)
  1300. {
  1301. best = dot;
  1302. bestaxis = i;
  1303. }
  1304. }
  1305. UAxis.AsVector3D() = baseaxis[bestaxis * 3 + 1];
  1306. VAxis.AsVector3D() = baseaxis[bestaxis * 3 + 2];
  1307. }
  1308. //-----------------------------------------------------------------------------
  1309. // Should we render this lit or not
  1310. //-----------------------------------------------------------------------------
  1311. void CMapFace::RenderUnlit( bool enable )
  1312. {
  1313. m_bIgnoreLighting = enable;
  1314. }
  1315. inline void Modulate( Color &pColor, float f )
  1316. {
  1317. pColor[0] *= f;
  1318. pColor[1] *= f;
  1319. pColor[2] *= f;
  1320. }
  1321. //-----------------------------------------------------------------------------
  1322. // Computes the color and texture to use
  1323. //-----------------------------------------------------------------------------
  1324. void CMapFace::ComputeColor( CRender3D* pRender, bool bRenderAsSelected,
  1325. SelectionState_t faceSelectionState,
  1326. bool ignoreLighting, Color &pColor )
  1327. {
  1328. EditorRenderMode_t eCurrentRenderMode = pRender->GetCurrentRenderMode();
  1329. // White w/alpha by default
  1330. pColor[0] = pColor[1] = pColor[2] = 255;
  1331. pColor[3] = m_uchAlpha;
  1332. float fShade;
  1333. if (!ignoreLighting)
  1334. fShade = pRender->LightPlane(plane.normal);
  1335. else
  1336. fShade = 1.0;
  1337. switch (eCurrentRenderMode)
  1338. {
  1339. case RENDER_MODE_TEXTURED:
  1340. case RENDER_MODE_TEXTURED_SHADED:
  1341. case RENDER_MODE_LIGHT_PREVIEW2:
  1342. case RENDER_MODE_LIGHT_PREVIEW_RAYTRACED:
  1343. Modulate( pColor, fShade );
  1344. break;
  1345. case RENDER_MODE_SELECTION_OVERLAY:
  1346. if( faceSelectionState == SELECT_MULTI_PARTIAL )
  1347. {
  1348. pColor[2] = 100;
  1349. pColor[3] = 64;
  1350. }
  1351. else if( ( faceSelectionState == SELECT_NORMAL ) || bRenderAsSelected )
  1352. {
  1353. SelectFaceColor( pColor );
  1354. pColor[3] = 64;
  1355. }
  1356. break;
  1357. case RENDER_MODE_LIGHTMAP_GRID:
  1358. if (bRenderAsSelected)
  1359. {
  1360. SelectFaceColor( pColor );
  1361. }
  1362. else if (texture.nLightmapScale > DEFAULT_LIGHTMAP_SCALE)
  1363. {
  1364. pColor[0] = 150;
  1365. }
  1366. else if (texture.nLightmapScale < DEFAULT_LIGHTMAP_SCALE)
  1367. {
  1368. pColor[2] = 100;
  1369. }
  1370. Modulate( pColor, fShade );
  1371. break;
  1372. case RENDER_MODE_TRANSLUCENT_FLAT:
  1373. case RENDER_MODE_FLAT:
  1374. if (bRenderAsSelected)
  1375. SelectFaceColor( pColor );
  1376. else
  1377. pColor.SetColor( r,g,b,m_uchAlpha );
  1378. Modulate( pColor, fShade );
  1379. break;
  1380. case RENDER_MODE_WIREFRAME:
  1381. if (bRenderAsSelected)
  1382. SelectEdgeColor( pColor );
  1383. else
  1384. pColor.SetColor( r,g,b,m_uchAlpha );
  1385. break;
  1386. case RENDER_MODE_SMOOTHING_GROUP:
  1387. {
  1388. // Render the non-smoothing group faces in white, yellow for the others.
  1389. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1390. if ( pDoc )
  1391. {
  1392. int iGroup = pDoc->GetSmoothingGroupVisual();
  1393. if ( InSmoothingGroup( iGroup ) )
  1394. {
  1395. pColor[2] = 0;
  1396. }
  1397. }
  1398. Modulate( pColor, fShade );
  1399. break;
  1400. }
  1401. default:
  1402. assert(0);
  1403. break;
  1404. }
  1405. }
  1406. static bool ModeUsesTextureCoords(EditorRenderMode_t mode)
  1407. {
  1408. return (
  1409. (mode == RENDER_MODE_TEXTURED) ||
  1410. (mode == RENDER_MODE_LIGHTMAP_GRID) ||
  1411. (mode == RENDER_MODE_TEXTURED_SHADED) ||
  1412. (mode == RENDER_MODE_LIGHT_PREVIEW2) ||
  1413. (mode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED)
  1414. );
  1415. }
  1416. //-----------------------------------------------------------------------------
  1417. // Draws the face using the material system material
  1418. //-----------------------------------------------------------------------------
  1419. void CMapFace::DrawFace( Color &pColor, EditorRenderMode_t mode )
  1420. {
  1421. // retrieve the coordinate frame to render into
  1422. // (most likely just the identity, unless we're animating)
  1423. VMatrix frame;
  1424. bool hasParent = GetTransformMatrix( frame );
  1425. // don't do this -- if you use the material system to rotate and/or translate
  1426. // this will cull the locally spaced object!! -- need to pass around a flag!
  1427. #if 0
  1428. // A little culling....
  1429. float fEyeDot = DotProduct(plane.normal, ViewPoint);
  1430. if ((fEyeDot < plane.dist) && (mode != RENDER_MODE_WIREFRAME) && !hasParent &&
  1431. (m_uchAlpha == 255))
  1432. {
  1433. return;
  1434. }
  1435. #endif
  1436. // don't draw no draws in ray tracced mode
  1437. if ( mode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED )
  1438. {
  1439. if ( m_nFaceFlags & FACE_FLAGS_NODRAW_IN_LPREVIEW )
  1440. return;
  1441. }
  1442. MaterialPrimitiveType_t type = (mode == RENDER_MODE_WIREFRAME) ?
  1443. MATERIAL_LINE_LOOP : MATERIAL_POLYGON;
  1444. CMeshBuilder meshBuilder;
  1445. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  1446. IMesh *pMesh = pRenderContext->GetDynamicMesh();
  1447. meshBuilder.Begin( pMesh, type, nPoints );
  1448. for (int nPoint = 0; nPoint < nPoints; nPoint++)
  1449. {
  1450. if (ModeUsesTextureCoords(mode))
  1451. {
  1452. meshBuilder.TexCoord2f( 0, m_pTextureCoords[nPoint][0], m_pTextureCoords[nPoint][1] );
  1453. meshBuilder.TexCoord2f( 1, m_pLightmapCoords[nPoint][0], m_pLightmapCoords[nPoint][1]);
  1454. }
  1455. meshBuilder.Color4ubv( (byte*)&pColor );
  1456. // transform into absolute space
  1457. if ( hasParent )
  1458. {
  1459. Vector point;
  1460. VectorTransform( Points[nPoint], frame.As3x4(), point );
  1461. meshBuilder.Position3f(point[0], point[1], point[2]);
  1462. }
  1463. else
  1464. {
  1465. meshBuilder.Position3f(Points[nPoint][0], Points[nPoint][1], Points[nPoint][2]);
  1466. }
  1467. // FIXME: no smoothing group information
  1468. meshBuilder.Normal3fv(plane.normal.Base());
  1469. meshBuilder.TangentS3fv( m_pTangentAxes[nPoint].tangent.Base() );
  1470. meshBuilder.TangentT3fv( m_pTangentAxes[nPoint].binormal.Base() );
  1471. meshBuilder.AdvanceVertex();
  1472. }
  1473. meshBuilder.End();
  1474. pMesh->Draw();
  1475. }
  1476. //-----------------------------------------------------------------------------
  1477. // Renders the grid on the face
  1478. //-----------------------------------------------------------------------------
  1479. void CMapFace::RenderGridIfCloseEnough( CRender3D* pRender )
  1480. {
  1481. CMapFace *pThis = this;
  1482. RenderGridsIfCloseEnough( pRender, 1, &pThis );
  1483. }
  1484. //-----------------------------------------------------------------------------
  1485. // renders the texture axes
  1486. //-----------------------------------------------------------------------------
  1487. void CMapFace::RenderTextureAxes( CRender3D* pRender )
  1488. {
  1489. CMapFace *pThis = this;
  1490. RenderTextureAxes( pRender, 1, &pThis );
  1491. }
  1492. //-----------------------------------------------------------------------------
  1493. // for sorting
  1494. //-----------------------------------------------------------------------------
  1495. bool CMapFace::ShouldRenderLast()
  1496. {
  1497. if (!m_pTexture || !m_pTexture->GetMaterial())
  1498. return false;
  1499. return m_pTexture->GetMaterial()->IsTranslucent() || (m_uchAlpha != 255) ;
  1500. }
  1501. //-----------------------------------------------------------------------------
  1502. // render texture axes
  1503. //-----------------------------------------------------------------------------
  1504. void CMapFace::RenderTextureAxes( CRender3D* pRender, int nCount, CMapFace **ppFaces )
  1505. {
  1506. // Render the world axes.
  1507. pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
  1508. CMeshBuilder meshBuilder;
  1509. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  1510. IMesh *pMesh = pRenderContext->GetDynamicMesh();
  1511. meshBuilder.Begin( pMesh, MATERIAL_LINES, 2 * nCount );
  1512. Vector Center;
  1513. for ( int i = 0; i < nCount; ++i )
  1514. {
  1515. ppFaces[i]->GetCenter(Center);
  1516. meshBuilder.Color3ub(255, 255, 0);
  1517. meshBuilder.Position3f(Center[0], Center[1], Center[2]);
  1518. meshBuilder.AdvanceVertex();
  1519. meshBuilder.Color3ub(255, 255, 0);
  1520. meshBuilder.Position3f(Center[0] + ppFaces[i]->texture.UAxis[0] * TEXTURE_AXIS_LENGTH,
  1521. Center[1] + ppFaces[i]->texture.UAxis[1] * TEXTURE_AXIS_LENGTH,
  1522. Center[2] + ppFaces[i]->texture.UAxis[2] * TEXTURE_AXIS_LENGTH);
  1523. meshBuilder.AdvanceVertex();
  1524. meshBuilder.Color3ub(0, 255, 0);
  1525. meshBuilder.Position3f(Center[0], Center[1], Center[2]);
  1526. meshBuilder.AdvanceVertex();
  1527. meshBuilder.Color3ub(0, 255, 0);
  1528. meshBuilder.Position3f(Center[0] + ppFaces[i]->texture.VAxis[0] * TEXTURE_AXIS_LENGTH,
  1529. Center[1] + ppFaces[i]->texture.VAxis[1] * TEXTURE_AXIS_LENGTH,
  1530. Center[2] + ppFaces[i]->texture.VAxis[2] * TEXTURE_AXIS_LENGTH);
  1531. meshBuilder.AdvanceVertex();
  1532. }
  1533. meshBuilder.End();
  1534. pMesh->Draw();
  1535. pRender->PopRenderMode();
  1536. }
  1537. //-----------------------------------------------------------------------------
  1538. // Render grids
  1539. //-----------------------------------------------------------------------------
  1540. void CMapFace::Render3DGrids( CRender3D *pRender, int nCount, CMapFace **ppFaces )
  1541. {
  1542. // FIXME: Optimize this to render all of them in a single call
  1543. for ( int i = 0; i < nCount; ++i )
  1544. {
  1545. ppFaces[i]->Render3DGrid( pRender );
  1546. }
  1547. }
  1548. //-----------------------------------------------------------------------------
  1549. // Render grids
  1550. //-----------------------------------------------------------------------------
  1551. void CMapFace::RenderGridsIfCloseEnough( CRender3D* pRender, int nCount, CMapFace **ppFaces )
  1552. {
  1553. // If the 3D grid is enabled and we aren't picking,
  1554. // render the grid on this face.
  1555. if ( (!pRender->IsEnabled(RENDER_GRID)) || pRender->IsPicking() )
  1556. return;
  1557. Vector Maxs;
  1558. Vector Mins;
  1559. float fGridSize = pRender->GetGridDistance();
  1560. Vector viewPoint; pRender->GetCamera()->GetViewPoint( viewPoint );
  1561. CMapFace **ppFinalList = (CMapFace**)_alloca( nCount * sizeof(CMapFace*) );
  1562. int nFinalCount = 0;
  1563. for ( int i = 0; i < nCount; ++i )
  1564. {
  1565. ppFaces[i]->GetFaceBounds(Mins, Maxs);
  1566. for ( int j = 0; j < 3; j++)
  1567. {
  1568. Mins[j] -= fGridSize;
  1569. Maxs[j] += fGridSize;
  1570. }
  1571. // Only render the grid if the face is close enough to the camera.
  1572. if ( IsPointInBox(viewPoint, Mins, Maxs) )
  1573. {
  1574. ppFinalList[nFinalCount++] = ppFaces[i];
  1575. }
  1576. }
  1577. Render3DGrids( pRender, nFinalCount, ppFinalList );
  1578. }
  1579. //-----------------------------------------------------------------------------
  1580. // Adds a face's vertices to the meshbuilder
  1581. //-----------------------------------------------------------------------------
  1582. void CMapFace::AddFaceVertices( CMeshBuilder &meshBuilder, CRender3D* pRender, bool bRenderSelected, SelectionState_t faceSelectionState)
  1583. {
  1584. Vector point;
  1585. VMatrix frame;
  1586. Color color;
  1587. bool bHasParent = GetTransformMatrix( frame );
  1588. ComputeColor( pRender, bRenderSelected, faceSelectionState, m_bIgnoreLighting, color );
  1589. for ( int nPoint = 0; nPoint < nPoints; nPoint++ )
  1590. {
  1591. if ( bHasParent )
  1592. {
  1593. // transform into absolute space
  1594. VectorTransform( Points[nPoint], frame.As3x4(), point );
  1595. meshBuilder.Position3fv( point.Base() );
  1596. }
  1597. else
  1598. {
  1599. meshBuilder.Position3fv( Points[nPoint].Base() );
  1600. }
  1601. meshBuilder.Normal3fv( plane.normal.Base() );
  1602. meshBuilder.Color4ubv( (byte*)&color );
  1603. meshBuilder.TexCoord2fv( 0, m_pTextureCoords[nPoint].Base() );
  1604. meshBuilder.TexCoord2fv( 1, m_pLightmapCoords[nPoint].Base() );
  1605. meshBuilder.TangentS3fv( m_pTangentAxes[nPoint].tangent.Base() );
  1606. meshBuilder.TangentT3fv( m_pTangentAxes[nPoint].binormal.Base() );
  1607. meshBuilder.AdvanceVertex();
  1608. }
  1609. }
  1610. struct MapFaceRender_t
  1611. {
  1612. bool m_RenderSelected;
  1613. EditorRenderMode_t m_RenderMode;
  1614. IEditorTexture* m_pTexture;
  1615. CMapFace* m_pMapFace;
  1616. SelectionState_t m_FaceSelectionState;
  1617. };
  1618. typedef CUtlRBTree<MapFaceRender_t, int> FaceQueue_t;
  1619. //-----------------------------------------------------------------------------
  1620. // draws a list of faces in wireframe
  1621. //-----------------------------------------------------------------------------
  1622. void CMapFace::RenderWireframeFaces( CRender3D* pRender, int nCount, MapFaceRender_t **ppFaces )
  1623. {
  1624. // Draw the texture axes
  1625. int nAxesCount = 0;
  1626. CMapFace **ppAxesFaces = (CMapFace**)_alloca( nCount * sizeof(CMapFace*) );
  1627. for ( int i = 0; i < nCount; ++i )
  1628. {
  1629. if ( ppFaces[i]->m_FaceSelectionState != SELECT_NONE )
  1630. {
  1631. ppAxesFaces[ nAxesCount++ ] = ppFaces[i]->m_pMapFace;
  1632. }
  1633. }
  1634. if ( nAxesCount != 0 )
  1635. {
  1636. RenderTextureAxes( pRender, nAxesCount, ppAxesFaces );
  1637. }
  1638. if ( pRender->IsEnabled(RENDER_GRID) )
  1639. {
  1640. // Draw the grid
  1641. CMapFace **ppGridFaces = (CMapFace**)_alloca( nCount * sizeof(CMapFace*) );
  1642. for ( int i = 0; i < nCount; ++i )
  1643. {
  1644. ppGridFaces[i] = ppFaces[i]->m_pMapFace;
  1645. }
  1646. RenderGridsIfCloseEnough( pRender, nCount, ppGridFaces );
  1647. }
  1648. }
  1649. //-----------------------------------------------------------------------------
  1650. // Draws a batch of faces.
  1651. //-----------------------------------------------------------------------------
  1652. void CMapFace::RenderFacesBatch( CMeshBuilder &meshBuilder, IMesh* pMesh, CRender3D* pRender, MapFaceRender_t **ppFaces, int nFaceCount, int nVertexCount, int nIndexCount, bool bWireframe )
  1653. {
  1654. if ( bWireframe )
  1655. {
  1656. meshBuilder.Begin( pMesh, MATERIAL_LINES, nVertexCount, nIndexCount );
  1657. }
  1658. else
  1659. {
  1660. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertexCount, nIndexCount );
  1661. }
  1662. int nFirstVertex = 0;
  1663. for ( int i = 0; i < nFaceCount; ++i )
  1664. {
  1665. CMapFace *pMapFace = ppFaces[i]->m_pMapFace;
  1666. pMapFace->AddFaceVertices( meshBuilder, pRender, ppFaces[i]->m_RenderSelected, ppFaces[i]->m_FaceSelectionState );
  1667. int nPoints = pMapFace->GetPointCount();
  1668. if ( bWireframe )
  1669. {
  1670. meshBuilder.FastIndex( nFirstVertex );
  1671. for ( int j = 1; j < nPoints; ++j )
  1672. {
  1673. meshBuilder.FastIndex( nFirstVertex + j );
  1674. meshBuilder.FastIndex( nFirstVertex + j );
  1675. }
  1676. meshBuilder.FastIndex( nFirstVertex );
  1677. }
  1678. else
  1679. {
  1680. for ( int j = 2; j < nPoints; ++j )
  1681. {
  1682. meshBuilder.FastIndex( nFirstVertex );
  1683. meshBuilder.FastIndex( nFirstVertex + j - 1 );
  1684. meshBuilder.FastIndex( nFirstVertex + j );
  1685. }
  1686. }
  1687. nFirstVertex += nPoints;
  1688. }
  1689. meshBuilder.End();
  1690. pMesh->Draw();
  1691. }
  1692. //-----------------------------------------------------------------------------
  1693. // Draws a list of faces, breaking them up into batches if necessary.
  1694. //-----------------------------------------------------------------------------
  1695. void CMapFace::RenderFaces( CRender3D* pRender, int nCount, MapFaceRender_t **ppFaces )
  1696. {
  1697. if ( nCount == 0 )
  1698. return;
  1699. bool bWireframe = ppFaces[0]->m_RenderMode == RENDER_MODE_WIREFRAME;
  1700. if ( RenderingModeIsTextured(ppFaces[0]->m_RenderMode))
  1701. {
  1702. pRender->BindTexture( ppFaces[0]->m_pTexture );
  1703. }
  1704. pRender->PushRenderMode( ppFaces[0]->m_RenderMode );
  1705. int nBatchStart = 0;
  1706. int nIndexCount = 0;
  1707. int nVertexCount = 0;
  1708. int nMaxVerts, nMaxIndices;
  1709. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  1710. IMesh *pMesh = pRenderContext->GetDynamicMesh();
  1711. pRenderContext->GetMaxToRender( pMesh, true, &nMaxVerts, &nMaxIndices );
  1712. // Make sure we have enough for at least one triangle...
  1713. int nMinVerts = ppFaces[0]->m_pMapFace->GetPointCount();
  1714. int nMinIndices = max( nMinVerts*2, (nMinVerts-2)*3 );
  1715. if ( nMaxVerts < nMinVerts || nMaxIndices < nMinIndices )
  1716. {
  1717. pRenderContext->GetMaxToRender( pMesh, false, &nMaxVerts, &nMaxIndices );
  1718. }
  1719. CMeshBuilder meshBuilder;
  1720. for ( int nFace = 0; nFace < nCount; nFace++ )
  1721. {
  1722. Assert( ppFaces[nFace]->m_RenderMode == ppFaces[0]->m_RenderMode );
  1723. Assert( ppFaces[nFace]->m_pTexture == ppFaces[0]->m_pTexture );
  1724. int newIndices, newVertices = ppFaces[nFace]->m_pMapFace->GetPointCount();
  1725. if( bWireframe )
  1726. {
  1727. newIndices = newVertices*2;
  1728. }
  1729. else
  1730. {
  1731. newIndices = (newVertices-2) * 3;
  1732. }
  1733. if ( ( ( nVertexCount + newVertices ) > nMaxVerts ) || ( ( nIndexCount + newIndices ) > nMaxIndices ) )
  1734. {
  1735. // If we hit this assert, there's a single face that's too big for the meshbuilder to handle!
  1736. Assert( ( nFace - nBatchStart ) > 0 );
  1737. // We have a full batch, render it.
  1738. RenderFacesBatch( meshBuilder, pMesh, pRender, &ppFaces[nBatchStart], nFace - nBatchStart, nVertexCount, nIndexCount, bWireframe );
  1739. pRenderContext->GetMaxToRender( pMesh, false, &nMaxVerts, &nMaxIndices );
  1740. nBatchStart = nFace;
  1741. nVertexCount = 0;
  1742. nIndexCount = 0;
  1743. }
  1744. nVertexCount += newVertices;
  1745. nIndexCount += newIndices;
  1746. }
  1747. // Render whatever is left over.
  1748. RenderFacesBatch( meshBuilder, pMesh, pRender, &ppFaces[nBatchStart], nCount - nBatchStart, nVertexCount, nIndexCount, bWireframe );
  1749. //render additional wireframe stuff
  1750. if ( bWireframe )
  1751. {
  1752. RenderWireframeFaces( pRender, nCount, ppFaces );
  1753. }
  1754. pRender->PopRenderMode();
  1755. }
  1756. //-----------------------------------------------------------------------------
  1757. // draws a single face (displacement or normal)
  1758. //-----------------------------------------------------------------------------
  1759. void CMapFace::RenderFace3D( CRender3D* pRender, EditorRenderMode_t renderMode, bool renderSelected, SelectionState_t faceSelectionState )
  1760. {
  1761. pRender->PushRenderMode( renderMode );
  1762. if ( HasDisp() && CMapDoc::GetActiveMapDoc() && CMapDoc::GetActiveMapDoc()->IsDispDraw3D() )
  1763. {
  1764. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  1765. pDisp->Render3D( pRender, renderSelected, faceSelectionState );
  1766. }
  1767. else
  1768. {
  1769. Color color;
  1770. ComputeColor( pRender, renderSelected, faceSelectionState, m_bIgnoreLighting, color );
  1771. DrawFace( color, renderMode );
  1772. }
  1773. // Draw the texture axes
  1774. if( renderMode == RENDER_MODE_WIREFRAME )
  1775. {
  1776. if (faceSelectionState != SELECT_NONE)
  1777. RenderTextureAxes(pRender);
  1778. // Draw the grid
  1779. RenderGridIfCloseEnough( pRender );
  1780. }
  1781. else if ( m_pDetailObjects && Options.general.bShowDetailObjects )
  1782. {
  1783. // Only draw the detailed objects if the displacement/face is not currently selected.
  1784. pRender->AddTranslucentDeferredRendering( m_pDetailObjects );
  1785. }
  1786. pRender->PopRenderMode();
  1787. }
  1788. //-----------------------------------------------------------------------------
  1789. // Purpose:
  1790. // Input : mode -
  1791. //-----------------------------------------------------------------------------
  1792. static int SortVal(EditorRenderMode_t mode)
  1793. {
  1794. if (mode == RENDER_MODE_WIREFRAME)
  1795. return 2;
  1796. if ( mode == RENDER_MODE_SELECTION_OVERLAY )
  1797. return 1;
  1798. return 0;
  1799. }
  1800. //-----------------------------------------------------------------------------
  1801. // Purpose:
  1802. // Input : s1 -
  1803. // s2 -
  1804. // Output :
  1805. //-----------------------------------------------------------------------------
  1806. static bool OpaqueFacesLessFunc( const MapFaceRender_t &s1, const MapFaceRender_t &s2 )
  1807. {
  1808. // Render texture first, overlay second, wireframe 3rd
  1809. int nSort1 = SortVal(s1.m_RenderMode);
  1810. int nSort2 = SortVal(s2.m_RenderMode);
  1811. if (nSort1 < nSort2)
  1812. return true;
  1813. if (nSort1 > nSort2)
  1814. return false;
  1815. return s1.m_pTexture < s2.m_pTexture;
  1816. }
  1817. static FaceQueue_t g_OpaqueFaces(0, 0, OpaqueFacesLessFunc);
  1818. static CUtlVector< FaceQueue_t * > g_OpaqueInstanceFaces;
  1819. static FaceQueue_t *g_CurrentOpaqueFaces = &g_OpaqueFaces;
  1820. //-----------------------------------------------------------------------------
  1821. // Purpose: this function will add the face to the sorted current queue
  1822. // Input : pMapFace - the face to be added
  1823. // pTexture - the texture of the face
  1824. // renderMode - what type of rendering mode
  1825. // selected - if it is selected or not ( selected appears on top )
  1826. // faceSelectionState - if the face is individual selected
  1827. //-----------------------------------------------------------------------------
  1828. void CMapFace::AddFaceToQueue( CMapFace* pMapFace, IEditorTexture* pTexture, EditorRenderMode_t renderMode, bool selected, SelectionState_t faceSelectionState )
  1829. {
  1830. MapFaceRender_t newEntry;
  1831. newEntry.m_RenderMode = renderMode;
  1832. newEntry.m_pTexture = pTexture;
  1833. newEntry.m_RenderSelected = selected;
  1834. newEntry.m_pMapFace = pMapFace;
  1835. newEntry.m_FaceSelectionState = faceSelectionState;
  1836. g_CurrentOpaqueFaces->Insert( newEntry );
  1837. }
  1838. //-----------------------------------------------------------------------------
  1839. // Purpose: this function will add a new face queue to the top of the list and
  1840. // make it active
  1841. //-----------------------------------------------------------------------------
  1842. void CMapFace::PushFaceQueue( void )
  1843. {
  1844. g_OpaqueInstanceFaces.AddToHead( new FaceQueue_t( 0, 0, OpaqueFacesLessFunc ) );
  1845. g_CurrentOpaqueFaces = g_OpaqueInstanceFaces.Head();
  1846. }
  1847. //-----------------------------------------------------------------------------
  1848. // Purpose: this function will pop the top face queue off the list
  1849. //-----------------------------------------------------------------------------
  1850. void CMapFace::PopFaceQueue( void )
  1851. {
  1852. Assert( g_OpaqueInstanceFaces.Count() > 0 );
  1853. FaceQueue_t *pHead = g_OpaqueInstanceFaces.Head();
  1854. g_OpaqueInstanceFaces.Remove( 0 );
  1855. delete pHead;
  1856. if ( g_OpaqueInstanceFaces.Count() )
  1857. {
  1858. g_CurrentOpaqueFaces = g_OpaqueInstanceFaces.Head();
  1859. }
  1860. else
  1861. {
  1862. g_CurrentOpaqueFaces = &g_OpaqueFaces;
  1863. }
  1864. }
  1865. //-----------------------------------------------------------------------------
  1866. // renders queued up opaque faces, sorted by material
  1867. //-----------------------------------------------------------------------------
  1868. void CMapFace::RenderOpaqueFaces( CRender3D* pRender )
  1869. {
  1870. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  1871. MapFaceRender_t **ppMapFaces = (MapFaceRender_t**)_alloca( g_CurrentOpaqueFaces->Count() * sizeof( MapFaceRender_t* ) );
  1872. int nFaceCount = 0;
  1873. int nLastRenderMode = RENDER_MODE_NONE;
  1874. IEditorTexture *pLastTexture = NULL;
  1875. for ( int i = g_CurrentOpaqueFaces->FirstInorder(); i != g_CurrentOpaqueFaces->InvalidIndex(); i = g_CurrentOpaqueFaces->NextInorder(i) )
  1876. {
  1877. MapFaceRender_t& mapFace = ( *g_CurrentOpaqueFaces)[i];
  1878. if ( ( mapFace.m_RenderMode != nLastRenderMode ) || ( mapFace.m_pTexture != pLastTexture ) )
  1879. {
  1880. RenderFaces( pRender, nFaceCount, ppMapFaces );
  1881. nFaceCount = 0;
  1882. }
  1883. if ( mapFace.m_pMapFace->HasDisp() )
  1884. {
  1885. if ( RenderingModeIsTextured( mapFace.m_RenderMode ))
  1886. {
  1887. pRender->BindTexture( mapFace.m_pTexture );
  1888. }
  1889. mapFace.m_pMapFace->RenderFace3D( pRender, mapFace.m_RenderMode, mapFace.m_RenderSelected, mapFace.m_FaceSelectionState );
  1890. }
  1891. else
  1892. {
  1893. ppMapFaces[ nFaceCount++ ] = &mapFace;
  1894. nLastRenderMode = mapFace.m_RenderMode;
  1895. pLastTexture = mapFace.m_pTexture;
  1896. }
  1897. }
  1898. RenderFaces( pRender, nFaceCount, ppMapFaces );
  1899. g_CurrentOpaqueFaces->RemoveAll();
  1900. }
  1901. void CMapFace::Render2D(CRender2D *pRender)
  1902. {
  1903. SelectionState_t eFaceSelectionState = GetSelectionState();
  1904. SelectionState_t eSolidSelectionState;
  1905. if (m_pParent != NULL)
  1906. {
  1907. eSolidSelectionState = m_pParent->GetSelectionState();
  1908. }
  1909. else
  1910. {
  1911. eSolidSelectionState = eFaceSelectionState;
  1912. }
  1913. bool bRenderSelected = ( eSolidSelectionState != SELECT_NONE );
  1914. bRenderSelected = bRenderSelected || ( ( eFaceSelectionState != SELECT_NONE ) && (CMapFace::m_bShowFaceSelection) );
  1915. Vector vViewNormal; pRender->GetCamera()->GetViewForward( vViewNormal );
  1916. Vector vNormal; GetFaceNormal( vNormal );
  1917. // if face is parallel to view axis, skip it
  1918. bool bIsParallel = ( fabs( vViewNormal.Dot( vNormal) ) < 0.0001f );
  1919. if ( HasDisp() && ( bIsParallel || bRenderSelected ) )
  1920. {
  1921. Vector mins,maxs;
  1922. GetRender2DBox( mins,maxs );
  1923. Vector2D pt,pt2;
  1924. pRender->TransformPoint(pt, mins );
  1925. pRender->TransformPoint(pt2, maxs );
  1926. int sizeX = abs(pt2.x-pt.x);
  1927. int sizeY = abs(pt2.y-pt.y);
  1928. bool bDrawDispMap = Options.view2d.bDrawModels && ( (sizeX+sizeY) > 50 || bRenderSelected );
  1929. if ( bDrawDispMap )
  1930. {
  1931. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  1932. pDisp->Render2D( pRender, bRenderSelected, eFaceSelectionState );
  1933. return;
  1934. }
  1935. }
  1936. if ( !bIsParallel )
  1937. {
  1938. pRender->DrawPolyLine( nPoints, Points );
  1939. }
  1940. }
  1941. void CMapFace::RenderVertices(CRender *pRender)
  1942. {
  1943. for ( int i=0; i< nPoints;i++ )
  1944. pRender->DrawHandle( Points[i] );
  1945. }
  1946. //-----------------------------------------------------------------------------
  1947. // Purpose: Renders this face using the given 3D renderer.
  1948. // Input : pRender - Renderer to draw with.
  1949. //-----------------------------------------------------------------------------
  1950. void CMapFace::Render3D( CRender3D *pRender )
  1951. {
  1952. if (nPoints == 0)
  1953. {
  1954. return;
  1955. }
  1956. //
  1957. // Skip back faces unless rendering in wireframe.
  1958. //
  1959. EditorRenderMode_t eCurrentRenderMode = pRender->GetCurrentRenderMode();
  1960. if ( eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED )
  1961. {
  1962. if ( m_nFaceFlags & FACE_FLAGS_NODRAW_IN_LPREVIEW )
  1963. return;
  1964. }
  1965. SelectionState_t eFaceSelectionState = GetSelectionState();
  1966. SelectionState_t eSolidSelectionState;
  1967. if (m_pParent != NULL)
  1968. {
  1969. eSolidSelectionState = m_pParent->GetSelectionState();
  1970. }
  1971. else
  1972. {
  1973. eSolidSelectionState = eFaceSelectionState;
  1974. }
  1975. if ( !Options.general.bShowNoDrawBrushes && eSolidSelectionState == SELECT_NONE && m_pTexture == g_Textures.GetNoDrawTexture() )
  1976. return;
  1977. //
  1978. // Draw the face.
  1979. //
  1980. bool renderSelected = ( ( eSolidSelectionState != SELECT_NONE ) );
  1981. renderSelected = renderSelected || ( ( eFaceSelectionState != SELECT_NONE ) && (CMapFace::m_bShowFaceSelection) );
  1982. if (pRender->DeferRendering())
  1983. {
  1984. AddFaceToQueue( this, m_pTexture, eCurrentRenderMode, renderSelected, eFaceSelectionState );
  1985. if ( ( renderSelected && pRender->NeedsOverlay() ) )
  1986. {
  1987. AddFaceToQueue( this, m_pTexture, RENDER_MODE_SELECTION_OVERLAY, renderSelected, eFaceSelectionState );
  1988. }
  1989. }
  1990. else
  1991. {
  1992. // Set up the texture to use
  1993. pRender->BindTexture( m_pTexture );
  1994. RenderFace3D( pRender, eCurrentRenderMode, renderSelected, eFaceSelectionState );
  1995. if ( ( renderSelected && pRender->NeedsOverlay() ) )
  1996. {
  1997. RenderFace3D( pRender, RENDER_MODE_SELECTION_OVERLAY, renderSelected, eFaceSelectionState );
  1998. }
  1999. }
  2000. }
  2001. //-----------------------------------------------------------------------------
  2002. // Purpose: Renders the world grid projected onto the given face.
  2003. // Input : pFace - The face onto which the grid will be projected.
  2004. //-----------------------------------------------------------------------------
  2005. void CMapFace::Render3DGrid(CRender3D *pRender)
  2006. {
  2007. //
  2008. // Determine the extents of this face.
  2009. //
  2010. Extents_t Extents;
  2011. float fDelta[3];
  2012. float fGridSpacing = pRender->GetGridSize();
  2013. GetFaceExtents(Extents);
  2014. fDelta[0] = Extents[EXTENTS_XMAX][0] - Extents[EXTENTS_XMIN][0];
  2015. fDelta[1] = Extents[EXTENTS_YMAX][1] - Extents[EXTENTS_YMIN][1];
  2016. fDelta[2] = Extents[EXTENTS_ZMAX][2] - Extents[EXTENTS_ZMIN][2];
  2017. //
  2018. // Render the grid lines with wireframe material.
  2019. //
  2020. pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
  2021. CMeshBuilder meshBuilder;
  2022. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  2023. IMesh *pMesh = pRenderContext->GetDynamicMesh();
  2024. //
  2025. // For every dimension in which this face has a nonzero projection.
  2026. //
  2027. for (int nDim = 0; nDim < 3; nDim++)
  2028. {
  2029. if (fDelta[nDim] != 0)
  2030. {
  2031. Vector Normal;
  2032. Normal[0] = (float)((nDim % 3) == 0);
  2033. Normal[1] = (float)((nDim % 3) == 1);
  2034. Normal[2] = (float)((nDim % 3) == 2);
  2035. float fMin = Extents[nDim * 2][nDim];
  2036. float fMax = Extents[(nDim * 2) + 1][nDim];
  2037. float fStart = (float)(floor(fMin / fGridSpacing) * fGridSpacing);
  2038. float fEnd = (float)(ceil(fMax / fGridSpacing) * fGridSpacing);
  2039. float fGridPoint = fStart;
  2040. while (fGridPoint < fEnd)
  2041. {
  2042. int nPointsFound = 0;
  2043. //
  2044. // For every edge.
  2045. //
  2046. for (int nPoint = 0; nPoint < nPoints; nPoint++)
  2047. {
  2048. Vector PointFound[2];
  2049. //
  2050. // Get the start and end points of the edge.
  2051. //
  2052. Vector Point1 = Points[nPoint];
  2053. Vector Point2;
  2054. if (nPoint < nPoints - 1)
  2055. {
  2056. Point2 = Points[nPoint + 1];
  2057. }
  2058. else
  2059. {
  2060. Point2 = Points[0];
  2061. }
  2062. //
  2063. // If there is a projection of the normal vector along this edge.
  2064. //
  2065. if (Point2[nDim] != Point1[nDim])
  2066. {
  2067. //
  2068. // Solve for the point along this edge that intersects the grid line
  2069. // as a parameter from zero to one.
  2070. //
  2071. float fScale = (fGridPoint - Point1[nDim]) / (Point2[nDim] - Point1[nDim]);
  2072. if ((fScale >= 0) && (fScale <= 1))
  2073. {
  2074. PointFound[nPointsFound][0] = Point1[0] + (Point2[0] - Point1[0]) * fScale;
  2075. PointFound[nPointsFound][1] = Point1[1] + (Point2[1] - Point1[1]) * fScale;
  2076. PointFound[nPointsFound][2] = Point1[2] + (Point2[2] - Point1[2]) * fScale;
  2077. nPointsFound++;
  2078. if (nPointsFound == 2)
  2079. {
  2080. Vector RenderPoint;
  2081. meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );
  2082. VectorMA(PointFound[0], 0.2, plane.normal, RenderPoint);
  2083. meshBuilder.Position3f(RenderPoint[0], RenderPoint[1], RenderPoint[2]);
  2084. meshBuilder.Color3ub(Normal[0] * 255, Normal[1] * 255, Normal[2] * 255);
  2085. meshBuilder.AdvanceVertex();
  2086. VectorMA(PointFound[1], 0.2, plane.normal, RenderPoint);
  2087. meshBuilder.Position3f(RenderPoint[0], RenderPoint[1], RenderPoint[2]);
  2088. meshBuilder.Color3ub(Normal[0] * 255, Normal[1] * 255, Normal[2] * 255);
  2089. meshBuilder.AdvanceVertex();
  2090. meshBuilder.End();
  2091. pMesh->Draw();
  2092. nPointsFound = 0;
  2093. }
  2094. }
  2095. }
  2096. }
  2097. fGridPoint += fGridSpacing;
  2098. }
  2099. }
  2100. }
  2101. pRender->PopRenderMode();
  2102. }
  2103. //-----------------------------------------------------------------------------
  2104. // Purpose:
  2105. // Input : pLoadInfo -
  2106. // pFace -
  2107. // Output : ChunkFileResult_t
  2108. //-----------------------------------------------------------------------------
  2109. ChunkFileResult_t CMapFace::LoadDispInfoCallback(CChunkFile *pFile, CMapFace *pFace)
  2110. {
  2111. SignalUpdate( EVTYPE_FACE_CHANGED );
  2112. // allocate a displacement (for the face)
  2113. EditDispHandle_t dispHandle = EditDispMgr()->Create();
  2114. CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );
  2115. //
  2116. // load the displacement info and set relationships
  2117. //
  2118. ChunkFileResult_t eResult = pDisp->LoadVMF( pFile );
  2119. if( eResult == ChunkFile_Ok )
  2120. {
  2121. pDisp->SetParent( pFace );
  2122. pFace->SetDisp( dispHandle );
  2123. CMapWorld *pWorld = GetActiveWorld();
  2124. if( pWorld )
  2125. {
  2126. IWorldEditDispMgr *pDispMgr = pWorld->GetWorldEditDispManager();
  2127. if( pDispMgr )
  2128. {
  2129. pDispMgr->AddToWorld( dispHandle );
  2130. }
  2131. }
  2132. }
  2133. return( eResult );
  2134. }
  2135. //-----------------------------------------------------------------------------
  2136. // Purpose: Handles key values when loading a VMF file.
  2137. // Input : szKey - Key being handled.
  2138. // szValue - Value of the key in the VMF file.
  2139. // Output : Returns ChunkFile_Ok or an error if there was a parsing error.
  2140. //-----------------------------------------------------------------------------
  2141. ChunkFileResult_t CMapFace::LoadKeyCallback(const char *szKey, const char *szValue, LoadFace_t *pLoadFace)
  2142. {
  2143. SignalUpdate( EVTYPE_FACE_CHANGED );
  2144. CMapFace *pFace = pLoadFace->pFace;
  2145. if (!stricmp(szKey, "id"))
  2146. {
  2147. CChunkFile::ReadKeyValueInt(szValue, pFace->m_nFaceID);
  2148. }
  2149. else if (!stricmp(szKey, "rotation"))
  2150. {
  2151. pFace->texture.rotate = atof(szValue);
  2152. }
  2153. else if (!stricmp(szKey, "plane"))
  2154. {
  2155. int nRead = sscanf(szValue, "(%f %f %f) (%f %f %f) (%f %f %f)",
  2156. &pFace->plane.planepts[0][0], &pFace->plane.planepts[0][1], &pFace->plane.planepts[0][2],
  2157. &pFace->plane.planepts[1][0], &pFace->plane.planepts[1][1], &pFace->plane.planepts[1][2],
  2158. &pFace->plane.planepts[2][0], &pFace->plane.planepts[2][1], &pFace->plane.planepts[2][2]);
  2159. if (nRead != 9)
  2160. {
  2161. // TODO: need specific error message
  2162. return(ChunkFile_Fail);
  2163. }
  2164. }
  2165. else if (!stricmp(szKey, "material"))
  2166. {
  2167. strcpy(pLoadFace->szTexName, szValue);
  2168. }
  2169. else if (!stricmp(szKey, "uaxis"))
  2170. {
  2171. int nRead = sscanf(szValue, "[%f %f %f %f] %f",
  2172. &pFace->texture.UAxis[0], &pFace->texture.UAxis[1], &pFace->texture.UAxis[2], &pFace->texture.UAxis[3], &pFace->texture.scale[0]);
  2173. if (nRead != 5)
  2174. {
  2175. // TODO: need specific error message
  2176. return(ChunkFile_Fail);
  2177. }
  2178. }
  2179. else if (!stricmp(szKey, "vaxis"))
  2180. {
  2181. int nRead = sscanf(szValue, "[%f %f %f %f] %f",
  2182. &pFace->texture.VAxis[0], &pFace->texture.VAxis[1], &pFace->texture.VAxis[2], &pFace->texture.VAxis[3], &pFace->texture.scale[1]);
  2183. if (nRead != 5)
  2184. {
  2185. // TODO: need specific error message
  2186. return(ChunkFile_Fail);
  2187. }
  2188. }
  2189. else if (!stricmp(szKey, "lightmapscale"))
  2190. {
  2191. pFace->texture.nLightmapScale = atoi(szValue);
  2192. }
  2193. else if (!stricmp(szKey, "smoothing_groups"))
  2194. {
  2195. pFace->m_fSmoothingGroups = atoi(szValue);
  2196. }
  2197. return(ChunkFile_Ok);
  2198. }
  2199. //-----------------------------------------------------------------------------
  2200. // Purpose: Loads a face chunk from the VMF file.
  2201. // Input : pFile - Chunk file being loaded.
  2202. // Output : Returns ChunkFile_Ok or an error if there was a parsing error.
  2203. //-----------------------------------------------------------------------------
  2204. ChunkFileResult_t CMapFace::LoadVMF(CChunkFile *pFile)
  2205. {
  2206. SignalUpdate( EVTYPE_FACE_CHANGED );
  2207. //
  2208. // Set up handlers for the subchunks that we are interested in.
  2209. //
  2210. CChunkHandlerMap Handlers;
  2211. Handlers.AddHandler("dispinfo", (ChunkHandler_t)LoadDispInfoCallback, this);
  2212. //
  2213. // Read the keys and sub-chunks.
  2214. //
  2215. LoadFace_t LoadFace;
  2216. memset(&LoadFace, 0, sizeof(LoadFace));
  2217. LoadFace.pFace = this;
  2218. pFile->PushHandlers(&Handlers);
  2219. ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadKeyCallback, &LoadFace);
  2220. pFile->PopHandlers();
  2221. if (eResult == ChunkFile_Ok)
  2222. {
  2223. CalcPlane();
  2224. SetTexture(LoadFace.szTexName);
  2225. }
  2226. return(eResult);
  2227. }
  2228. //-----------------------------------------------------------------------------
  2229. // Purpose: Called after this object is added to the world.
  2230. //
  2231. // NOTE: This function is NOT called during serialization. Use PostloadWorld
  2232. // to do similar bookkeeping after map load.
  2233. //
  2234. // Input : pWorld - The world that we have been added to.
  2235. //-----------------------------------------------------------------------------
  2236. void CMapFace::OnAddToWorld(CMapWorld *pWorld)
  2237. {
  2238. SignalUpdate( EVTYPE_FACE_CHANGED );
  2239. if (HasDisp())
  2240. {
  2241. //
  2242. // Add it to the world displacement list.
  2243. //
  2244. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
  2245. if (pDispMgr != NULL)
  2246. {
  2247. pDispMgr->AddToWorld( m_DispHandle );
  2248. }
  2249. }
  2250. }
  2251. //-----------------------------------------------------------------------------
  2252. // Purpose: Called just after this object has been removed from the world so
  2253. // that it can unlink itself from other objects in the world.
  2254. // Input : pWorld - The world that we were just removed from.
  2255. // bNotifyChildren - Whether we should forward notification to our children.
  2256. //-----------------------------------------------------------------------------
  2257. void CMapFace::OnRemoveFromWorld(void)
  2258. {
  2259. SignalUpdate( EVTYPE_FACE_CHANGED );
  2260. if (HasDisp())
  2261. {
  2262. //
  2263. // Add it to the world displacement list.
  2264. //
  2265. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
  2266. if (pDispMgr != NULL)
  2267. {
  2268. pDispMgr->RemoveFromWorld( m_DispHandle );
  2269. }
  2270. }
  2271. }
  2272. //-----------------------------------------------------------------------------
  2273. // Purpose:
  2274. // Input : *pFile -
  2275. // Output : ChunkFileResult_t
  2276. //-----------------------------------------------------------------------------
  2277. ChunkFileResult_t CMapFace::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo)
  2278. {
  2279. NormalizeTextureShifts();
  2280. //
  2281. // Check for duplicate plane points. All three plane points must be unique
  2282. // or it isn't a valid plane. Try to fix it if it isn't valid.
  2283. //
  2284. if (!CheckFace())
  2285. {
  2286. Fix();
  2287. }
  2288. ChunkFileResult_t eResult = pFile->BeginChunk("side");
  2289. char szBuf[512];
  2290. //
  2291. // Write our unique face ID.
  2292. //
  2293. if (eResult == ChunkFile_Ok)
  2294. {
  2295. eResult = pFile->WriteKeyValueInt("id", m_nFaceID);
  2296. }
  2297. //
  2298. // Write the plane information.
  2299. //
  2300. if (eResult == ChunkFile_Ok)
  2301. {
  2302. sprintf(szBuf, "(%g %g %g) (%g %g %g) (%g %g %g)",
  2303. (double)plane.planepts[0][0], (double)plane.planepts[0][1], (double)plane.planepts[0][2],
  2304. (double)plane.planepts[1][0], (double)plane.planepts[1][1], (double)plane.planepts[1][2],
  2305. (double)plane.planepts[2][0], (double)plane.planepts[2][1], (double)plane.planepts[2][2]);
  2306. eResult = pFile->WriteKeyValue("plane", szBuf);
  2307. }
  2308. if (eResult == ChunkFile_Ok)
  2309. {
  2310. char szTexture[MAX_PATH];
  2311. strcpy(szTexture, texture.texture);
  2312. strupr(szTexture);
  2313. eResult = pFile->WriteKeyValue("material", szTexture);
  2314. }
  2315. if (eResult == ChunkFile_Ok)
  2316. {
  2317. sprintf(szBuf, "[%g %g %g %g] %g", (double)texture.UAxis[0], (double)texture.UAxis[1], (double)texture.UAxis[2], (double)texture.UAxis[3], (double)texture.scale[0]);
  2318. eResult = pFile->WriteKeyValue("uaxis", szBuf);
  2319. }
  2320. if (eResult == ChunkFile_Ok)
  2321. {
  2322. sprintf(szBuf, "[%g %g %g %g] %g", (double)texture.VAxis[0], (double)texture.VAxis[1], (double)texture.VAxis[2], (double)texture.VAxis[3], (double)texture.scale[1]);
  2323. eResult = pFile->WriteKeyValue("vaxis", szBuf);
  2324. }
  2325. if (eResult == ChunkFile_Ok)
  2326. {
  2327. eResult = pFile->WriteKeyValueFloat("rotation", texture.rotate);
  2328. }
  2329. if (eResult == ChunkFile_Ok)
  2330. {
  2331. eResult = pFile->WriteKeyValueFloat("lightmapscale", texture.nLightmapScale);
  2332. }
  2333. // Save smoothing group data.
  2334. if (eResult == ChunkFile_Ok)
  2335. {
  2336. eResult = pFile->WriteKeyValueInt("smoothing_groups", m_fSmoothingGroups );
  2337. }
  2338. //
  2339. // Write the displacement chunk.
  2340. //
  2341. if ((eResult == ChunkFile_Ok) && (HasDisp()))
  2342. {
  2343. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  2344. eResult = pDisp->SaveVMF(pFile, pSaveInfo);
  2345. }
  2346. if (eResult == ChunkFile_Ok)
  2347. {
  2348. eResult = pFile->EndChunk();
  2349. }
  2350. return(eResult);
  2351. }
  2352. //-----------------------------------------------------------------------------
  2353. // Purpose: Enables or disables the special rendering of selected faces.
  2354. // Input : bShowSelection - true to enable, false to disable.
  2355. //-----------------------------------------------------------------------------
  2356. void CMapFace::SetShowSelection(bool bShowSelection)
  2357. {
  2358. CMapFace::m_bShowFaceSelection = bShowSelection;
  2359. }
  2360. //-----------------------------------------------------------------------------
  2361. // Purpose:
  2362. // Input : nPoint -
  2363. // u -
  2364. // v -
  2365. //-----------------------------------------------------------------------------
  2366. void CMapFace::SetTextureCoords(int nPoint, float u, float v)
  2367. {
  2368. if (nPoint < nPoints)
  2369. {
  2370. m_pTextureCoords[nPoint][0] = u;
  2371. m_pTextureCoords[nPoint][1] = v;
  2372. }
  2373. }
  2374. //-----------------------------------------------------------------------------
  2375. //-----------------------------------------------------------------------------
  2376. size_t CMapFace::GetDataSize( void )
  2377. {
  2378. // get base map class size
  2379. size_t size = sizeof( CMapFace );
  2380. //
  2381. // better approximate by added in verts, texture coordinates,
  2382. // and lightmap coordinates
  2383. //
  2384. size += ( sizeof( Vector ) * nPoints );
  2385. size += ( sizeof( Vector2D ) * ( nPoints * 2 ) );
  2386. // add displacement size if necessary
  2387. if( HasDisp() )
  2388. {
  2389. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  2390. size += pDisp->GetSize();
  2391. }
  2392. return size;
  2393. }
  2394. //-----------------------------------------------------------------------------
  2395. // Purpose: Returns our bounds for 2D rendering. These bounds do not consider
  2396. // any displacement information.
  2397. // Input : boundMin -
  2398. // boundMax -
  2399. // Output : Returns true on success, false on failure.
  2400. //-----------------------------------------------------------------------------
  2401. bool CMapFace::GetRender2DBox( Vector& boundMin, Vector& boundMax )
  2402. {
  2403. // valid face?
  2404. if( nPoints == 0 )
  2405. return false;
  2406. //
  2407. // find min and maximum points on face
  2408. //
  2409. VectorFill( boundMin, COORD_NOTINIT );
  2410. VectorFill( boundMax, -COORD_NOTINIT );
  2411. for( int i = 0; i < nPoints; i++ )
  2412. {
  2413. if( Points[i][0] < boundMin[0] ) { boundMin[0] = Points[i][0]; }
  2414. if( Points[i][1] < boundMin[1] ) { boundMin[1] = Points[i][1]; }
  2415. if( Points[i][2] < boundMin[2] ) { boundMin[2] = Points[i][2]; }
  2416. if( Points[i][0] > boundMax[0] ) { boundMax[0] = Points[i][0]; }
  2417. if( Points[i][1] > boundMax[1] ) { boundMax[1] = Points[i][1]; }
  2418. if( Points[i][2] > boundMax[2] ) { boundMax[2] = Points[i][2]; }
  2419. }
  2420. return true;
  2421. }
  2422. //-----------------------------------------------------------------------------
  2423. // Purpose: Returns our bounds for frustum culling, including the bounds of
  2424. // any displacement information.
  2425. // Input : boundMin -
  2426. // boundMax -
  2427. // Output : Returns true on success, false on failure.
  2428. //-----------------------------------------------------------------------------
  2429. bool CMapFace::GetCullBox( Vector& boundMin, Vector& boundMax )
  2430. {
  2431. // get the face bounds
  2432. if( !GetRender2DBox( boundMin, boundMax ) )
  2433. return false;
  2434. //
  2435. // add displacement to bounds
  2436. //
  2437. if( HasDisp() )
  2438. {
  2439. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  2440. Vector bbox[2];
  2441. pDisp->GetBoundingBox( bbox[0], bbox[1] );
  2442. for( int i = 0; i < 2; i++ )
  2443. {
  2444. if( bbox[i][0] < boundMin[0] ) { boundMin[0] = bbox[i][0]; }
  2445. if( bbox[i][1] < boundMin[1] ) { boundMin[1] = bbox[i][1]; }
  2446. if( bbox[i][2] < boundMin[2] ) { boundMin[2] = bbox[i][2]; }
  2447. if( bbox[i][0] > boundMax[0] ) { boundMax[0] = bbox[i][0]; }
  2448. if( bbox[i][1] > boundMax[1] ) { boundMax[1] = bbox[i][1]; }
  2449. if( bbox[i][2] > boundMax[2] ) { boundMax[2] = bbox[i][2]; }
  2450. }
  2451. }
  2452. return true;
  2453. }
  2454. //-----------------------------------------------------------------------------
  2455. // Purpose:
  2456. // Input : HitPos -
  2457. // Start -
  2458. // End -
  2459. // Output : Returns true if the ray intersected the face, false if not.
  2460. //-----------------------------------------------------------------------------
  2461. bool CMapFace::TraceLine(Vector &HitPos, Vector &HitNormal, Vector const &Start, Vector const &End )
  2462. {
  2463. if (HasDisp())
  2464. {
  2465. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  2466. return( pDisp->TraceLine( HitPos, HitNormal, Start, End ) );
  2467. }
  2468. //
  2469. // Find the point of intersection of the ray with the given plane.
  2470. //
  2471. float t = Start.Dot(plane.normal) - plane.dist;
  2472. float d = -(End - Start).Dot(plane.normal);
  2473. if ( d == 0.0f )
  2474. return false;
  2475. t = t / d;
  2476. HitPos = Start + (t * (End - Start));
  2477. HitNormal = plane.normal;
  2478. return(true);
  2479. }
  2480. //-----------------------------------------------------------------------------
  2481. //-----------------------------------------------------------------------------
  2482. bool CMapFace::TraceLineInside( Vector &HitPos, Vector &HitNormal,
  2483. Vector const &Start, Vector const &End, bool bNoDisp )
  2484. {
  2485. // if the face is displaced -- collide with that
  2486. if( HasDisp() && !bNoDisp )
  2487. {
  2488. CMapDisp *pDisp = EditDispMgr()->GetDisp( m_DispHandle );
  2489. return( pDisp->TraceLine( HitPos, HitNormal, Start, End ) );
  2490. }
  2491. //
  2492. // Find the point of intersection of the ray with the given plane.
  2493. //
  2494. float t = Start.Dot( plane.normal ) - plane.dist;
  2495. if ( -( End - Start ).Dot( plane.normal ) != 0.0f )
  2496. {
  2497. t = t / -( End - Start ).Dot( plane.normal );
  2498. }
  2499. HitPos = Start + ( t * ( End - Start ) );
  2500. //
  2501. // determine if the plane point lies behind all of the polygon planes (edges)
  2502. //
  2503. for( int ndxEdge = 0; ndxEdge < nPoints; ndxEdge++ )
  2504. {
  2505. // create the edge and cross it with the face plane normal
  2506. Vector edge;
  2507. edge = Points[(ndxEdge+1)%nPoints] - Points[ndxEdge];
  2508. PLANE edgePlane;
  2509. edgePlane.normal = edge.Cross( plane.normal );
  2510. VectorNormalize( edgePlane.normal );
  2511. edgePlane.dist = edgePlane.normal.Dot( Points[ndxEdge] );
  2512. // determine if the facing is correct
  2513. float dist = edgePlane.normal.Dot( Points[(ndxEdge+2)%nPoints] ) - edgePlane.dist;
  2514. if( dist > 0.0f )
  2515. {
  2516. // flip
  2517. edgePlane.normal.Negate();
  2518. edgePlane.dist = -edgePlane.dist;
  2519. }
  2520. // check to see if plane point lives behind the plane
  2521. dist = edgePlane.normal.Dot( HitPos ) - edgePlane.dist;
  2522. if( dist > 0.0f )
  2523. return false;
  2524. }
  2525. return true;
  2526. }
  2527. //-----------------------------------------------------------------------------
  2528. // NOTE: actually this could be calculated once for the face since only the
  2529. // face normal is being used (no smoothing groups, etc.), but that may
  2530. // change????
  2531. //-----------------------------------------------------------------------------
  2532. void CMapFace::CalcTangentSpaceAxes( void )
  2533. {
  2534. // destroy old axes if need be
  2535. FreeTangentSpaceAxes();
  2536. // allocate memory for tangent space axes
  2537. if( !AllocTangentSpaceAxes( nPoints ) )
  2538. return;
  2539. //
  2540. // get the texture space axes
  2541. //
  2542. Vector4D& uVect = texture.UAxis;
  2543. Vector4D& vVect = texture.VAxis;
  2544. //
  2545. // calculate the tangent space per polygon point
  2546. //
  2547. for( int ptIndex = 0; ptIndex < nPoints; ptIndex++ )
  2548. {
  2549. // get the current tangent space axes
  2550. TangentSpaceAxes_t *pAxis = &m_pTangentAxes[ptIndex];
  2551. //
  2552. // create the axes
  2553. //
  2554. pAxis->binormal = vVect.AsVector3D();
  2555. VectorNormalize( pAxis->binormal );
  2556. CrossProduct( plane.normal, pAxis->binormal, pAxis->tangent );
  2557. VectorNormalize( pAxis->tangent );
  2558. CrossProduct( pAxis->tangent, plane.normal, pAxis->binormal );
  2559. VectorNormalize( pAxis->binormal );
  2560. //
  2561. // adjust tangent for "backwards" mapping if need be
  2562. //
  2563. Vector tmpVect;
  2564. CrossProduct( uVect.AsVector3D(), vVect.AsVector3D(), tmpVect );
  2565. if( DotProduct( plane.normal, tmpVect ) > 0.0f )
  2566. {
  2567. VectorScale( pAxis->tangent, -1.0f, pAxis->tangent );
  2568. }
  2569. }
  2570. }
  2571. //-----------------------------------------------------------------------------
  2572. //-----------------------------------------------------------------------------
  2573. bool CMapFace::AllocTangentSpaceAxes( int count )
  2574. {
  2575. if( count < 1 )
  2576. return false;
  2577. m_pTangentAxes = new TangentSpaceAxes_t[count];
  2578. if( !m_pTangentAxes )
  2579. return false;
  2580. return true;
  2581. }
  2582. //-----------------------------------------------------------------------------
  2583. //-----------------------------------------------------------------------------
  2584. void CMapFace::FreeTangentSpaceAxes( void )
  2585. {
  2586. if( m_pTangentAxes )
  2587. {
  2588. delete [] m_pTangentAxes;
  2589. m_pTangentAxes = NULL;
  2590. }
  2591. }
  2592. //-----------------------------------------------------------------------------
  2593. //-----------------------------------------------------------------------------
  2594. int CMapFace::SmoothingGroupCount( void )
  2595. {
  2596. int nCount = 0;
  2597. for ( int iGroup = 0; iGroup < SMOOTHING_GROUP_MAX_COUNT; ++iGroup )
  2598. {
  2599. if ( ( m_fSmoothingGroups & ( 1 << iGroup ) ) != 0 )
  2600. {
  2601. nCount++;
  2602. }
  2603. }
  2604. return nCount;
  2605. }
  2606. //-----------------------------------------------------------------------------
  2607. //-----------------------------------------------------------------------------
  2608. void CMapFace::AddSmoothingGroup( int iGroup )
  2609. {
  2610. Assert( iGroup >= 0 );
  2611. Assert( iGroup < SMOOTHING_GROUP_MAX_COUNT );
  2612. m_fSmoothingGroups |= ( 1 << ( iGroup - 1 ) );
  2613. }
  2614. //-----------------------------------------------------------------------------
  2615. //-----------------------------------------------------------------------------
  2616. void CMapFace::RemoveSmoothingGroup( int iGroup )
  2617. {
  2618. Assert( iGroup >= 0 );
  2619. Assert( iGroup < SMOOTHING_GROUP_MAX_COUNT );
  2620. m_fSmoothingGroups &= ~( 1 << ( iGroup - 1 ) );
  2621. }
  2622. //-----------------------------------------------------------------------------
  2623. //-----------------------------------------------------------------------------
  2624. bool CMapFace::InSmoothingGroup( int iGroup )
  2625. {
  2626. if ( ( m_fSmoothingGroups & ( 1 << ( iGroup - 1 ) ) ) != 0 )
  2627. return true;
  2628. return false;
  2629. }
  2630. //-----------------------------------------------------------------------------
  2631. // Purpose: Performs an intersection of this list with another.
  2632. // Input : IntersectWith - the list to intersect with.
  2633. // In - the list of items that were in both lists
  2634. // Out - the list of items that were in one list but not the other.
  2635. //-----------------------------------------------------------------------------
  2636. void CMapFaceList::Intersect(CMapFaceList &IntersectWith, CMapFaceList &In, CMapFaceList &Out)
  2637. {
  2638. //
  2639. // See what we items have that are in the other list.
  2640. //
  2641. for (int i = 0; i < Count(); i++)
  2642. {
  2643. CMapFace *pFace = Element(i);
  2644. if (IntersectWith.Find(pFace) != -1)
  2645. {
  2646. if (In.Find(pFace) == -1)
  2647. {
  2648. In.AddToTail(pFace);
  2649. }
  2650. }
  2651. else
  2652. {
  2653. if (Out.Find(pFace) == -1)
  2654. {
  2655. Out.AddToTail(pFace);
  2656. }
  2657. }
  2658. }
  2659. //
  2660. // Now go the other way.
  2661. //
  2662. for (int i = 0; i < IntersectWith.Count(); i++)
  2663. {
  2664. CMapFace *pFace = IntersectWith.Element(i);
  2665. if (Find(pFace) != -1)
  2666. {
  2667. if (In.Find(pFace) == -1)
  2668. {
  2669. In.AddToTail(pFace);
  2670. }
  2671. }
  2672. else
  2673. {
  2674. if (Out.Find(pFace) == -1)
  2675. {
  2676. Out.AddToTail(pFace);
  2677. }
  2678. }
  2679. }
  2680. }
  2681. //-----------------------------------------------------------------------------
  2682. // Purpose: Performs an intersection of this list with another.
  2683. // Input : IntersectWith - the list to intersect with.
  2684. // In - the list of items that were in both lists
  2685. // Out - the list of items that were in one list but not the other.
  2686. //-----------------------------------------------------------------------------
  2687. void CMapFaceIDList::Intersect(CMapFaceIDList &IntersectWith, CMapFaceIDList &In, CMapFaceIDList &Out)
  2688. {
  2689. //
  2690. // See what we items have that are in the other list.
  2691. //
  2692. for (int i = 0; i < Count(); i++)
  2693. {
  2694. int nFaceID = Element(i);
  2695. if (IntersectWith.Find(nFaceID) != -1)
  2696. {
  2697. if (In.Find(nFaceID) == -1)
  2698. {
  2699. In.AddToTail(nFaceID);
  2700. }
  2701. }
  2702. else
  2703. {
  2704. if (Out.Find(nFaceID) == -1)
  2705. {
  2706. Out.AddToTail(nFaceID);
  2707. }
  2708. }
  2709. }
  2710. //
  2711. // Now go the other way.
  2712. //
  2713. for (int i = 0; i < IntersectWith.Count(); i++)
  2714. {
  2715. int nFaceID = IntersectWith.Element(i);
  2716. if (Find(nFaceID) != -1)
  2717. {
  2718. if (In.Find(nFaceID) == -1)
  2719. {
  2720. In.AddToTail(nFaceID);
  2721. }
  2722. }
  2723. else
  2724. {
  2725. if (Out.Find(nFaceID) == -1)
  2726. {
  2727. Out.AddToTail(nFaceID);
  2728. }
  2729. }
  2730. }
  2731. }
  2732. void CMapFace::DoTransform(const VMatrix &matrix)
  2733. {
  2734. SignalUpdate( EVTYPE_FACE_CHANGED );
  2735. if( nPoints < 3 )
  2736. {
  2737. Assert( nPoints > 2 );
  2738. return;
  2739. }
  2740. CMapDisp *pDisp = NULL;
  2741. Vector bbDispOld[2]; // Old bbox for the disp.
  2742. if( HasDisp() )
  2743. {
  2744. EditDispHandle_t handle = GetDisp();
  2745. pDisp = EditDispMgr()->GetDisp( handle );
  2746. if ( pDisp )
  2747. pDisp->GetBoundingBox( bbDispOld[0], bbDispOld[1] );
  2748. }
  2749. Vector oldPoint = Points[0];
  2750. // Transform the face points.
  2751. for (int i = 0; i < nPoints; i++)
  2752. {
  2753. TransformPoint( matrix, Points[i] );
  2754. }
  2755. bool bFlip = (matrix[0][0]*matrix[1][1]*matrix[2][2]) < 0;
  2756. if ( bFlip )
  2757. {
  2758. // mirror transformation would break CCW culling, so revert point order
  2759. PointsRevertOrder( Points, nPoints );
  2760. }
  2761. CalcPlaneFromFacePoints();
  2762. // ok, now apply all changes to texture & displacment too
  2763. VMatrix mTrans = matrix;
  2764. QAngle rotateAngles;
  2765. Vector moveDelta;
  2766. MatrixAngles( matrix.As3x4(), rotateAngles, moveDelta );
  2767. bool bIsLocking = Options.IsLockingTextures()!=0;
  2768. bool bIsMoving = moveDelta.LengthSqr() > 0.00001;
  2769. // erase move component from matrix
  2770. mTrans.SetTranslation( vec3_origin );
  2771. // check if new matrix is simple identity matrix
  2772. if ( mTrans.IsIdentity() )
  2773. {
  2774. if ( GetTexture() )
  2775. {
  2776. if ( bIsMoving && bIsLocking )
  2777. {
  2778. // Offset texture coordinates if we're moving and texture locking.
  2779. OffsetTexture(moveDelta);
  2780. }
  2781. // Recalculate the texture coordinates for this face.
  2782. CalcTextureCoords();
  2783. }
  2784. if ( pDisp )
  2785. {
  2786. pDisp->UpdateSurfData( this );
  2787. // Update the neighbors of displacements that intersect the old as well as the new bbox.
  2788. // Without this, there can be errors if you drag > 2 edges to interset each other, then
  2789. // move one of the intersectors (cloning can easily cause this case to happen).
  2790. Vector bbDispNew[2];
  2791. pDisp->GetBoundingBox( bbDispNew[0], bbDispNew[1] );
  2792. CMapDisp::UpdateNeighborsOfDispsIntersectingBox( bbDispOld[0], bbDispOld[1], 1.0 );
  2793. CMapDisp::UpdateNeighborsOfDispsIntersectingBox( bbDispNew[0], bbDispNew[1], 1.0 );
  2794. }
  2795. // Create any detail objects if appropriate
  2796. DetailObjects::BuildAnyDetailObjects(this);
  2797. return;
  2798. }
  2799. // ok, transformation is more complex then a simple move
  2800. if ( GetTexture() )
  2801. {
  2802. Vector vU = texture.UAxis.AsVector3D();
  2803. Vector vV = texture.VAxis.AsVector3D();
  2804. // store original length
  2805. float fScaleU = vU.Length();
  2806. float fScaleV = vV.Length();
  2807. if ( fScaleU <= 0 )
  2808. fScaleU = 1;
  2809. if ( fScaleV <=0 )
  2810. fScaleV = 1;
  2811. // transform UV axis
  2812. TransformPoint( mTrans, vU );
  2813. TransformPoint( mTrans, vV );
  2814. // get scaling factor for both axes
  2815. fScaleU = vU.Length()/fScaleU;
  2816. fScaleV = vV.Length()/fScaleV;
  2817. if ( fScaleU <= 0 )
  2818. fScaleU = 1;
  2819. if ( fScaleV <=0 )
  2820. fScaleV = 1;
  2821. // check is the transformation would destory the UV axis (both normals & perpendicular)
  2822. bool bUVAxisSameScale = fequal(fScaleV,1,0.0001) && fequal(fScaleU,1,0.0001);
  2823. bool bUVAxisPerpendicular = fequal(DotProduct( vU, vV ),0,0.0025);
  2824. // Rotate the U/V axes to keep them oriented the same relative
  2825. // to this face.
  2826. if ( bIsLocking && bUVAxisPerpendicular )
  2827. {
  2828. // make sure UV axes are normals & perpendicalur
  2829. texture.UAxis.AsVector3D() = vU/fScaleU;
  2830. texture.VAxis.AsVector3D() = vV/fScaleV;
  2831. }
  2832. if ( bUVAxisSameScale )
  2833. {
  2834. // scale is fine
  2835. if ( !bIsLocking )
  2836. {
  2837. // If we are not texture locking, recalculate the texture axes based on current
  2838. // texture alignment setting. This prevents the axes from becoming normal to the face.
  2839. InitializeTextureAxes(Options.GetTextureAlignment(), INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE);
  2840. }
  2841. }
  2842. else // we stretch/scale axes
  2843. {
  2844. // operation changes scale of textures, check if we really want that:
  2845. bIsLocking = Options.IsScaleLockingTextures()!=0;
  2846. if ( bIsLocking )
  2847. {
  2848. texture.scale[0] *= fScaleU;
  2849. texture.scale[1] *= fScaleV;
  2850. }
  2851. }
  2852. if ( bIsMoving && bIsLocking )
  2853. {
  2854. // Offset texture coordinates if we're moving and texture locking.
  2855. OffsetTexture(moveDelta);
  2856. }
  2857. // Recalculate the texture coordinates for this face.
  2858. CalcTextureCoords();
  2859. }
  2860. // rotate the displacement field data - if any!
  2861. if( pDisp )
  2862. {
  2863. pDisp->DoTransform( mTrans );
  2864. // Update the neighbors of displacements that intersect the old as well as the new bbox.
  2865. // Without this, there can be errors if you drag > 2 edges to interset each other, then
  2866. // move one of the intersectors (cloning can easily cause this case to happen).
  2867. Vector bbDispNew[2];
  2868. pDisp->GetBoundingBox( bbDispNew[0], bbDispNew[1] );
  2869. CMapDisp::UpdateNeighborsOfDispsIntersectingBox( bbDispOld[0], bbDispOld[1], 1.0 );
  2870. CMapDisp::UpdateNeighborsOfDispsIntersectingBox( bbDispNew[0], bbDispNew[1], 1.0 );
  2871. }
  2872. // Create any detail objects if appropriate
  2873. DetailObjects::BuildAnyDetailObjects(this);
  2874. }