BVB Source Codes

CRYENGINE Show BreakableGlassSystem.cpp Source code

Return Download CRYENGINE: download BreakableGlassSystem.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. #include "StdAfx.h"
  4. #include "BreakableGlassSystem.h"
  5. #include <CryRenderer/RenderElements/CREBreakableGlass.h>
  6. #include <CryRenderer/RenderElements/CREBreakableGlassHelpers.h>
  7.  
  8. #include "CryAction.h"
  9. #include "ActionGame.h"
  10. #include <Cry3DEngine/IIndexedMesh.h>
  11.  
  12. // Defines
  13. #define PHYSEVENT_COLLIDER 0
  14. #define PHYSEVENT_COLLIDEE 1
  15.  
  16. // Error logging
  17. #ifndef RELEASE
  18.         #define LOG_GLASS_ERROR(...)   CryLogAlways("[BreakGlassSystem Error]: " __VA_ARGS__)
  19.         #define LOG_GLASS_WARNING(...) CryLogAlways("[BreakGlassSystem Warning]: " __VA_ARGS__)
  20. #else
  21.         #define LOG_GLASS_ERROR(...)
  22.         #define LOG_GLASS_WARNING(...)
  23. #endif
  24.  
  25. //--------------------------------------------------------------------------------------------------
  26. // Name: CBreakableGlassSystem
  27. // Desc: Constructor
  28. //--------------------------------------------------------------------------------------------------
  29. CBreakableGlassSystem::CBreakableGlassSystem()
  30.         : m_enabled(false)
  31. {
  32.         // Add physics callback
  33.         if (IPhysicalWorld* pPhysWorld = gEnv->pPhysicalWorld)
  34.         {
  35.                 const float listenerPriority = 2001.0f;
  36.                 pPhysWorld->AddEventClient(EventPhysCollision::id, &CBreakableGlassSystem::HandleImpact, 1, listenerPriority);
  37.         }
  38.  
  39.         // Hook up debug cvars
  40.         if (m_pGlassCVars = new SBreakableGlassCVars())
  41.         {
  42. #ifdef GLASS_DEBUG_MODE
  43.                 // Rendering
  44.                 REGISTER_CVAR2("g_glassSystem_draw", &m_pGlassCVars->m_draw, 1, VF_CHEAT, "Toggles drawing of glass nodes");
  45.                 REGISTER_CVAR2("g_glassSystem_drawWireframe", &m_pGlassCVars->m_drawWireframe, 0, VF_CHEAT, "Toggles drawing of glass node wireframe");
  46.                 REGISTER_CVAR2("g_glassSystem_drawDebugData", &m_pGlassCVars->m_drawDebugData, 0, VF_CHEAT, "Toggles drawing of glass node break/impact data");
  47.                 REGISTER_CVAR2("g_glassSystem_drawFragData", &m_pGlassCVars->m_drawFragData, 0, VF_CHEAT, "Toggles drawing of glass physicalized fragment data");
  48.  
  49.                 // Impact decals
  50.                 REGISTER_CVAR2("g_glassSystem_decalAlwaysRebuild", &m_pGlassCVars->m_decalAlwaysRebuild, 0, VF_CHEAT, "Forces decals to rebuild every frame, allowing real-time tweaking.");
  51.                 REGISTER_CVAR2("g_glassSystem_decalScale", &m_pGlassCVars->m_decalScale, 2.5f, VF_CHEAT, "Scale of procedural impact decals on glass");
  52.                 REGISTER_CVAR2("g_glassSystem_decalMinRadius", &m_pGlassCVars->m_decalMinRadius, 0.25f, VF_CHEAT, "Minimum size of decal around impact");
  53.                 REGISTER_CVAR2("g_glassSystem_decalMaxRadius", &m_pGlassCVars->m_decalMaxRadius, 1.25f, VF_CHEAT, "Maximum size of decal around impact");
  54.                 REGISTER_CVAR2("g_glassSystem_decalRandChance", &m_pGlassCVars->m_decalRandChance, 0.67f, VF_CHEAT, "Chance for a decal to randomly be scaled up");
  55.                 REGISTER_CVAR2("g_glassSystem_decalRandScale", &m_pGlassCVars->m_decalRandScale, 1.6f, VF_CHEAT, "Scale of decal if selected randomly");
  56.                 REGISTER_CVAR2("g_glassSystem_decalMinImpactSpeed", &m_pGlassCVars->m_minImpactSpeed, 400.0f, VF_CHEAT, "Minimum speed for an impact to use the bullet decal");
  57.  
  58.                 // Physicalized fragments
  59.                 REGISTER_CVAR2("g_glassSystem_fragImpulseScale", &m_pGlassCVars->m_fragImpulseScale, 5.0f, VF_CHEAT, "Scales impulse applied to fragments when physicalized");
  60.                 REGISTER_CVAR2("g_glassSystem_fragAngImpulseScale", &m_pGlassCVars->m_fragAngImpulseScale, 0.1f, VF_CHEAT, "Scales *angular* impulse applied to fragments when physicalized");
  61.                 REGISTER_CVAR2("g_glassSystem_fragImpulseDampen", &m_pGlassCVars->m_fragImpulseDampen, 0.3f, VF_CHEAT, "Dampens impulse applied to non-directly impacted fragments");
  62.                 REGISTER_CVAR2("g_glassSystem_fragAngImpulseDampen", &m_pGlassCVars->m_fragAngImpulseDampen, 0.4f, VF_CHEAT, "Dampens *angular* impulse applied to non-directly impacted fragments");
  63.                 REGISTER_CVAR2("g_glassSystem_fragSpread", &m_pGlassCVars->m_fragSpread, 1.5f, VF_CHEAT, "Spread velocity to apply to impacted fragments");
  64.                 REGISTER_CVAR2("g_glassSystem_fragMaxSpeed", &m_pGlassCVars->m_fragMaxSpeed, 4.0f, VF_CHEAT, "Maximum speed to apply to physicalized fragments");
  65.  
  66.                 // Impact breaks
  67.                 REGISTER_CVAR2("g_glassSystem_impactSplitMinRadius", &m_pGlassCVars->m_impactSplitMinRadius, 0.05f, VF_CHEAT, "Minimum radius for split around impact");
  68.                 REGISTER_CVAR2("g_glassSystem_impactSplitRandMin", &m_pGlassCVars->m_impactSplitRandMin, 0.5f, VF_CHEAT, "Minimum radius variation for split around impact");
  69.                 REGISTER_CVAR2("g_glassSystem_impactSplitRandMax", &m_pGlassCVars->m_impactSplitRandMax, 1.5f, VF_CHEAT, "Maximum radius variation for split around impact");
  70.                 REGISTER_CVAR2("g_glassSystem_impactEdgeFadeScale", &m_pGlassCVars->m_impactEdgeFadeScale, 2.0f, VF_CHEAT, "Scales radial crack fade distance");
  71.  
  72.                 // Particle effects
  73.                 REGISTER_CVAR2("g_glassSystem_particleFXEnable", &m_pGlassCVars->m_particleFXEnable, 1, VF_CHEAT, "Toggles particle effects being played when fragments are broken");
  74.                 REGISTER_CVAR2("g_glassSystem_particleFXUseColours", &m_pGlassCVars->m_particleFXUseColours, 0, VF_CHEAT, "Toggles particle effects being coloured to match the glass");
  75.                 REGISTER_CVAR2("g_glassSystem_particleFXScale", &m_pGlassCVars->m_particleFXScale, 0.25f, VF_CHEAT, "Scales the glass particle effects spawned");
  76. #endif // GLASS_DEBUG_MODE
  77.         }
  78.  
  79.         // Add callback for system enabled cvar
  80.         IConsole* pConsole = gEnv->pConsole;
  81.         ICVar* pSysEnabledCvar = pConsole ? pConsole->GetCVar("g_glassSystemEnable") : NULL;
  82.  
  83.         if (pSysEnabledCvar)
  84.         {
  85.                 m_enabled = (pSysEnabledCvar->GetIVal() != 0);
  86.                 pSysEnabledCvar->SetOnChangeCallback(OnEnabledCVarChange);
  87.         }
  88. }//-------------------------------------------------------------------------------------------------
  89.  
  90. //--------------------------------------------------------------------------------------------------
  91. // Name: ~CBreakableGlassSystem
  92. // Desc: Destructor
  93. //--------------------------------------------------------------------------------------------------
  94. CBreakableGlassSystem::~CBreakableGlassSystem()
  95. {
  96.         // Release all nodes and data
  97.         ResetAll();
  98.  
  99. #ifdef GLASS_DEBUG_MODE
  100.         // Remove debug cvars after all nodes that could be using them
  101.         if (IConsole* pConsole = gEnv->pConsole)
  102.         {
  103.                 pConsole->UnregisterVariable("g_glassSystem_draw");
  104.                 pConsole->UnregisterVariable("g_glassSystem_drawWireframe");
  105.                 pConsole->UnregisterVariable("g_glassSystem_drawDebugData");
  106.                 pConsole->UnregisterVariable("g_glassSystem_drawFragData");
  107.  
  108.                 pConsole->UnregisterVariable("g_glassSystem_decalAlwaysRebuild");
  109.                 pConsole->UnregisterVariable("g_glassSystem_decalScale");
  110.                 pConsole->UnregisterVariable("g_glassSystem_decalMinRadius");
  111.                 pConsole->UnregisterVariable("g_glassSystem_decalMaxRadius");
  112.                 pConsole->UnregisterVariable("g_glassSystem_decalRandChance");
  113.                 pConsole->UnregisterVariable("g_glassSystem_decalRandScale");
  114.                 pConsole->UnregisterVariable("g_glassSystem_decalMinImpactSpeed");
  115.  
  116.                 pConsole->UnregisterVariable("g_glassSystem_fragImpulseScale");
  117.                 pConsole->UnregisterVariable("g_glassSystem_fragAngImpulseScale");
  118.                 pConsole->UnregisterVariable("g_glassSystem_fragImpulseDampen");
  119.                 pConsole->UnregisterVariable("g_glassSystem_fragAngImpulseDampen");
  120.                 pConsole->UnregisterVariable("g_glassSystem_fragSpread");
  121.                 pConsole->UnregisterVariable("g_glassSystem_fragMaxSpeed");
  122.  
  123.                 pConsole->UnregisterVariable("g_glassSystem_particleFXEnable");
  124.                 pConsole->UnregisterVariable("g_glassSystem_particleFXUseColours");
  125.                 pConsole->UnregisterVariable("g_glassSystem_particleFXScale");
  126.         }
  127. #endif // GLASS_DEBUG_MODE
  128.  
  129.         SAFE_DELETE(m_pGlassCVars);
  130.  
  131.         // Remove system enabled cvar callback
  132.         IConsole* pConsole = gEnv->pConsole;
  133.         ICVar* pSysEnabledCVar = pConsole ? pConsole->GetCVar("g_glassSystemEnable") : NULL;
  134.  
  135.         if (pSysEnabledCVar)
  136.         {
  137.                 pSysEnabledCVar->SetOnChangeCallback(NULL);
  138.         }
  139.  
  140.         // Remove physics callback
  141.         if (IPhysicalWorld* pPhysWorld = gEnv->pPhysicalWorld)
  142.         {
  143.                 pPhysWorld->RemoveEventClient(EventPhysCollision::id, &CBreakableGlassSystem::HandleImpact, 1);
  144.         }
  145. }//-------------------------------------------------------------------------------------------------
  146.  
  147. //--------------------------------------------------------------------------------------------------
  148. // Name: Update
  149. // Desc: Allows render nodes to updates
  150. //--------------------------------------------------------------------------------------------------
  151. void CBreakableGlassSystem::Update(const float frameTime)
  152. {
  153.         AssertUnusedIfDisabled();
  154.  
  155.         // Allow render nodes to update
  156.         SBreakableGlassUpdateParams updateParams;
  157.         updateParams.m_frametime = frameTime;
  158.  
  159.         const int numPlanes = m_glassPlanes.Count();
  160.         for (int i = 0; i < numPlanes; ++i)
  161.         {
  162.                 if (IBreakableGlassRenderNode* pRenderNode = m_glassPlanes[i])
  163.                 {
  164.                         pRenderNode->Update(updateParams);
  165.  
  166.                         // May be able to remove glass entirely
  167.                         if (pRenderNode->HasGlassShattered() && !pRenderNode->HasActiveFragments())
  168.                         {
  169.                                 pRenderNode->ReleaseNode();
  170.                                 SAFE_DELETE(m_glassPlanes[i]);
  171.                         }
  172.                 }
  173.         }
  174. }//-------------------------------------------------------------------------------------------------
  175.  
  176. //--------------------------------------------------------------------------------------------------
  177. // Name: OnEnabledCVarChange
  178. // Desc: Handles glass system cvar changes
  179. //--------------------------------------------------------------------------------------------------
  180. void CBreakableGlassSystem::OnEnabledCVarChange(ICVar* pCVar)
  181. {
  182.         if (pCVar)
  183.         {
  184.                 if (CCryAction* pCryAction = CCryAction::GetCryAction())
  185.                 {
  186.                         if (CBreakableGlassSystem* pGlassSystem = static_cast<CBreakableGlassSystem*>(pCryAction->GetIBreakableGlassSystem()))
  187.                         {
  188.                                 pGlassSystem->m_enabled = (pCVar->GetIVal() != 0);
  189.                         }
  190.                 }
  191.         }
  192. }
  193.  
  194. //--------------------------------------------------------------------------------------------------
  195. // Name: HandleImpact
  196. // Desc: Passes an impact on to the simulation
  197. //--------------------------------------------------------------------------------------------------
  198. int CBreakableGlassSystem::HandleImpact(const EventPhys* pPhysEvent)
  199. {
  200.         if (CCryAction* pCryAction = CCryAction::GetCryAction())
  201.         {
  202.                 if (CBreakableGlassSystem* pGlassSystem = static_cast<CBreakableGlassSystem*>(pCryAction->GetIBreakableGlassSystem()))
  203.                 {
  204.                         pGlassSystem->AssertUnusedIfDisabled();
  205.  
  206.                         if (pGlassSystem->m_enabled)
  207.                         {
  208.                                 if (const EventPhysCollision* pCollEvent = static_cast<const EventPhysCollision*>(pPhysEvent))
  209.                                 {
  210.                                         // Glass fragments always get destroyed on their first collision
  211.                                         const uint physFrag = (pCollEvent->iForeignData[0] == PHYS_FOREIGN_ID_BREAKABLE_GLASS_FRAGMENT) ? 0 : 1;
  212.                                         IPhysicalEntity* pPhysEnt = pCollEvent->pEntity[physFrag];
  213.  
  214.                                         if (pPhysEnt && pCollEvent->iForeignData[physFrag] == PHYS_FOREIGN_ID_BREAKABLE_GLASS_FRAGMENT)
  215.                                         {
  216.                                                 // Fragments only collide with non-glass geometry
  217.                                                 const int nonFragType = pCollEvent->iForeignData[1 - physFrag];
  218.  
  219.                                                 if (nonFragType != PHYS_FOREIGN_ID_BREAKABLE_GLASS
  220.                                                     && nonFragType != PHYS_FOREIGN_ID_BREAKABLE_GLASS_FRAGMENT
  221.                                                     && nonFragType != PHYS_FOREIGN_ID_ENTITY) // Only break on floors, walls, etc.
  222.                                                 {
  223.                                                         // Verify parent glass node, then allow it to handle impact
  224.                                                         if (SGlassPhysFragment* pPhysFrag = (SGlassPhysFragment*)pCollEvent->pForeignData[physFrag])
  225.                                                         {
  226.                                                                 if (IBreakableGlassRenderNode* pRenderNode = (IBreakableGlassRenderNode*)pPhysFrag->m_pRenderNode)
  227.                                                                 {
  228.                                                                         pRenderNode->DestroyPhysFragment(pPhysFrag);
  229.                                                                 }
  230.                                                         }
  231.                                                 }
  232.                                         }
  233.                                         else if (pCollEvent->iForeignData[PHYSEVENT_COLLIDEE] == PHYS_FOREIGN_ID_BREAKABLE_GLASS)
  234.                                         {
  235.                                                 // Get breakable glass data
  236.                                                 PodArray<IBreakableGlassRenderNode*>& glassPlanes = pGlassSystem->m_glassPlanes;
  237.                                                 const int numPlanes = glassPlanes.Count();
  238.  
  239.                                                 // Duplicate event so we can freely manipulate it
  240.                                                 EventPhysCollision dupeEvent = *pCollEvent;
  241.                                                 const EventPhysCollision* pDupeEvent = (const EventPhysCollision*)&dupeEvent;
  242.  
  243.                                                 // Some actors can force breaks for gameplay reasons
  244.                                                 IPhysicalEntity* pCollider = dupeEvent.pEntity[PHYSEVENT_COLLIDER];
  245.  
  246.                                                 if (pCollider && pCollider->GetType() == PE_LIVING)
  247.                                                 {
  248.                                                         IEntity* pColliderEntity = (IEntity*)pCollider->GetForeignData(PHYS_FOREIGN_ID_ENTITY);
  249.                                                         IActor* pActor = pColliderEntity ? gEnv->pGameFramework->GetIActorSystem()->GetActor(pColliderEntity->GetId()) : NULL;
  250.  
  251.                                                         if (pActor && pActor->MustBreakGlass())
  252.                                                         {
  253.                                                                 pGlassSystem->ModifyEventToForceBreak(&dupeEvent);
  254.                                                         }
  255.                                                 }
  256.  
  257.                                                 // Verify glass node and pass impact through
  258.                                                 IBreakableGlassRenderNode* pRenderNode = (IBreakableGlassRenderNode*)pCollEvent->pForeignData[PHYSEVENT_COLLIDEE];
  259.                                                 const int targetId = pRenderNode ? pRenderNode->GetId() : numPlanes;
  260.  
  261.                                                 if (targetId < numPlanes && pRenderNode == glassPlanes[targetId])
  262.                                                 {
  263.                                                         pGlassSystem->PassImpactToNode(pRenderNode, pDupeEvent);
  264.                                                 }
  265.                                         }
  266.                                 }
  267.                         }
  268.                 }
  269.         }
  270.  
  271.         return 1; // Pass event to other handlers even if we processed it
  272. }//-------------------------------------------------------------------------------------------------
  273.  
  274. //--------------------------------------------------------------------------------------------------
  275. // Name: BreakGlassObject
  276. // Desc: Replaces a physical StatObj with a glass node where supported
  277. // Note: We're currently duplicating some work done in CryAction, so should be reading that in.
  278. //                       Similarly, we should also be accepting the SProcessImpactIn struct instead of the event
  279. //--------------------------------------------------------------------------------------------------
  280. bool CBreakableGlassSystem::BreakGlassObject(const EventPhysCollision& physEvent, const bool forceGlassBreak)
  281. {
  282.         bool bSuccess = false;
  283.         SBreakableGlassInitParams initParams;
  284.  
  285.         // Get surface type's breaking effect
  286.         ISurfaceTypeManager* pSurfaceTypeManager = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager();
  287.         initParams.surfaceTypeId = physEvent.idmat[PHYSEVENT_COLLIDEE];
  288.  
  289.         if (ISurfaceType* pMat = pSurfaceTypeManager->GetSurfaceType(initParams.surfaceTypeId))
  290.         {
  291.                 if (ISurfaceType::SBreakable2DParams* pBreakableParams = pMat->GetBreakable2DParams())
  292.                 {
  293.                         initParams.pShatterEffect = pBreakableParams->full_fracture_fx ? gEnv->pParticleManager->FindEffect(pBreakableParams->full_fracture_fx) : NULL;
  294.                 }
  295.         }
  296.  
  297.         // Calculate glass data
  298.         SBreakableGlassPhysData physData;
  299.  
  300.         if (ExtractPhysDataFromEvent(physEvent, physData, initParams) &&
  301.             ValidateExtractedOutline(physData, initParams))
  302.         {
  303.                 Vec3 size;
  304.                 Matrix34 transMat;
  305.  
  306.                 CalculateGlassBounds(physData.pPhysGeom, size, transMat);
  307.  
  308.                 // Update initialisation parameters
  309.                 if (!physData.defaultFrag.empty())
  310.                 {
  311.                         initParams.pInitialFrag = physData.defaultFrag.begin();
  312.                         initParams.numInitialFragPts = physData.defaultFrag.size();
  313.                 }
  314.                 else
  315.                 {
  316.                         initParams.pInitialFrag = NULL;
  317.                         initParams.numInitialFragPts = 0;
  318.                 }
  319.  
  320.                 initParams.size.set(size.x, size.y);
  321.                 initParams.thickness = size.z;
  322.  
  323.                 // Fix-up mesh and calculate anchors
  324.                 CalculateGlassAnchors(physData, initParams);
  325.  
  326.                 // Calculate UV coords at axis-aligned points
  327.                 Vec2 origin = InterpolateUVCoords(physData.uvBasis[0], physData.uvBasis[1], physData.uvBasis[2], Vec2Constants<f32>::fVec2_Zero);
  328.                 Vec2 xAxisPt = InterpolateUVCoords(physData.uvBasis[0], physData.uvBasis[1], physData.uvBasis[2], Vec2Constants<f32>::fVec2_OneX);
  329.                 Vec2 yAxisPt = InterpolateUVCoords(physData.uvBasis[0], physData.uvBasis[1], physData.uvBasis[2], Vec2Constants<f32>::fVec2_OneY);
  330.  
  331.                 // Convert the UV basis to X and Y axes w/ an offset
  332.                 initParams.uvOrigin = origin;
  333.                 initParams.uvXAxis = xAxisPt - origin;
  334.                 initParams.uvYAxis = yAxisPt - origin;
  335.  
  336.                 // Create a replacement glass node
  337.                 IBreakableGlassRenderNode* pRenderNode = InitGlassNode(physData, initParams, transMat);
  338.  
  339.                 // Duplicate the collision event and patch it up for the new node
  340.                 EventPhysCollision dupeEvent = physEvent;
  341.  
  342.                 dupeEvent.pEntity[PHYSEVENT_COLLIDEE] = pRenderNode->GetPhysics();
  343.                 dupeEvent.pForeignData[PHYSEVENT_COLLIDEE] = (void*)pRenderNode;
  344.                 dupeEvent.iForeignData[PHYSEVENT_COLLIDEE] = PHYS_FOREIGN_ID_BREAKABLE_GLASS;
  345.  
  346.                 pe_params_part partParams;
  347.                 if (dupeEvent.pEntity[PHYSEVENT_COLLIDEE]->GetParams(&partParams) && partParams.nMats > 0)
  348.                 {
  349.                         dupeEvent.idmat[PHYSEVENT_COLLIDEE] = partParams.pMatMapping[0];
  350.                 }
  351.  
  352.                 if (forceGlassBreak)
  353.                 {
  354.                         ModifyEventToForceBreak(&dupeEvent);
  355.                 }
  356.  
  357.                 // Send it directly to the new node
  358.                 PassImpactToNode(pRenderNode, &dupeEvent);
  359.  
  360.                 bSuccess = true;
  361.         }
  362.  
  363.         return bSuccess;
  364. }//-------------------------------------------------------------------------------------------------
  365.  
  366. //--------------------------------------------------------------------------------------------------
  367. // Name: ResetAll
  368. // Desc: Resets all glass to initial state
  369. //--------------------------------------------------------------------------------------------------
  370. void CBreakableGlassSystem::ResetAll()
  371. {
  372.         // Glass nodes are only created *after* a collision,
  373.         // so need to actually remove all nodes here
  374.         ReleaseGlassNodes();
  375. }//-------------------------------------------------------------------------------------------------
  376.  
  377. //--------------------------------------------------------------------------------------------------
  378. // Name: GetMemoryUsage
  379. // Desc: Adds the size of the system
  380. //--------------------------------------------------------------------------------------------------
  381. void CBreakableGlassSystem::GetMemoryUsage(ICrySizer* pSizer) const
  382. {
  383.         // System usage
  384.         SIZER_COMPONENT_NAME(pSizer, "BreakableGlassSystem");
  385.         pSizer->AddObject(this, sizeof(*this));
  386. }//-------------------------------------------------------------------------------------------------
  387.  
  388. //--------------------------------------------------------------------------------------------------
  389. // Name: ExtractPhysDataFromEvent
  390. // Desc: Extracts collider's physical data from an event
  391. // Note 1: Ideally *ALL* of this should be calculated offline and the minimal data loaded
  392. // Note 2: We're currently duplicating some work done in CryAction, so should be reading that in
  393. //--------------------------------------------------------------------------------------------------
  394. bool CBreakableGlassSystem::ExtractPhysDataFromEvent(const EventPhysCollision& physEvent, SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams)
  395. {
  396.         if (IPhysicalEntity* pPhysEntity = physEvent.pEntity[PHYSEVENT_COLLIDEE])
  397.         {
  398.                 // Get collider entity data
  399.                 const int entType = pPhysEntity->GetiForeignData();
  400.                 const int entPart = physEvent.partid[PHYSEVENT_COLLIDEE];
  401.  
  402.                 // Local output data
  403.                 IStatObj* pStatObj = NULL;
  404.                 IMaterial* pRenderMat = NULL;
  405.                 phys_geometry* pPhysGeom = NULL;
  406.                 IRenderNode::RenderFlagsType renderFlags = 0;
  407.  
  408.                 Matrix34A entityMat;
  409.                 entityMat.SetIdentity();
  410.  
  411.                 // Only handling simple objects at the moment
  412.                 const pe_type physType = pPhysEntity->GetType();
  413.  
  414.                 if (physType == PE_STATIC || physType == PE_RIGID)
  415.                 {
  416.                         // Entity or static object?
  417.                         if (entType == PHYS_FOREIGN_ID_ENTITY)
  418.                         {
  419.                                 IEntity* pEntity = (IEntity*)pPhysEntity->GetForeignData(PHYS_FOREIGN_ID_ENTITY);
  420.  
  421.                                 pStatObj = pEntity->GetStatObj(entPart);
  422.                                 entityMat = pEntity->GetSlotWorldTM(entPart);
  423.  
  424.                                 if (IEntityRender* pIEntityRender = pEntity->GetRenderInterface())
  425.                                 {
  426.                                         pRenderMat = pIEntityRender->GetRenderMaterial(entPart);
  427.  
  428.                                         IRenderNode* pRenderNode = pIEntityRender->GetRenderNode();
  429.                                         renderFlags = pRenderNode ? pRenderNode->GetRndFlags() : 0;
  430.  
  431.                                         // Fall back to top level material if sub-object fails to find it
  432.                                         if (!pRenderMat)
  433.                                         {
  434.                                                 pRenderMat = pIEntityRender->GetRenderMaterial();
  435.  
  436.                                                 if (!pRenderMat && pStatObj)
  437.                                                 {
  438.                                                         pRenderMat = pStatObj->GetMaterial();
  439.                                                 }
  440.                                         }
  441.                                 }
  442.                         }
  443.                         else if (entType == PHYS_FOREIGN_ID_STATIC)
  444.                         {
  445.                                 if (IRenderNode* pBrush = (IRenderNode*)physEvent.pForeignData[PHYSEVENT_COLLIDEE])
  446.                                 {
  447.                                         pStatObj = pBrush->GetEntityStatObj(0, 0, &entityMat);
  448.                                         pRenderMat = pBrush->GetMaterial();
  449.                                         renderFlags = pBrush->GetRndFlags();
  450.  
  451.                                         // May need to get sub-object and it's material
  452.                                         if (pStatObj && pStatObj->GetFlags() & STATIC_OBJECT_COMPOUND)
  453.                                         {
  454.                                                 if (IStatObj::SSubObject* pSubObj = pStatObj->GetSubObject(entPart))
  455.                                                 {
  456.                                                         pStatObj = pSubObj->pStatObj;
  457.  
  458.                                                         if (!pSubObj->bIdentityMatrix)
  459.                                                         {
  460.                                                                 entityMat = entityMat * pSubObj->tm;
  461.                                                         }
  462.  
  463.                                                         // Find the correct sub-material
  464.                                                         // Note: We loop as the slots don't always line up
  465.                                                         const int subMtlCount = pRenderMat->GetSubMtlCount();
  466.                                                         for (int i = 0; i < subMtlCount; ++i)
  467.                                                         {
  468.                                                                 if (IMaterial* pSubMat = pRenderMat->GetSubMtl(i))
  469.                                                                 {
  470.                                                                         if (pSubMat->GetSurfaceTypeId() == initParams.surfaceTypeId)
  471.                                                                         {
  472.                                                                                 pRenderMat = pSubMat;
  473.                                                                                 break;
  474.                                                                         }
  475.                                                                 }
  476.                                                         }
  477.                                                 }
  478.                                         }
  479.                                 }
  480.                         }
  481.                 }
  482.  
  483.                 // Validate geometry of collided object
  484.                 pPhysGeom = pStatObj ? pStatObj->GetPhysGeom() : NULL;
  485.                 IGeometry* pGeom = pPhysGeom ? pPhysGeom->pGeom : NULL;
  486.                 bool validGeom = false;
  487.  
  488.                 primitives::box bbox;
  489.                 int thinAxis;
  490.  
  491.                 if (pGeom)
  492.                 {
  493.                         // Determine thin geometry axis for glass alignment
  494.                         pGeom->GetBBox(&bbox);
  495.                         thinAxis = idxmin3((float*)&bbox.size);
  496.  
  497.                         // Handle geometry mesh type
  498.                         switch (pGeom->GetType())
  499.                         {
  500.                         case GEOM_TRIMESH:
  501.                                 // Perform full mesh analysis and extraction
  502.                                 if (mesh_data* pPhysMeshData = (mesh_data*)pGeom->GetData())
  503.                                 {
  504.                                         if (ValidatePhysMesh(pPhysMeshData, thinAxis) && ExtractPhysMesh(pPhysMeshData, thinAxis, bbox, data.defaultFrag))
  505.                                         {
  506.                                                 validGeom = true;
  507.                                         }
  508.                                 }
  509.                                 break;
  510.  
  511.                         case GEOM_BOX:
  512.                                 // Simple box, so assume valid
  513.                                 validGeom = true;
  514.                                 break;
  515.  
  516.                         default:
  517.                                 // Only support boxes and tri-meshes
  518.                                 break;
  519.                         }
  520.                 }
  521.  
  522.                 // Invalid geometry, so can't continue
  523.                 if (!validGeom)
  524.                 {
  525.                         pPhysGeom = NULL;
  526.                 }
  527.                 // Attempt UV coord extraction from render mesh
  528.                 else
  529.                 {
  530.                         ExtractUVCoords(pStatObj, bbox, thinAxis, data);
  531.                 }
  532.  
  533.                 // Copy final data
  534.                 data.pStatObj = pStatObj;
  535.                 data.pPhysGeom = pPhysGeom;
  536.                 data.renderFlags = renderFlags;
  537.                 data.entityMat = entityMat;
  538.                 initParams.pGlassMaterial = pRenderMat;
  539.         }
  540.  
  541.         return data.pStatObj && data.pPhysGeom && initParams.pGlassMaterial;
  542. }//-------------------------------------------------------------------------------------------------
  543.  
  544. //--------------------------------------------------------------------------------------------------
  545. // Name: ValidatePhysMesh
  546. // Desc: Determines if a mesh is breakable/supported by the system
  547. //--------------------------------------------------------------------------------------------------
  548. bool CBreakableGlassSystem::ValidatePhysMesh(mesh_data* pPhysMesh, const int thinAxis)
  549. {
  550.         bool isBreakable = true;
  551.  
  552.         // Only support a singular mesh
  553.         if (pPhysMesh->pIslands && pPhysMesh->nIslands > 1)
  554.         {
  555.                 LOG_GLASS_WARNING("Physics mesh has too many islands, glass elements split incorrectly?");
  556.                 isBreakable = false;
  557.         }
  558.         // Only support meshes with an actual surface (sanity check)
  559.         else if (pPhysMesh->nVertices < 3)
  560.         {
  561.                 LOG_GLASS_WARNING("Physics mesh doesn't contain enough vertices.");
  562.                 isBreakable = false;
  563.         }
  564.  
  565.         return isBreakable;
  566. }//-------------------------------------------------------------------------------------------------
  567.  
  568. //--------------------------------------------------------------------------------------------------
  569. // Name: ExtractPhysMesh
  570. // Desc: Obtains a glass-fragment list from a breakable mesh
  571. //--------------------------------------------------------------------------------------------------
  572. bool CBreakableGlassSystem::ExtractPhysMesh(mesh_data* pPhysMesh, const int thinAxis, const primitives::box& bbox, TGlassDefFragmentArray& fragOutline)
  573. {
  574.         // Calculate thin axis
  575.         const Vec3& thinRow = bbox.Basis.GetRow(thinAxis);
  576.         const Matrix33& vtxTrans = bbox.Basis;
  577.  
  578.         // Traverse connected triangles and build merged list
  579.         const int numTris = pPhysMesh->nTris;
  580.         TGlassFragmentIndexArray touchedTris, fragTris;
  581.  
  582.         for (int i = 0; i < numTris; ++i)
  583.         {
  584.                 MergeFragmentTriangles(pPhysMesh, i, thinRow, touchedTris, fragTris);
  585.         }
  586.  
  587.         // Thinnest axis determines mesh orientation
  588.         int xAxis = thinAxis == 0 ? 2 : (thinAxis == 1 ? 0 : 0);
  589.         int yAxis = thinAxis == 0 ? 1 : (thinAxis == 1 ? 2 : 1);
  590.  
  591.         const Vec3& vx = vtxTrans.GetRow(xAxis);
  592.         const Vec3& vy = vtxTrans.GetRow(yAxis);
  593.  
  594.         // Create single outline for each set of triangles, discarding thin axis
  595.         strided_pointer<Vec3> pVerts = pPhysMesh->pVertices;
  596.         index_t* pInds = pPhysMesh->pIndices;
  597.  
  598.         if (fragTris.empty())
  599.         {
  600.                 LOG_GLASS_ERROR("Valid phys mesh found, but no triangles extractable.");
  601.         }
  602.         else
  603.         {
  604.                 // Pre-size outline buffer
  605.                 const uint fragTriCount = min(fragTris.size(), fragOutline.max_size() - 2);
  606.                 const uint vertCount = min(fragTriCount + 2, fragOutline.max_size());
  607.  
  608.                 for (int i = 0; i < fragTriCount + 2; ++i)
  609.                 {
  610.                         fragOutline.push_back();
  611.                 }
  612.  
  613.                 // Add initial triangle
  614.                 int triIndex = fragTris[0] * 3;
  615.  
  616.                 Vec3 tmpVtx = pVerts[pInds[triIndex]];
  617.                 fragOutline[0].x = vx.Dot(tmpVtx);
  618.                 fragOutline[0].y = vy.Dot(tmpVtx);
  619.  
  620.                 tmpVtx = pVerts[pInds[triIndex + 1]];
  621.                 fragOutline[1].x = vx.Dot(tmpVtx);
  622.                 fragOutline[1].y = vy.Dot(tmpVtx);
  623.  
  624.                 tmpVtx = pVerts[pInds[triIndex + 2]];
  625.                 fragOutline[2].x = vx.Dot(tmpVtx);
  626.                 fragOutline[2].y = vy.Dot(tmpVtx);
  627.  
  628.                 int ptCount = 3;
  629.  
  630.                 // Calculate area of triangle to determine winding order
  631.                 float area = fragOutline[2].x * fragOutline[0].y - fragOutline[0].x * fragOutline[2].y;
  632.                 area += fragOutline[0].x * fragOutline[1].y - fragOutline[1].x * fragOutline[0].y;
  633.                 area += fragOutline[1].x * fragOutline[2].y - fragOutline[2].x * fragOutline[1].y;
  634.  
  635.                 // Reverse triangle order if not clockwise
  636.                 if (area < 0.0f)
  637.                 {
  638.                         Vec2 temp = fragOutline[2];
  639.                         fragOutline[2] = fragOutline[0];
  640.                         fragOutline[0] = temp;
  641.                 }
  642.  
  643.                 // Merge each additional triangle into the shape
  644.                 for (int j = 1; j < fragTriCount; ++j)
  645.                 {
  646.                         Vec2 triVerts[3];
  647.                         triIndex = fragTris[j] * 3;
  648.  
  649.                         tmpVtx = pVerts[pInds[triIndex]];
  650.                         triVerts[0].x = vx.Dot(tmpVtx);
  651.                         triVerts[0].y = vy.Dot(tmpVtx);
  652.  
  653.                         tmpVtx = pVerts[pInds[triIndex + 1]];
  654.                         triVerts[1].x = vx.Dot(tmpVtx);
  655.                         triVerts[1].y = vy.Dot(tmpVtx);
  656.  
  657.                         tmpVtx = pVerts[pInds[triIndex + 2]];
  658.                         triVerts[2].x = vx.Dot(tmpVtx);
  659.                         triVerts[2].y = vy.Dot(tmpVtx);
  660.  
  661.                         // Find triangle's shared indices
  662.                         uint insertInd = 0;
  663.                         int indMin = -1, indMax = -1;
  664.  
  665.                         for (int k = 0; k < ptCount; ++k)
  666.                         {
  667.                                 for (int l = 0; l < 3; ++l)
  668.                                 {
  669.                                         // Matching triangle point
  670.                                         if (fragOutline[k] == triVerts[l])
  671.                                         {
  672.                                                 // Record which indices were matched
  673.                                                 insertInd += l;
  674.  
  675.                                                 // Store matching edge
  676.                                                 if (indMin == -1)
  677.                                                 {
  678.                                                         indMin = indMax = k;
  679.                                                 }
  680.                                                 else
  681.                                                 {
  682.                                                         indMin = min(indMin, k);
  683.                                                         indMax = max(indMax, k);
  684.                                                 }
  685.  
  686.                                                 break;
  687.                                         }
  688.                                 }
  689.  
  690.                                 // Can break if two indices found
  691.                                 if (indMin != indMax)
  692.                                 {
  693.                                         break;
  694.                                 }
  695.                         }
  696.  
  697.                         // Calculate triangle index to add (assuming results are 0+1, 0+2 or 1+2)
  698.                         insertInd = min(3 - insertInd, (uint)2);
  699.  
  700.                         // If at boundaries, can simply push
  701.                         if (indMin == 0 && indMax == ptCount - 1)
  702.                         {
  703.                                 fragOutline[ptCount] = triVerts[insertInd];
  704.                         }
  705.                         // May need to insert new vertex between shared vertices
  706.                         else
  707.                         {
  708.                                 for (int k = ptCount; k > indMax; --k)
  709.                                 {
  710.                                         fragOutline[k] = fragOutline[k - 1];
  711.                                 }
  712.  
  713.                                 fragOutline[indMax] = triVerts[insertInd];
  714.                         }
  715.  
  716.                         ++ptCount;
  717.                 }
  718.         }
  719.  
  720.         // Remove center offset from extracted mesh
  721.         const Vec3 center = vtxTrans.TransformVector(bbox.center);
  722.         const Vec2 centerOffset(center[xAxis], center[yAxis]);
  723.         const int fragOutlineSize = fragOutline.size();
  724.  
  725.         for (int i = 0; i < fragOutlineSize; ++i)
  726.         {
  727.                 fragOutline[i] -= centerOffset;
  728.         }
  729.  
  730.         return !fragOutline.empty();
  731. }//-------------------------------------------------------------------------------------------------
  732.  
  733. //--------------------------------------------------------------------------------------------------
  734. // Name: MergeFragmentTriangles
  735. // Desc: Merges connected triangles into a single fragment list
  736. //--------------------------------------------------------------------------------------------------
  737. void CBreakableGlassSystem::MergeFragmentTriangles(mesh_data* pPhysMesh, const int tri, const Vec3& thinRow, TGlassFragmentIndexArray& touchedTris, TGlassFragmentIndexArray& fragTris)
  738. {
  739.         // Check for limits
  740.         if (touchedTris.size() < touchedTris.max_size())
  741.         {
  742.                 // Search for existing term
  743.                 const uint size = touchedTris.size();
  744.                 int index = -1;
  745.  
  746.                 for (uint i = 0; i < size; ++i)
  747.                 {
  748.                         if (touchedTris[i] == tri)
  749.                         {
  750.                                 index = i;
  751.                                 break;
  752.                         }
  753.                 }
  754.  
  755.                 if (index == -1)
  756.                 {
  757.                         touchedTris.push_back(tri);
  758.  
  759.                         // Only support (fairly) flat surfaces
  760.                         const float flatThresh = 0.95f;
  761.  
  762.                         if (thinRow.Dot(pPhysMesh->pNormals[tri]) >= flatThresh)
  763.                         {
  764.                                 // Merge fragment
  765.                                 fragTris.push_back(tri);
  766.  
  767.                                 // Recursively merge all connected triangles
  768.                                 for (int i = 0; i < 3; ++i)
  769.                                 {
  770.                                         const int buddy = pPhysMesh->pTopology[tri].ibuddy[i];
  771.                                         if (buddy >= 0)
  772.                                         {
  773.                                                 MergeFragmentTriangles(pPhysMesh, buddy, thinRow, touchedTris, fragTris);
  774.                                         }
  775.                                 }
  776.                         }
  777.                 }
  778.         }
  779. }//-------------------------------------------------------------------------------------------------
  780.  
  781. //--------------------------------------------------------------------------------------------------
  782. // Name: ExtractUVCoords
  783. // Desc: Extracts the uv coord basis from the render mesh
  784. //--------------------------------------------------------------------------------------------------
  785. void CBreakableGlassSystem::ExtractUVCoords(IStatObj* const pStatObj, const primitives::box& bbox, const int thinAxis, SBreakableGlassPhysData& data)
  786. {
  787.         IIndexedMesh* pIndexedMesh = pStatObj ? pStatObj->GetIndexedMesh(true) : NULL;
  788.         CMesh* pMesh = pIndexedMesh ? pIndexedMesh->GetMesh() : NULL;
  789.  
  790.         if (pMesh)
  791.         {
  792.                 CRY_ASSERT(pMesh->m_pPositionsF16 == 0);
  793.                 const SMeshTexCoord* pUV = pMesh->m_pTexCoord;
  794.                 const SMeshTangents* pTangents = pMesh->m_pTangents;
  795.                 const Vec3* pPositions = pMesh->m_pPositions;
  796.  
  797.                 if (pTangents && pUV)
  798.                 {
  799.                         // Determine thinnest axis
  800.                         Vec3 thinRow = bbox.Basis.GetRow(idxmin3((float*)&bbox.size));
  801.  
  802.                         // Attempt UV coord extraction
  803.                         const uint numVerts = (uint)pMesh->GetVertexCount();
  804.                         CRY_ASSERT(numVerts == pMesh->GetTexCoordCount());
  805.  
  806.                         // Initialise our surface points with invalid data
  807.                         uint pts[6] =
  808.                         {
  809.                                 numVerts, numVerts, numVerts, // Front faces
  810.                                 numVerts, numVerts, numVerts  // Back faces
  811.                         };
  812.  
  813.                         for (int i = 0; i < numVerts; ++i)
  814.                         {
  815.                                 // Unpack surface data
  816.                                 const Vec3 normal = pTangents[i].GetN();
  817.                                 const Vec2 uv = pUV[i].GetUV();
  818.  
  819.                                 // Validate vertex is part of our surface
  820.                                 const float flatThreshold = 0.95f;
  821.                                 const float dot = thinRow.Dot(normal);
  822.  
  823.                                 if (dot >= flatThreshold)
  824.                                 {
  825.                                         const Vec2 uv0 = pUV[pts[0]].GetUV();
  826.  
  827.                                         // Store unique points to determine UV axes
  828.                                         if (pts[0] == numVerts)
  829.                                         {
  830.                                                 pts[0] = i;
  831.                                         }
  832.                                         else if (pts[1] == numVerts && uv0.x != uv.x)
  833.                                         {
  834.                                                 pts[1] = i;
  835.                                         }
  836.                                         else if (pts[2] == numVerts && uv0.y != uv.y)
  837.                                         {
  838.                                                 pts[2] = i;
  839.                                         }
  840.  
  841.                                         if (pts[1] < numVerts && pts[2] < numVerts)
  842.                                         {
  843.                                                 break;
  844.                                         }
  845.                                 }
  846.                                 else if (dot <= -flatThreshold)
  847.                                 {
  848.                                         const Vec2 uv3 = pUV[pts[3]].GetUV();
  849.  
  850.                                         // Same as above, but for back-faces
  851.                                         if (pts[3] == numVerts)
  852.                                         {
  853.                                                 pts[3] = i;
  854.                                         }
  855.                                         else if (pts[4] == numVerts && uv3.x != uv.x)
  856.                                         {
  857.                                                 pts[4] = i;
  858.                                         }
  859.                                         else if (pts[5] == numVerts && uv3.y != uv.y)
  860.                                         {
  861.                                                 pts[5] = i;
  862.                                         }
  863.  
  864.                                         if (pts[4] < numVerts && pts[5] < numVerts)
  865.                                         {
  866.                                                 break;
  867.                                         }
  868.                                 }
  869.                         }
  870.  
  871.                         // If we failed to get data from the front faces, swap with back
  872.                         if (pts[1] == numVerts || pts[2] == numVerts)
  873.                         {
  874.                                 pts[0] = pts[3];
  875.                                 pts[1] = pts[4];
  876.                                 pts[2] = pts[5];
  877.                         }
  878.  
  879.                         if (pts[1] < numVerts && pts[2] < numVerts)
  880.                         {
  881.                                 // Calculate vertex transformation constants
  882.                                 int xAxis = thinAxis == 0 ? 2 : (thinAxis == 1 ? 0 : 0);
  883.                                 int yAxis = thinAxis == 0 ? 1 : (thinAxis == 1 ? 2 : 1);
  884.  
  885.                                 const Vec3& vx = bbox.Basis.GetRow(xAxis);
  886.                                 const Vec3& vy = bbox.Basis.GetRow(yAxis);
  887.  
  888.                                 const Vec3 centerAxis = bbox.Basis.TransformVector(bbox.center);
  889.                                 Vec2 centerOffset(centerAxis[xAxis], centerAxis[yAxis]);
  890.  
  891.                                 // Transform and store best 3 points
  892.                                 data.uvBasis[0].x = vx.Dot(pPositions[pts[0]]) - centerOffset.x;
  893.                                 data.uvBasis[0].y = vy.Dot(pPositions[pts[0]]) - centerOffset.y;
  894.  
  895.                                 data.uvBasis[1].x = vx.Dot(pPositions[pts[1]]) - centerOffset.x;
  896.                                 data.uvBasis[1].y = vy.Dot(pPositions[pts[1]]) - centerOffset.y;
  897.  
  898.                                 data.uvBasis[2].x = vx.Dot(pPositions[pts[2]]) - centerOffset.x;
  899.                                 data.uvBasis[2].y = vy.Dot(pPositions[pts[2]]) - centerOffset.y;
  900.  
  901.                                 // Store associated UV coords
  902.                                 pUV[pts[0]].ExportTo(data.uvBasis[0].z, data.uvBasis[0].w);
  903.                                 pUV[pts[1]].ExportTo(data.uvBasis[1].z, data.uvBasis[1].w);
  904.                                 pUV[pts[2]].ExportTo(data.uvBasis[2].z, data.uvBasis[2].w);
  905.                         }
  906.                         else
  907.                         {
  908.                                 LOG_GLASS_ERROR("Failed to extract UV coordinates from mesh.");
  909.                         }
  910.                 }
  911.         }
  912. }//-------------------------------------------------------------------------------------------------
  913.  
  914. //--------------------------------------------------------------------------------------------------
  915. // Name: InterpolateUVCoords
  916. // Desc: Calculates the barycentric coordinates of a point and interpolates its UV coords
  917. //--------------------------------------------------------------------------------------------------
  918. Vec2 CBreakableGlassSystem::InterpolateUVCoords(const Vec4& uvPt0, const Vec4& uvPt1, const Vec4& uvPt2, const Vec2& pt)
  919. {
  920.         // Calculate barycentric coordinates of position
  921.         Vec2 A(uvPt0.x, uvPt0.y);
  922.         Vec2 B(uvPt1.x, uvPt1.y);
  923.         Vec2 C(uvPt2.x, uvPt2.y);
  924.  
  925.         Vec2 v0(C - A);
  926.         Vec2 v1(B - A);
  927.         Vec2 v2(pt - A);
  928.  
  929.         float dot00 = v0.Dot(v0);
  930.         float dot01 = v0.Dot(v1);
  931.         float dot02 = v0.Dot(v2);
  932.         float dot11 = v1.Dot(v1);
  933.         float dot12 = v1.Dot(v2);
  934.  
  935.         float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
  936.         float uLerp = (dot11 * dot02 - dot01 * dot12) * invDenom;
  937.         float vLerp = (dot00 * dot12 - dot01 * dot02) * invDenom;
  938.  
  939.         // Interpolate from known uv coordinates
  940.         Vec2 uvAB(uvPt1.z - uvPt0.z, uvPt1.w - uvPt0.w);
  941.         Vec2 uvAC(uvPt2.z - uvPt0.z, uvPt2.w - uvPt0.w);
  942.  
  943.         Vec2 uv;
  944.         uv.x = uvPt0.z + uvAC.x * uLerp + uvAB.x * vLerp;
  945.         uv.y = uvPt0.w + uvAC.y * uLerp + uvAB.y * vLerp;
  946.  
  947.         return uv;
  948. }//-------------------------------------------------------------------------------------------------
  949.  
  950. //--------------------------------------------------------------------------------------------------
  951. // Name: ValidateExtractedOutline
  952. // Desc: Performs a final validation pass on the extracted outline
  953. //--------------------------------------------------------------------------------------------------
  954. bool CBreakableGlassSystem::ValidateExtractedOutline(SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams)
  955. {
  956.         bool valid = true;
  957.  
  958.         // Check for overlapping points (leads to FPE during triangulation)
  959.         if (initParams.pInitialFrag && initParams.numInitialFragPts > 0)
  960.         {
  961.                 const Vec2* pPts = initParams.pInitialFrag;
  962.                 const uint numEdges = initParams.numInitialFragPts - 1;
  963.                 const float minEdgeLen = 0.0001f;
  964.  
  965.                 for (uint i = 0, j = 1; i < numEdges; ++i, ++j)
  966.                 {
  967.                         const Vec2 edge(pPts[i] - pPts[j]);
  968.  
  969.                         if (edge.GetLength2() < minEdgeLen)
  970.                         {
  971.                                 LOG_GLASS_ERROR("Extracted mesh has invalid edges.");
  972.  
  973.                                 valid = false;
  974.                                 break;
  975.                         }
  976.                 }
  977.         }
  978.  
  979.         // Check for overlapping UVs (leads to FPE during uv basis calculation)
  980.         if (valid)
  981.         {
  982.                 const Vec2 uvPtA(data.uvBasis[0].x, data.uvBasis[0].y);
  983.                 const Vec2 uvPtB(data.uvBasis[1].x, data.uvBasis[1].y);
  984.                 const Vec2 uvPtC(data.uvBasis[2].x, data.uvBasis[2].y);
  985.  
  986.                 const Vec2 uvEdge0(uvPtC - uvPtA);
  987.                 const Vec2 uvEdge1(uvPtB - uvPtA);
  988.  
  989.                 const float dot00 = uvEdge0.Dot(uvEdge0);
  990.                 const float dot01 = uvEdge0.Dot(uvEdge1);
  991.                 const float dot11 = uvEdge1.Dot(uvEdge1);
  992.                 const float epsilon = 0.001f;
  993.  
  994.                 if (fabs_tpl(dot00 * dot11 - dot01 * dot01) < epsilon)
  995.                 {
  996.                         LOG_GLASS_ERROR("Extracted mesh has invalid uv layout.");
  997.                         valid = false;
  998.                 }
  999.         }
  1000.  
  1001.         return valid;
  1002. }//-------------------------------------------------------------------------------------------------
  1003.  
  1004. //--------------------------------------------------------------------------------------------------
  1005. // Name: CalculateGlassBounds
  1006. // Desc: Calculates glass bounds from physics geometry
  1007. //--------------------------------------------------------------------------------------------------
  1008. void CBreakableGlassSystem::CalculateGlassBounds(const phys_geometry* const pPhysGeom, Vec3& size, Matrix34& matrix)
  1009. {
  1010.         // Find thinnest axis of physics geometry
  1011.         primitives::box bbox;
  1012.         pPhysGeom->pGeom->GetBBox(&bbox);
  1013.  
  1014.         Matrix33 basis = bbox.Basis.T();
  1015.         Vec3 halfSize = bbox.size;
  1016.         Vec3 center = bbox.center;
  1017.  
  1018.         const uint thinAxis = idxmin3(&halfSize.x);
  1019.  
  1020.         // Need to rotate so Z is our thin axis
  1021.         if (thinAxis < 2)
  1022.         {
  1023.                 float tempSize;
  1024.                 Matrix33 tempMat;
  1025.                 tempMat.SetIdentity();
  1026.  
  1027.                 // Calculate the rotation based on current facing dir
  1028.                 const Vec3 axes[2] =
  1029.                 {
  1030.                         Vec3Constants<float>::fVec3_OneX,
  1031.                         Vec3Constants<float>::fVec3_OneY
  1032.                 };
  1033.  
  1034.                 const Vec3& thinRow = bbox.Basis.GetRow(thinAxis);
  1035.                 const Vec3 localAxis = bbox.Basis.TransformVector(axes[thinAxis]);
  1036.                 float rot = (thinRow.Dot(localAxis) >= 0.0f) ? -gf_PI * 0.5f : gf_PI * 0.5f;
  1037.  
  1038.                 if (thinAxis == 0)
  1039.                 {
  1040.                         tempSize = halfSize.x;
  1041.                         halfSize.x = halfSize.z;
  1042.  
  1043.                         tempMat.SetRotationY(rot);
  1044.                 }
  1045.                 else
  1046.                 {
  1047.                         tempSize = halfSize.y;
  1048.                         halfSize.y = halfSize.z;
  1049.  
  1050.                         tempMat.SetRotationX(rot);
  1051.                 }
  1052.  
  1053.                 // Apply rotation to matrix and vectors
  1054.                 basis = basis * tempMat;
  1055.                 halfSize.z = tempSize;
  1056.         }
  1057.  
  1058.         // Assert minimum thickness
  1059.         const float halfMinThickness = 0.004f;
  1060.         halfSize.z = max(halfSize.z, halfMinThickness);
  1061.  
  1062.         size = halfSize * 2.0f;
  1063.  
  1064.         // Calculate locally offset bounds
  1065.         matrix.SetIdentity();
  1066.         matrix.SetTranslation(-halfSize);
  1067.  
  1068.         matrix = basis * matrix;
  1069.  
  1070.         matrix.AddTranslation(center);
  1071. }//-------------------------------------------------------------------------------------------------
  1072.  
  1073. //--------------------------------------------------------------------------------------------------
  1074. // Name: CalculateGlassAnchors
  1075. // Desc: Ensures mesh is within bounds and calculates anchor points
  1076. //--------------------------------------------------------------------------------------------------
  1077. void CBreakableGlassSystem::CalculateGlassAnchors(SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams)
  1078. {
  1079.         // Apply half-size offset to uv basis
  1080.         const Vec2& size = initParams.size;
  1081.         const Vec2 halfSize = size * 0.5f;
  1082.  
  1083.         for (uint i = 0; i < 3; ++i)
  1084.         {
  1085.                 data.uvBasis[i].x += halfSize.x;
  1086.                 data.uvBasis[i].y += halfSize.y;
  1087.         }
  1088.  
  1089.         // Only continue if non-default mesh used
  1090.         Vec2* pPts = initParams.pInitialFrag;
  1091.         const uint numPts = initParams.numInitialFragPts;
  1092.  
  1093.         if (pPts && numPts >= 3)
  1094.         {
  1095.                 Vec2 minPt = size;
  1096.                 Vec2 maxPt = Vec2Constants<float>::fVec2_Zero;
  1097.  
  1098.                 // Offset fragment data to glass working space
  1099.                 for (uint i = 0; i < numPts; ++i)
  1100.                 {
  1101.                         pPts[i] += halfSize;
  1102.  
  1103.                         // Update bounds
  1104.                         minPt.x = min(minPt.x, pPts[i].x);
  1105.                         minPt.y = min(minPt.y, pPts[i].y);
  1106.  
  1107.                         maxPt.x = max(maxPt.x, pPts[i].x);
  1108.                         maxPt.y = max(maxPt.y, pPts[i].y);
  1109.                 }
  1110.  
  1111.                 // Calculate any final offset towards working bounds
  1112.                 minPt.x = min(minPt.x, 0.0f);
  1113.                 minPt.y = min(minPt.y, 0.0f);
  1114.  
  1115.                 maxPt.x = max(maxPt.x - size.x, 0.0f);
  1116.                 maxPt.y = max(maxPt.y - size.y, 0.0f);
  1117.  
  1118.                 Vec2 offset;
  1119.                 offset.x = (maxPt.x > 0.0f) ? -maxPt.x : -minPt.x;
  1120.                 offset.y = (maxPt.y > 0.0f) ? -maxPt.y : -minPt.y;
  1121.  
  1122.                 // Adjust bounds if off
  1123.                 if (offset.GetLength2() > 0.0f)
  1124.                 {
  1125.                         for (uint i = 0; i < numPts; ++i)
  1126.                         {
  1127.                                 pPts[i] += offset;
  1128.                         }
  1129.  
  1130.                         for (uint i = 0; i < 3; ++i)
  1131.                         {
  1132.                                 data.uvBasis[i].x += offset.x;
  1133.                                 data.uvBasis[i].y += offset.y;
  1134.                         }
  1135.                 }
  1136.  
  1137.                 // Determine best anchor points
  1138.                 const uint maxNumAnchors = SBreakableGlassInitParams::MaxNumAnchors;
  1139.                 const uint numAnchors = min(numPts, maxNumAnchors);
  1140.                 initParams.numAnchors = numAnchors;
  1141.  
  1142.                 if (numPts <= maxNumAnchors)
  1143.                 {
  1144.                         // If few enough points, use them directly
  1145.                         for (uint i = 0; i < numAnchors; ++i)
  1146.                         {
  1147.                                 initParams.anchors[i] = pPts[i];
  1148.                         }
  1149.                 }
  1150.                 else
  1151.                 {
  1152.                         // Else, determine mesh points closest to corners
  1153.                         const Vec2 anchorPos[maxNumAnchors] =
  1154.                         {
  1155.                                 Vec2(0.0f,              0.0f),
  1156.                                 Vec2(0.0f,              initParams.size.y),
  1157.                                 Vec2(initParams.size.x, 0.0f),
  1158.                                 Vec2(initParams.size.x, initParams.size.y)
  1159.                         };
  1160.  
  1161.                         uint anchorPts[maxNumAnchors] = { 0, 0, 0, 0 };
  1162.  
  1163.                         for (uint i = 0; i < maxNumAnchors; ++i)
  1164.                         {
  1165.                                 float anchorDistSq = FLT_MAX;
  1166.  
  1167.                                 for (uint j = 0; j < numPts; ++j)
  1168.                                 {
  1169.                                         // Store point if new closest found
  1170.                                         const float distSq = (anchorPos[i] - pPts[j]).GetLength2();
  1171.  
  1172.                                         if (distSq < anchorDistSq)
  1173.                                         {
  1174.                                                 // Avoid duplicates
  1175.                                                 bool duplicate = false;
  1176.                                                 for (uint k = 0; k < i; ++k)
  1177.                                                 {
  1178.                                                         if (anchorPts[k] == j)
  1179.                                                         {
  1180.                                                                 duplicate = true;
  1181.                                                                 break;
  1182.                                                         }
  1183.                                                 }
  1184.  
  1185.                                                 if (!duplicate)
  1186.                                                 {
  1187.                                                         anchorDistSq = distSq;
  1188.                                                         anchorPts[i] = j;
  1189.                                                 }
  1190.                                         }
  1191.                                 }
  1192.  
  1193.                                 // Save closest pt to this corner
  1194.                                 initParams.anchors[i] = pPts[anchorPts[i]];
  1195.                         }
  1196.                 }
  1197.  
  1198.                 // Calculate approx. polygon center
  1199.                 Vec2 center(Vec2Constants<float>::fVec2_Zero);
  1200.                 const float fNumPts = (float)numPts;
  1201.  
  1202.                 for (uint i = 0; i < numPts; ++i)
  1203.                 {
  1204.                         center += pPts[i];
  1205.                 }
  1206.  
  1207.                 center *= __fres(fNumPts);
  1208.  
  1209.                 // Finally, shift anchors slightly towards center
  1210.                 const float anchorPtShift = 0.015f;
  1211.  
  1212.                 for (uint i = 0; i < numAnchors; ++i)
  1213.                 {
  1214.                         Vec2 toCenter = (center - initParams.anchors[i]).GetNormalized();
  1215.                         initParams.anchors[i] += toCenter * anchorPtShift;
  1216.                 }
  1217.         }
  1218. }//-------------------------------------------------------------------------------------------------
  1219.  
  1220. //--------------------------------------------------------------------------------------------------
  1221. // Name: PassImpactToNode
  1222. // Desc: Passes an impact through to the node
  1223. //--------------------------------------------------------------------------------------------------
  1224. void CBreakableGlassSystem::PassImpactToNode(IBreakableGlassRenderNode* pRenderNode, const EventPhysCollision* pPhysEvent)
  1225. {
  1226.         if (pRenderNode && pPhysEvent)
  1227.         {
  1228.                 // Special case for explosion
  1229.                 const int explosionMatId = -1;
  1230.                 const bool isExplosion = pPhysEvent->idmat[0] == explosionMatId;
  1231.  
  1232.                 if (isExplosion)
  1233.                 {
  1234.                         pRenderNode->ApplyExplosionToGlass(pPhysEvent);
  1235.                 }
  1236.                 // Standard impact with glass
  1237.                 else
  1238.                 {
  1239.                         pRenderNode->ApplyImpactToGlass(pPhysEvent);
  1240.                 }
  1241.         }
  1242. }//-------------------------------------------------------------------------------------------------
  1243.  
  1244. //--------------------------------------------------------------------------------------------------
  1245. // Name: InitGlassNode
  1246. // Desc: Creates and initialises a new glass render node
  1247. //--------------------------------------------------------------------------------------------------
  1248. IBreakableGlassRenderNode* CBreakableGlassSystem::InitGlassNode(const SBreakableGlassPhysData& physData, SBreakableGlassInitParams& initParams, const Matrix34& transMat)
  1249. {
  1250.         IBreakableGlassRenderNode* pRenderNode = (IBreakableGlassRenderNode*)gEnv->p3DEngine->CreateRenderNode(eERType_BreakableGlass);
  1251.  
  1252.         if (pRenderNode)
  1253.         {
  1254.                 // Determine node ID and add to list
  1255.                 const uint16 id = m_glassPlanes.Count();
  1256.  
  1257.                 IBreakableGlassRenderNode*& pGlassPlane = m_glassPlanes.AddNew();
  1258.                 pGlassPlane = pRenderNode;
  1259.  
  1260.                 // Basic initialisation
  1261.                 pRenderNode->SetId(id);
  1262.                 pRenderNode->SetRndFlags(physData.renderFlags);
  1263.                 pRenderNode->SetRndFlags(ERF_HIDDEN, false);
  1264.  
  1265.                 // By default, anchor to four corner points
  1266.                 // Note: This should read artist-placed data
  1267.                 if (initParams.numAnchors == 0)
  1268.                 {
  1269.                         const Vec2 anchorMin(0.01f, 0.01f);
  1270.                         const Vec2 anchorMax(initParams.size - anchorMin);
  1271.  
  1272.                         initParams.numAnchors = SBreakableGlassInitParams::MaxNumAnchors;
  1273.                         initParams.anchors[0].set(anchorMin.x, anchorMin.y);
  1274.                         initParams.anchors[1].set(anchorMin.x, anchorMax.y);
  1275.                         initParams.anchors[2].set(anchorMax.x, anchorMin.y);
  1276.                         initParams.anchors[3].set(anchorMax.x, anchorMax.y);
  1277.                 }
  1278.  
  1279.                 // Set params (which will force a state reset)
  1280.                 pRenderNode->InitialiseNode(initParams, physData.entityMat * transMat);
  1281.  
  1282.                 // Set cvar struct
  1283.                 pRenderNode->SetCVars(m_pGlassCVars);
  1284.         }
  1285.  
  1286.         return pRenderNode;
  1287. }//-------------------------------------------------------------------------------------------------
  1288.  
  1289. //--------------------------------------------------------------------------------------------------
  1290. // Name: ReleaseGlassNodes
  1291. // Desc: Releases and removes all glass nodes
  1292. //--------------------------------------------------------------------------------------------------
  1293. void CBreakableGlassSystem::ReleaseGlassNodes()
  1294. {
  1295.         AssertUnusedIfDisabled();
  1296.  
  1297.         const int numPlanes = m_glassPlanes.Count();
  1298.         for (int i = 0; i < numPlanes; ++i)
  1299.         {
  1300.                 if (IBreakableGlassRenderNode* pRenderNode = m_glassPlanes[i])
  1301.                 {
  1302.                         pRenderNode->ReleaseNode();
  1303.                         SAFE_DELETE(m_glassPlanes[i]);
  1304.                 }
  1305.         }
  1306.  
  1307.         m_glassPlanes.Free();
  1308. }//-------------------------------------------------------------------------------------------------
  1309.  
  1310. //--------------------------------------------------------------------------------------------------
  1311. // Name: ModifyEventToForceBreak
  1312. // Desc: If forced, then clamp event impulse to a value that will cause a reasonable break
  1313. //--------------------------------------------------------------------------------------------------
  1314. void CBreakableGlassSystem::ModifyEventToForceBreak(EventPhysCollision* pPhysEvent)
  1315. {
  1316.         if (pPhysEvent)
  1317.         {
  1318.                 const float forceBreakSpeed = 20.0f;
  1319.                 if (pPhysEvent->vloc[PHYSEVENT_COLLIDER].GetLengthSquared() < forceBreakSpeed * forceBreakSpeed)
  1320.                 {
  1321.                         pPhysEvent->vloc[PHYSEVENT_COLLIDER].NormalizeSafe();
  1322.                         pPhysEvent->vloc[PHYSEVENT_COLLIDER] *= forceBreakSpeed;
  1323.                 }
  1324.         }
  1325. }//-------------------------------------------------------------------------------------------------
  1326.  
  1327. //--------------------------------------------------------------------------------------------------
  1328. // Name: AssertUnusedIfDisabled
  1329. // Desc: Asserts that if the system is currently disabled, it should have no used nodes
  1330. //--------------------------------------------------------------------------------------------------
  1331. void CBreakableGlassSystem::AssertUnusedIfDisabled()
  1332. {
  1333. #ifndef RELEASE
  1334.         CRY_ASSERT_MESSAGE(m_enabled || m_glassPlanes.Count() == 0, "Breakable glass system is disabled, should not have any active planes.");
  1335. #endif
  1336. }//-------------------------------------------------------------------------------------------------
  1337.  
downloadBreakableGlassSystem.cpp Source code - Download CRYENGINE Source code
Related Source Codes/Software:
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top