Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3467 lines
92 KiB

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