/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. // Description : PhysicalWorld class implementation #include "StdAfx.h" #if ENABLE_CRY_PHYSICS #include "bvtree.h" #include "geometry.h" #include "overlapchecks.h" #include "intersectionchecks.h" #include "raybv.h" #include "raygeom.h" #include "geoman.h" #include "singleboxtree.h" #include "boxgeom.h" #include "cylindergeom.h" #include "capsulegeom.h" #include "spheregeom.h" #include "trimesh.h" #include "rigidbody.h" #include "physicalplaceholder.h" #include "physicalentity.h" #include "rigidentity.h" #include "particleentity.h" #include "livingentity.h" #include "wheeledvehicleentity.h" #include "articulatedentity.h" #include "ropeentity.h" #include "softentity.h" #include "tetrlattice.h" #include "physicalworld.h" #include "waterman.h" #if defined(AZ_RESTRICTED_PLATFORM) #undef AZ_RESTRICTED_SECTION #define PHYSICALWORLD_CPP_SECTION_1 1 #define PHYSICALWORLD_CPP_SECTION_2 2 #endif #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include <windows.h> #endif int g_dummyBuf[16]; // forward declarations of memory functions to access static data namespace TriMeshStaticData { void GetMemoryUsage(ICrySizer* pSizer); } CPhysicalWorld::CPhysicalEntityIt::CPhysicalEntityIt(CPhysicalWorld* pWorld) { m_pWorld = pWorld; m_refs = 0; MoveFirst(); } bool CPhysicalWorld::CPhysicalEntityIt::IsEnd() { return !m_pCurEntity; } IPhysicalEntity* CPhysicalWorld::CPhysicalEntityIt::Next() { if (IsEnd()) { return NULL; } if (m_pCurEntity->m_next) { m_pCurEntity = m_pCurEntity->m_next; } else if (m_pCurEntity->m_iSimClass < 0) { m_pCurEntity = 0; } else { int i; for (i = m_pCurEntity->m_iSimClass + 1; i < 8 && !m_pWorld->m_pTypedEnts[i]; i++) { ; } m_pCurEntity = i < 8 ? m_pWorld->m_pTypedEnts[i] : m_pWorld->m_pHiddenEnts; } return m_pCurEntity; } IPhysicalEntity* CPhysicalWorld::CPhysicalEntityIt::This() { if (IsEnd()) { return NULL; } return m_pCurEntity; } void CPhysicalWorld::CPhysicalEntityIt::MoveFirst() { int i; for (i = 0; i < 8 && !m_pWorld->m_pTypedEnts[i]; i++) { ; } m_pCurEntity = i < 8 ? m_pWorld->m_pTypedEnts[i] : m_pWorld->m_pHiddenEnts; } #if MAX_PHYS_THREADS <= 1 threadID g_physThreadId = THREADID_NULL; int IsPhysThread() { return iszero((int)(CryGetCurrentThreadId() - g_physThreadId)); } void MarkAsPhysThread() { g_physThreadId = CryGetCurrentThreadId(); } void MarkAsPhysWorkerThread(int*) { } int get_iCaller() { return IsPhysThread() ^ 1; } int get_iCaller_int() { return 0; } #else TLS_DEFINE(int*, g_pidxPhysThread); // The physics thread is indicated by a pointer to the value 0 // * The address of the value 0 is significant (see IsPhysThread) // * The address comes from a static declared here (g_ibufPhysThread) void MarkAsPhysThread() { static int g_ibufPhysThread[2] = { 1, 0 }; // NOTE: Content will be overwritten on first invocation... static int* g_lastPtr = g_ibufPhysThread; int* ptr = TLS_GET(int*, g_pidxPhysThread); if (ptr != g_lastPtr) { // Branchless version of: // ptr = (g_lastPtr == &g_ibufPhysThread[0] ? &g_ibufPhysThread[1] : &g_ibufPhysThread[0]) ptr = g_ibufPhysThread + (g_lastPtr - g_ibufPhysThread ^ 1); // Explanation of above: // g_lastPtr is expected to point to either &g_ibufPhysThread[0] or &g_ibufPhysThread[1] // // Therefore depending on the value of g_lastPtr the expression result is: // // g_lastPtr == &g_ibufPhysThread[0] g_lastPtr == &g_ibufPhysThread[1] // $ = g_lastPtr - g_ibufPhysThread 0 1 // $$ = $^1 1 0 // ptr = g_ibufPhysThread + $$ &g_ibufPhysThread[1] &g_ibufPhysThread[0] // This changes the content of the g_ibufPhysThread arrary to either {0, MAX_PHYS_THREADS} or {MAX_PHYS_THREADS, 0} // (Alternating each time a new thread is marked as the physics thread) *g_lastPtr = MAX_PHYS_THREADS; *ptr = 0; TLS_SET(g_pidxPhysThread, g_lastPtr = ptr); } } // Workers are indicated by populating g_pidxPhysThread with a pointer to the index of the worker // * The index may be 0-based or 1-based depending on the setting of MAIN_THREAD_NONWORKER // * FIRST_WORKER_THREAD is defined to 0 or 1 accordingly // * see CPhysicalWorld::TimeStep for worker creation/registration // // The pointer used must be an address on the stack on the thread procedure (see IsPhysThread) // * see CPhysicalWorld::ThreadProc for setup void MarkAsPhysWorkerThread(int* pidx) { TLS_SET(g_pidxPhysThread, pidx); } int IsPhysThread() { int dummy = 0; INT_PTR ptr = (INT_PTR)TLS_GET(INT_PTR, g_pidxPhysThread); // This is branchless logic to determine if this is a physics thread or not. // Equivalent to: return (ptr ? *ptr : 0); ptr += (INT_PTR)&dummy - ptr & (ptr - 1 >> sizeof(INT_PTR) * 8 - 1 ^ ptr >> sizeof(INT_PTR) * 8 - 1); return *(int*)ptr; // Explanation of the above: // // We'll begin with brackets: // ptr += (((INT_PTR)&dummy) - ptr) & (((ptr - 1) >> ((sizeof(INT_PTR) * 8) - 1)) ^ (ptr >> ((sizeof(INT_PTR) * 8) - 1))); // // Let: // a = (((INT_PTR)&dummy) - ptr) // b = ((ptr - 1) >> ((sizeof(INT_PTR) * 8) - 1)) // c = (ptr >> ((sizeof(INT_PTR) * 8) - 1)) // Then: // ptr += a & (b ^ c) // // What values can ptr hold? // A. ptr is nullptr if g_pidxPhysThread is not initialized for this thread (i.e. not a physics thread or worker) // B. ptr is the address of a stack local -- if the thread is a worker (see MarkAsPhysWorkerThread) // C. ptr is the address of a static (&g_ibufPhysThread[0 or 1]) -- if the thread was ever marked as the physics thread // // A) When ptr is nullptr (64-bit): // a = (&dummy - 0) = &dummy // b = (-1 >> 63) = Implementation specific behavior! MSVC propagates the MSB // = 0xFFFFFFFFFFFFFFFF // c = (0 >> 63) = 0 // // Thus: // ptr += &dummy & (0xFFFFFFFFFFFFFFFF ^ 0) // ptr += &dummy // // Or more fully: // ptr = ptr + &dummy // // Since ptr == nullptr: // ptr = &dummy // // And dummy == 0, so: // return 0 // // // B) When ptr is address of a stack local: // a = (&dummy - &local) // b = ((&local - 1) >> 63) = k (MSB of local) // c = (&local >> 63) = k (Also the MSB of local) // // Thus: // ptr += (&dummy - &local) & (k ^ k) // // Since (k ^ k) == 0: // ptr += 0 // // *ptr is the worker index: // return true (unless its the first worker and MAIN_THREAD_NONWORKER is defined) // // // C) When ptr is address of a static: // a = (&dummy - &static) // b = ((&static - 1) >> 63) = k (MSB of local) // c = (&static >> 63) = k (Also the MSB of local) // // Thus: // ptr += (&dummy - &static) & (k ^ k) // // Since (k ^ k) == 0: // ptr += 0 // // *ptr is the content of g_ibufPhysThread (see MarkAsPhysThread): // If the phys thread hasn't been switched then this is 0 // If the phys thread was switched then this is MAX_PHYS_THREADS // Note: Yes, this does mean that if you call MarkAsPhysThread and then immediately // call IsPhysThread it will return 0 (which doesn't seem right...) // (Unless the phys thread was switched twice in a row ... then it would alternate values in // g_ibufPhysThread buffer and potentially multiple threads would think they're the phys thread // ... is that intended?) // // // So basically the whole thing is equivalent to: // INT_PTR ptr = (INT_PTR)TLS_GET(INT_PTR, g_pidxPhysThread); // return (ptr ? *ptr : 0); // } int get_iCaller() { int dummy = MAX_PHYS_THREADS; INT_PTR ptr = (INT_PTR)TLS_GET(INT_PTR, g_pidxPhysThread); ptr += (INT_PTR)&dummy - ptr & (ptr - 1 >> sizeof(INT_PTR) * 8 - 1 ^ ptr >> sizeof(INT_PTR) * 8 - 1); return *(int*)ptr; } #endif int GetEntityProfileType(CPhysicalEntity* pent) { int i = pent->GetType(); if (iszero(i - PE_RIGID) | iszero(i - PE_ARTICULATED) | iszero(i - PE_WHEELEDVEHICLE) && pent->GetMassInv() == 0.0f) { return 10; } if (i == PE_ROPE && pent->m_pOuterEntity) { return 7 + iszero(pent->m_pOuterEntity->GetType() - PE_ARTICULATED); } else if (i == PE_ARTICULATED) { i += 9 + 2 - i & - iszero(pent->m_iSimClass - 4); } else if (i == PE_AREA) { return 11; } return i - 2; } CPhysicalWorld::CPhysicalWorld(ILog* pLog) : m_nWorkerThreads(0) { m_pLog = pLog; g_pPhysWorlds[g_nPhysWorlds] = this; g_nPhysWorlds = min(g_nPhysWorlds + 1, g_cryPhysicsMaxPhysWorlds); m_pEventClient = 0; g_pLockIntersect = &m_lockCaller[MAX_PHYS_THREADS]; ////////////////////////////////////////////////////////////////////////// // Initialize physics event listeners ////////////////////////////////////////////////////////////////////////// m_pPhysicsStreamer = 0; int i; for (i = 0; i < EVENT_TYPES_NUM; i++) { m_pEventClients[i][0] = m_pEventClients[i][1] = 0; } m_vars.nMaxSubsteps = 10; for (i = 0; i < NSURFACETYPES; i++) { m_BouncinessTable[i] = 0; m_FrictionTable[i] = 1.2f; m_DynFrictionTable[i] = 1.2f / 1.5f; m_SurfaceFlagsTable[i] = 0; } m_vars.nMaxStackSizeMC = 8; m_vars.maxMassRatioMC = 50.0f; m_vars.nMaxMCiters = 6000; m_vars.nMinMCiters = 4; m_vars.nMaxMCitersHopeless = 6000; m_vars.accuracyMC = 0.002f; m_vars.accuracyLCPCG = 0.005f; m_vars.nMaxContacts = 150; m_vars.nMaxPlaneContacts = 8; m_vars.nMaxPlaneContactsDistress = 4; m_vars.nMaxLCPCGsubiters = 120; m_vars.nMaxLCPCGsubitersFinal = 250; m_vars.nMaxLCPCGmicroiters = 12000; m_vars.nMaxLCPCGmicroitersFinal = 25000; m_vars.nMaxLCPCGiters = 5; m_vars.minLCPCGimprovement = 0.05f; m_vars.nMaxLCPCGFruitlessIters = 4; m_vars.accuracyLCPCGnoimprovement = 0.05f; m_vars.minSeparationSpeed = 0.02f; m_vars.maxwCG = 500.0f; m_vars.maxvCG = 500.0f; m_vars.maxvUnproj = 10.0f; m_vars.maxMCMassRatio = 100.0f; m_vars.maxMCVel = 15.0f; m_vars.maxLCPCGContacts = 100; m_vars.bFlyMode = 0; m_vars.iCollisionMode = 0; m_vars.bSingleStepMode = 0; m_vars.bDoStep = 0; m_vars.fixedTimestep = 0; m_vars.timeGranularity = 0.0001f; m_vars.maxWorldStep = 0.2f; m_vars.iDrawHelpers = 0; m_vars.iOutOfBounds = raycast_out_of_bounds | get_entities_out_of_bounds; #if !defined(MOBILE) m_vars.nMaxSubsteps = 5; #else m_vars.nMaxSubsteps = 2; #endif m_vars.nMaxSurfaces = NSURFACETYPES; m_vars.maxContactGap = 0.01f; m_vars.maxContactGapPlayer = 0.01f; m_vars.bProhibitUnprojection = 1;//2; m_vars.bUseDistanceContacts = 0; m_vars.unprojVelScale = 10.0f; m_vars.maxUnprojVel = 2.5f; m_vars.maxUnprojVelRope = 10.0f; m_vars.gravity.Set(0, 0, -9.8f); m_vars.nGroupDamping = 8; m_vars.groupDamping = 0.5f; m_vars.nMaxSubstepsLargeGroup = 5; m_vars.nBodiesLargeGroup = 30; m_vars.bEnforceContacts = 1;//1; m_vars.bBreakOnValidation = 0; m_vars.bLogActiveObjects = 0; m_vars.bMultiplayer = 0; m_vars.bProfileEntities = 0; m_vars.bProfileFunx = 0; m_vars.bProfileGroups = 0; m_vars.minBounceSpeed = 1; m_vars.nGEBMaxCells = 1024; m_vars.maxVel = 100.0f; m_vars.maxVelPlayers = 150.0f; m_vars.maxVelBones = 10.0f; m_vars.bSkipRedundantColldet = 1; m_vars.penaltyScale = 0.3f; m_vars.maxContactGapSimple = 0.03f; m_vars.bLimitSimpleSolverEnergy = 1; m_vars.nMaxEntityCells = 300000; m_vars.nMaxAreaCells = 128; m_vars.nMaxEntityContacts = 256; m_vars.tickBreakable = 0.1f; m_vars.approxCapsLen = 1.2f; m_vars.nMaxApproxCaps = 7; m_vars.bCGUnprojVel = 0; m_vars.bLogLatticeTension = 0; m_vars.nMaxLatticeIters = 100000; m_vars.bLogStructureChanges = 1; m_vars.bPlayersCanBreak = 0; m_vars.bMultithreaded = 0; m_vars.breakImpulseScale = 1.0f; m_vars.jointGravityStep = 1.0f; m_vars.jointDmgAccum = 2.0f; m_vars.jointDmgAccumThresh = 0.2f; m_vars.massLimitDebris = 1E10f; m_vars.maxSplashesPerObj = 0; m_vars.splashDist0 = 7.0f; m_vars.minSplashForce0 = 15000.0f; m_vars.minSplashVel0 = 4.5f; m_vars.splashDist1 = 30.0f; m_vars.minSplashForce1 = 150000.0f; m_vars.minSplashVel1 = 10.0f; m_vars.lastTimeStep = 0; m_vars.numThreads = 2; m_vars.physCPU = 4; m_vars.physWorkerCPU = 1; m_vars.helperOffset.zero(); m_vars.timeScalePlayers = 1.0f; MARK_UNUSED m_vars.flagsColliderDebris; m_vars.flagsANDDebris = -1; m_vars.bDebugExplosions = 0; m_vars.ticksPerSecond = 3000000000U; #if USE_IMPROVED_RIGID_ENTITY_SYNCHRONISATION m_vars.netInterpTime = 0.1f; m_vars.netExtrapMaxTime = 0.5f; m_vars.netSequenceFrequency = 256; m_vars.netDebugDraw = 0; #else m_vars.netMinSnapDist = 0.1f; m_vars.netVelSnapMul = 0.1f; m_vars.netMinSnapDot = 0.99f; m_vars.netAngSnapMul = 0.01f; m_vars.netSmoothTime = 5.0f; #endif m_vars.bEntGridUseOBB = 1; m_vars.nStartupOverloadChecks = 20; m_vars.maxRopeColliderSize = #if defined(MOBILE) 100; #else 0; #endif m_vars.breakageMinAxisInertia = 0.01f; memset(m_grpProfileData, 0, sizeof(m_grpProfileData)); m_grpProfileData[ 0].pName = "Rigid bodies"; m_grpProfileData[ 1].pName = "Wheeled vehicles"; m_grpProfileData[ 2].pName = "Actors"; m_grpProfileData[ 3].pName = "Physicalized particles"; m_grpProfileData[ 4].pName = "Ragdolls"; m_grpProfileData[ 5].pName = "Ropes (standalone)"; m_grpProfileData[ 6].pName = "Cloth"; m_grpProfileData[ 7].pName = "Ropes (vegetation)"; m_grpProfileData[ 8].pName = "Ropes (character)"; m_grpProfileData[ 9].pName = "Character skeletons"; m_grpProfileData[10].pName = "Kinematic objects"; m_grpProfileData[11].pName = "Areas"; m_grpProfileData[12].pName = "Entities total"; m_grpProfileData[11].nCallsLast = 1; m_grpProfileData[13].pName = "Queued requests"; m_grpProfileData[12].nCallsLast = 1; m_grpProfileData[14].pName = "World step total"; m_grpProfileData[13].nCallsLast = 1; memset(m_JobProfileInfo, 0, sizeof(m_JobProfileInfo)); m_JobProfileInfo[0].pName = "rb_geomintersect"; m_JobProfileInfo[1].pName = "solver "; m_JobProfileInfo[2].pName = "cloth "; m_JobProfileInfo[3].pName = "rope "; m_JobProfileInfo[4].pName = "featherstone"; m_JobProfileInfo[5].pName = "rb_primintersect"; m_pFirstEventChunk = m_pCurEventChunk = (EventChunk*)(new char[sizeof(EventChunk) + EVENT_CHUNK_SZ]); m_pRwiHitsTail = (m_pRwiHitsHead = (m_pRwiHitsPool = new ray_hit[257]) + 1) + 255; Init(); } CPhysicalWorld::~CPhysicalWorld() { Shutdown(); ////////////////////////////////////////////////////////////////////////// // Deinitialize physics event listeners ////////////////////////////////////////////////////////////////////////// for (int i = 0; i < EVENT_TYPES_NUM; i++) { EventClient* pClient, * pNextClient; for (int bLogged = 0; bLogged < 2; bLogged++) { for (pClient = m_pEventClients[i][bLogged]; pClient; pClient = pNextClient) { pNextClient = pClient->next; delete pClient; } m_pEventClients[i][bLogged] = 0; } } ////////////////////////////////////////////////////////////////////////// memset(g_StaticPhysicalEntity.m_pUsedParts = new int[MAX_PHYS_THREADS + 1][16], 0, sizeof(*g_StaticPhysicalEntity.m_pUsedParts)); int i; for (i = 0; i < g_nPhysWorlds && g_pPhysWorlds[i] != this; i++) { ; } if (i < g_nPhysWorlds) { g_nPhysWorlds--; } for (; i < g_nPhysWorlds; i++) { g_pPhysWorlds[i] = g_pPhysWorlds[i + 1]; } if (!g_nPhysWorlds) { g_pPhysWorlds[0] = 0; } m_nPumpLoggedEventsHits = 0; delete[] m_pRwiHitsPool; delete[] ((char*)m_pFirstEventChunk); } void CPhysicalWorld::Init() { InitGeoman(); m_pTmpEntList = 0; m_pTmpEntList1 = 0; m_pTmpEntList2 = 0; m_pGroupMass = 0; m_pMassList = 0; m_pGroupIds = 0; m_pGroupNums = 0; m_nEnts = 0; m_nEntsAlloc = 0; m_bEntityCountReserved = 0; m_pEntGrid = 0; m_gthunks = 0; m_thunkPoolSz = 0; m_timePhysics = m_timeSurplus = 0; m_timeSnapshot[0] = m_timeSnapshot[1] = m_timeSnapshot[2] = m_timeSnapshot[3] = 0; m_iTimeSnapshot[0] = m_iTimeSnapshot[1] = m_iTimeSnapshot[2] = m_iTimeSnapshot[3] = 0; m_iTimePhysics = 0; int i; for (i = 0; i < 8; i++) { m_pTypedEnts[i] = m_pTypedEntsPerm[i] = 0; m_updateTimes[i] = 0; } m_pHiddenEnts = 0; for (i = 0; i < 10; i++) { m_nTypeEnts[i] = 0; } for (i = 0; i <= MAX_PHYS_THREADS; i++) { m_pHeightfield[i] = 0; m_lockCaller[i] = 0; m_threadData[i].szList = 0; m_threadData[i].pTmpEntList = 0; m_threadData[i].szTmpPartBVList = m_threadData[i].nTmpPartBVs = 0; m_threadData[i].pTmpPartBVList = 0; m_threadData[i].pTmpPartBVListOwner = 0; m_threadData[i].pTmpPrecompEntsLE = 0; m_threadData[i].nPrecompEntsAllocLE = 0; m_threadData[i].pTmpPrecompPartsLE = 0; m_threadData[i].nPrecompPartsAllocLE = 0; m_threadData[i].pTmpNoResponseContactLE = 0; m_threadData[i].nNoResponseAllocLE = 0; } m_zGran = 1.0f / 16; m_rzGran = 16; m_iNextId = 1; m_iNextIdDown = m_lastExtId = m_nExtIds = 0; m_pEntsById = 0; m_nIdsAlloc = 0; m_nExplVictims = m_nExplVictimsAlloc = 0; m_pPlaceholders = 0; m_pPlaceholderMap = 0; m_nPlaceholders = m_nPlaceholderChunks = 0; m_iLastPlaceholder = -1; m_nProfiledEnts = 0; m_iSubstep = 0; m_bWorldStep = 0; m_nDynamicEntitiesDeleted = 0; m_nQueueSlots = m_nQueueSlotsAlloc = 0; m_nQueueSlotsAux = m_nQueueSlotsAllocAux = 0; m_nQueueSlotSize = m_nQueueSlotSizeAux = QUEUE_SLOT_SZ; m_pQueueSlots = 0; m_pQueueSlotsAux = 0; m_pGlobalArea = 0; m_nAreas = m_nBigAreas = 0; m_pDeletedAreas = 0; memset(m_pTypedAreas, 0, sizeof(m_pTypedAreas)); m_numNonWaterAreas = m_numGravityAreas = 0; m_pActiveArea = 0; m_iLastLogPump = 0; m_pFreeContact = m_pLastFreeContact = CONTACT_END(m_pFreeContact); m_nFreeContacts = m_nContactsAlloc = 0; m_pFreeEntPart = m_pLastFreeEntPart = CONTACT_END(m_pFreeEntPart); m_nFreeEntParts = m_nEntPartsAlloc = 0; m_pExpl = 0; m_nExpl = m_nExplAlloc = 0; m_idExpl = 0; m_pDeformingEnts = 0; m_nDeformingEnts = m_nDeformingEntsAlloc = 0; m_pRenderer = 0; m_lockDeformingEntsList = 0; m_lockAreas = 0; m_lockActiveAreas = 0; m_matWater = -1; m_bCheckWaterHits = 0; g_StaticPhysicalEntity.m_pWorld = this; g_StaticPhysicalEntity.m_id = -2; memset(&CPhysicalEntity::m_defpart, 0, sizeof(geom)); CPhysicalEntity::m_defpart.q.SetIdentity(); CPhysicalEntity::m_defpart.scale = 1.0f; m_rwiQueueHead = -1; m_rwiQueueTail = -64; m_rwiQueueSz = m_rwiQueueAlloc = 0; m_rwiQueue = 0; m_lockRwiQueue = 0; m_pRwiHitsTail = (m_pRwiHitsHead = m_pRwiHitsPool + 1) + 255; memset(m_pRwiHitsPool, 0, 257 * sizeof(ray_hit)); for (i = 0; i < 255; i++) { m_pRwiHitsHead[i].next = m_pRwiHitsHead + i + 1; } m_pRwiHitsHead[i].next = m_pRwiHitsHead; m_rwiPoolEmpty = 1; m_rwiHitsPoolSize = 256; m_lockRwiHitsPool = 0; m_lockTPR = 0; m_pwiQueueHead = -1; m_pwiQueueTail = 0; m_pwiQueueSz = m_pwiQueueAlloc = 0; m_pwiQueue = 0; m_lockPwiQueue = 0; m_breakQueueHead = -1; m_breakQueueTail = 0; m_breakQueueSz = m_breakQueueAlloc = 0; m_breakQueue = 0; m_lockBreakQueue = 0; m_pWaterMan = 0; m_idStep = 0; m_nEntListAllocs = 0; m_curGroupMass = 0; m_pPODCells = &(m_pDummyPODcell = &m_dummyPODcell); m_dummyPODcell.lifeTime = 1E10f; m_dummyPODcell.zlim.set(1E10f, -1E10f); m_log2PODscale = 0; m_iActivePODCell0 = -1; m_bHasPODGrid = 0; m_iLastPODUpdate = 1; m_nProfileFunx = m_nProfileFunxAlloc = 0; m_pFuncProfileData = 0; m_posViewer.zero(); for (i = 0; i <= MAX_PHYS_THREADS; i++) { #ifndef _RELEASE m_nGEA[i] = 0; #endif m_prevGEABBox[i][0].Set(1E10f, 1E10f, 1E10f); m_prevGEABBox[i][1].zero(); m_prevGEAobjtypes[i] = m_nprevGEAEnts[i] = 0; m_BBoxPlayerGroup[i][0] = m_BBoxPlayerGroup[i][1] = Vec3(1e10f); } for (i = 0; i < EVENT_TYPES_NUM; i++) { m_pFreeEvents[i] = 0; m_nEvents[i] = 0; } m_pEventFirst = m_pEventLast = 0; m_pCurEventChunk = m_pFirstEventChunk; m_szCurEventChunk = 0; m_pFirstEventChunk->next = 0; m_pEntBeingDeleted = 0; m_bGridThunksChanged = 0; m_bUpdateOnlyFlagged = 0; m_lockStep = 0; m_lockQueue = 0; m_lockGrid = 0; m_lockList = 0; m_lockEventsQueue = 0; m_lockEntIdList = 0; m_lockEventClients = 0; m_lockPODGrid = 0; m_lockFuncProfiler = 0; m_lockEntProfiler = 0; m_lockContacts = 0; m_lockEntParts = 0; m_idThread = m_idPODThread = threadID(THREADID_NULL); m_nOnDemandListFailures = 0; m_iLastPODUpdate = 1; m_dummyPODcell.zlim[0] = 1E10f; m_dummyPODcell.zlim[1] = 1E10f; m_lockNextEntityGroup = 0; m_lockMovedEntsList = 0; m_lockPlayerGroups = 0; m_lockAuxStepEnt = 0; m_lockWaterMan = 0; m_bMassDestruction = 0; m_nSlowFrames = 0; m_oldThunks = 0; m_lockOldThunks = 0; m_oldEntParts = 0; // Some things, like RWI, depend on m_pTmpEntList being intialized. In the rare case // where we have no physics objects created it will be empty, so to make sure we can // still place the first object, allocate a single element. CPhysicalEntity** physEnt; ReallocTmpEntList(physEnt, 0, 1); } void CPhysicalWorld::InitGThunksPool() { // Round up to multiples of 64k const int bytes = 32768 * sizeof(pe_gridthunk); const int numPages = (bytes + (64 * 1024 - 1)) >> 16; const int nStartingThunks = (numPages << 16) / sizeof(pe_gridthunk); AllocGThunksPool(nStartingThunks); } static pe_gridthunk* AllocateThunks(int& nNewSize) { // Round up to multiples of 64k size_t bytes = sizeof(pe_gridthunk) * nNewSize; size_t numPages = (bytes + 64 * 1024 - 1) >> 16; nNewSize = (numPages << 16) / sizeof(pe_gridthunk); void* memory = CryModuleMalloc(numPages << 16); return new (memory) pe_gridthunk[nNewSize]; } static void FreeThunks(pe_gridthunk* thunks) { // NB: Dont care about the destructors CryModuleFree(thunks); } ////////////////////////////////////////////////////////////////////////// void CPhysicalWorld::AllocGThunksPool(int nNewSize) { if (nNewSize == m_thunkPoolSz) { return; } if (nNewSize < m_thunkPoolSz) { DeallocGThunksPool(); } pe_gridthunk* prevthunks = m_gthunks, * gthunks; gthunks = AllocateThunks(nNewSize); if (prevthunks) { // Reallocate NO_BUFFER_OVERRUN memcpy(gthunks, prevthunks, m_thunkPoolSz * sizeof(pe_gridthunk)); memset(gthunks + m_thunkPoolSz, 0, (nNewSize - m_thunkPoolSz) * sizeof(pe_gridthunk)); int ithunk; for (ithunk = m_thunkPoolSz; ithunk < nNewSize - 1; ithunk++) { gthunks[ithunk].inextOwned = ithunk + 1; } gthunks[ithunk].inextOwned = 0; m_iFreeGThunk0 = m_thunkPoolSz; m_thunkPoolSz = nNewSize; m_gthunks = gthunks; if (m_bWorldStep) { WriteLock lock(m_lockOldThunks); *(pe_gridthunk**)prevthunks = m_oldThunks; m_oldThunks = prevthunks; } else { FreeThunks(prevthunks); } } else { // First Time m_thunkPoolSz = nNewSize; memset(gthunks, 0, m_thunkPoolSz * sizeof(pe_gridthunk)); int i; for (i = 0; i < m_thunkPoolSz - 1; i++) { gthunks[i].inextOwned = i + 1; } gthunks[i].inextOwned = 0; gthunks[0].inext = gthunks[0].iprev = 0; m_iFreeGThunk0 = 1; m_gthunks = gthunks; } } ////////////////////////////////////////////////////////////////////////// void CPhysicalWorld::DeallocGThunksPool() { if (m_gthunks) { FreeThunks(m_gthunks); } m_gthunks = 0; m_thunkPoolSz = m_iFreeGThunk0 = 0; } ////////////////////////////////////////////////////////////////////////// void CPhysicalWorld::Cleanup() { Shutdown(1); Init(); } void CPhysicalWorld::Shutdown(int bDeleteGeometries) { WriteLock lock(m_lockStep); for (int i = m_nWorkerThreads - 1; i >= 0; i--) { m_threads[i]->bStop = 1, m_threadStart[i].Set(), m_threadDone[i].Wait(); GetISystem()->GetIThreadTaskManager()->UnregisterTask(m_threads[i]); delete m_threads[i]; } int i; CPhysicalEntity* pent, * pent_next; m_bMassDestruction = 1; for (i = 0; i < 8; i++) { for (pent = m_pTypedEnts[i]; pent; pent = pent_next) { pent_next = pent->m_next; pent->Delete(); } m_pTypedEnts[i] = 0; m_pTypedEntsPerm[i] = 0; } for (pent = m_pHiddenEnts; pent; pent = pent_next) { pent_next = pent->m_next; pent->Delete(); } m_pHiddenEnts = 0; CPhysArea* pArea, * pNextArea; for (pArea = m_pDeletedAreas; pArea; pArea = pNextArea) { pNextArea = pArea->m_next; delete pArea; } m_pDeletedAreas = 0; delete[] m_pDeformingEnts; m_pDeformingEnts = 0; m_nEnts = m_nEntsAlloc = 0; m_bEntityCountReserved = 0; for (i = 0; i < m_nPlaceholderChunks; i++) { if (m_pPlaceholders[i]) { delete[] m_pPlaceholders[i]; } } if (m_pPlaceholders) { delete[] m_pPlaceholders; } m_pPlaceholders = 0; if (m_pPlaceholderMap) { delete[] m_pPlaceholderMap; } m_pPlaceholderMap = 0; m_nPlaceholderChunks = m_nPlaceholders = 0; m_iLastPlaceholder = -1; if (m_pEntGrid) { DeallocateGrid(m_pEntGrid, m_entgrid.size); } m_pEntGrid = 0; DeallocGThunksPool(); if (m_pTmpEntList) { delete[] m_pTmpEntList; } m_pTmpEntList = 0; if (m_pTmpEntList1) { delete[] m_pTmpEntList1; } m_pTmpEntList1 = 0; if (m_pTmpEntList2) { delete[] m_pTmpEntList2; } m_pTmpEntList2 = 0; if (m_pGroupMass) { delete[] m_pGroupMass; } m_pGroupMass = 0; if (m_pMassList) { delete[] m_pMassList; } m_pMassList = 0; if (m_pGroupIds) { delete[] m_pGroupIds; } m_pGroupIds = 0; if (m_pGroupNums) { delete[] m_pGroupNums; } m_pGroupNums = 0; if (m_pEntsById) { delete[] m_pEntsById; } m_pEntsById = 0; m_nIdsAlloc = 0; m_cubeMapStatic.Free(); m_cubeMapDynamic.Free(); if (m_nExplVictimsAlloc) { delete[] m_pExplVictims; m_pExplVictims = 0; delete[] m_pExplVictimsFrac; m_pExplVictimsFrac = 0; delete[] m_pExplVictimsImp; m_pExplVictimsImp = 0; } for (i = 1; i < MAX_PHYS_THREADS; i++) { delete[] m_threadData[i].pTmpEntList; } for (i = 0; i <= MAX_PHYS_THREADS; i++) { m_threadData[i].szList = 0; m_threadData[i].pTmpEntList = 0; } m_pEventFirst = m_pEventLast = 0; EventChunk* pChunk, * pChunkNext; for (pChunk = m_pFirstEventChunk->next; pChunk; pChunk = pChunkNext) { pChunkNext = pChunk->next; delete[] ((char*)pChunk); } m_szCurEventChunk = 0; m_pCurEventChunk = m_pFirstEventChunk; for (i = 0; i < m_nQueueSlotsAlloc; i++) { delete[] m_pQueueSlots[i]; } delete [] m_pQueueSlots; m_pQueueSlots = 0; m_nQueueSlots = m_nQueueSlotsAlloc = 0; m_nQueueSlotSize = m_nQueueSlotSizeAux = QUEUE_SLOT_SZ; m_pQueueSlots = 0; for (i = 0; i < m_nQueueSlotsAllocAux; i++) { delete[] m_pQueueSlotsAux[i]; } delete [] m_pQueueSlotsAux; m_nQueueSlotsAux = m_nQueueSlotsAllocAux = 0; m_pQueueSlotsAux = 0; if (m_pExpl) { for (i = 0; i < m_nExpl; i++) { m_pExpl[i].pGeom->Release(); } delete[] m_pExpl; m_pExpl = 0; } m_nExpl = m_nExplAlloc = 0; m_idExpl = 0; if (m_rwiQueue) { delete[] m_rwiQueue; m_rwiQueue = 0; } m_rwiQueueHead = -1; m_rwiQueueTail = -64; m_rwiQueueSz = m_rwiQueueAlloc = 0; m_rwiQueue = 0; m_lockRwiQueue = 0; if (m_pRwiHitsHead) { ray_hit* phit, * pchunk; for (phit = (pchunk = m_pRwiHitsPool) + 1; phit->next != m_pRwiHitsPool + 1; phit = phit->next) { if (!phit->next[-1].next) // means phit->next[-1] is a chunk header { pchunk->next = phit->next - 1; pchunk = phit->next - 1; } } for (pchunk = m_pRwiHitsPool->next; pchunk; pchunk = phit) { phit = pchunk->next; delete[] pchunk; } m_pRwiHitsTail = m_pRwiHitsHead; m_rwiHitsPoolSize = 0; } if (m_pwiQueue) { delete[] m_pwiQueue; m_pwiQueue = 0; } m_pwiQueueHead = -1; m_pwiQueueTail = 0; m_pwiQueueSz = m_pwiQueueAlloc = 0; m_pwiQueue = 0; m_lockPwiQueue = 0; if (m_breakQueue) { delete[] m_breakQueue; m_breakQueue = 0; } m_breakQueueHead = -1; m_breakQueueTail = 0; m_breakQueueSz = m_breakQueueAlloc = 0; m_breakQueue = 0; m_lockBreakQueue = 0; DestroyWaterManager(); if (bDeleteGeometries) { SetHeightfieldData(0); ShutDownGeoman(); delete m_pGlobalArea; m_pGlobalArea = NULL; } m_bMassDestruction = 0; for (i = 0; i <= MAX_PHYS_THREADS; i++) { delete[] m_threadData[i].pTmpPartBVList, m_threadData[i].pTmpPartBVList = 0; m_threadData[i].szTmpPartBVList = m_threadData[i].nTmpPartBVs = 0; m_threadData[i].pTmpPartBVListOwner = 0; delete[] m_threadData[i].pTmpPrecompEntsLE, m_threadData[i].pTmpPrecompEntsLE = 0; delete[] m_threadData[i].pTmpPrecompPartsLE, m_threadData[i].pTmpPrecompPartsLE = 0; delete[] m_threadData[i].pTmpNoResponseContactLE, m_threadData[i].pTmpNoResponseContactLE = 0; m_threadData[i].nPrecompPartsAllocLE = m_threadData[i].nPrecompEntsAllocLE = 0; m_threadData[i].nNoResponseAllocLE = 0; } FreeObjPool(m_pFreeContact, m_nFreeContacts, m_nContactsAlloc); FreeObjPool(m_pFreeEntPart, m_nFreeEntParts, m_nEntPartsAlloc); CleanupContactSolvers(); FlushOldThunks(); delete [] m_pFuncProfileData; m_pFuncProfileData = NULL; m_nProfileFunx = m_nProfileFunxAlloc = 0; CTriMesh::CleanupGlobalLoadState(); CPhysicalEntity::CleanupGlobalState(); } ///////////////////////////////////////////////////////////////////////////////////////////////////// void CPhysicalWorld::RemoveFromAllColliders(CPhysicalEntity* pEntity) { CPhysicalEntity* pent, * pent_next; for (int i = 0; i < 8; i++) { for (pent = m_pTypedEnts[i]; pent; pent = pent_next) { pent_next = pent->m_next; if (pent == pEntity) { continue; } pent->RemoveColliderMono(pEntity); } } for (pent = m_pHiddenEnts; pent; pent = pent_next) { pent_next = pent->m_next; if (pent == pEntity) { continue; } pent->RemoveColliderMono(pEntity); } } void CPhysicalWorld::RemovePhysicalEntityFromRayCastEvent(CPhysicalEntity* pEntity, EventPhysRWIResult* pEventRWI) { // You should know that the buffer pointed by "pEventRWI->pHits" is actually owned // somewhere else by // struct IPhysicalWorld // { // struct SRWIParams // { // ray_hit* hits; // } // } int i = 0; while (i < pEventRWI->nHits) { if (pEntity == pEventRWI->pHits[i].pCollider) { //Shift pHits. Doesn't need to be high performance, so brute force works ok. int j = i; int k = j+1; while (k < pEventRWI->nHits) { pEventRWI->pHits[j] = pEventRWI->pHits[k]; j++; k++; } pEventRWI->nHits--; } else { i++; } } } void CPhysicalWorld::RemoveFromAllEvents(CPhysicalEntity* pEntity) { // Things you should know... // Memory for events is allocated in the linked list that starts with m_pFirstEventChunk. // The table that contains all available events is "EventPhys* m_pFreeEvents[EVENT_TYPES_NUM]". // All valid events per frame (when PumpLoggedEvents() is called) are found in: // EventPhys* m_pEventFirst, until the last event equals m_pEventLast. // Our goal is to Hijack the sequence that starts with m_pEventFirst and skip all events // that reference @pEntity. WriteLock lock(m_lockEventsQueue); EventPhys* pPrevEvent = nullptr; EventPhys* pEvent = m_pEventFirst; while (pEvent) { //Stereo Events hold reference to two physical entities CPhysicalEntity* pEntity0 = nullptr; CPhysicalEntity* pEntity1 = nullptr; if (pEvent->idval <= EventPhysCollision::id) //Stereo Event { EventPhysStereo* pStereoEvent = (EventPhysStereo*)pEvent; pEntity0 = (CPhysicalEntity*)pStereoEvent->pEntity[0]; pEntity1 = (CPhysicalEntity*)pStereoEvent->pEntity[1]; } else //Mono Events hold reference to one physical entity { EventPhysMono* pMonoEvent = (EventPhysMono*)pEvent; pEntity0 = (CPhysicalEntity*)pMonoEvent->pEntity; if (pEvent->idval == EventPhysRWIResult::id) { RemovePhysicalEntityFromRayCastEvent(pEntity, (EventPhysRWIResult*)pEvent); } } bool removeEvent = false; if ((pEntity0 && (pEntity0 == pEntity)) || (pEntity1 && (pEntity1 == pEntity))) { removeEvent = true; } if (!removeEvent) { pPrevEvent = pEvent; pEvent = pEvent->next; continue; } if ((pEvent->idval == EventPhysCreateEntityPart::id) && (pEntity0->m_iDeletionTime)) { DestroyPhysicalEntity(((EventPhysCreateEntityPart*)pEvent)->pEntNew, 0, 1); } if (pEvent == m_pEventFirst) { if (pEvent == m_pEventLast) { m_pEventLast = pEvent->next; } m_pEventFirst = pEvent->next; pEvent = pEvent->next; continue; } pPrevEvent->next = pEvent->next; if (pEvent == m_pEventLast) { m_pEventLast = pPrevEvent; } pEvent = pEvent->next; } } IPhysicalEntity* CPhysicalWorld::SetHeightfieldData(const heightfield* phf, int* pMatMapping, int nMats) { //It is a good idea to stop background threads before removing or adding the terrain. //The background threads will be recreated and restarted during game tick (CPhysicalWorld::TimeStep(...)) for (int i = m_nWorkerThreads - 1; i >= 0; i--) { m_threads[i]->bStop = 1, m_threadStart[i].Set(), m_threadDone[i].Wait(); GetISystem()->GetIThreadTaskManager()->UnregisterTask(m_threads[i]); delete m_threads[i]; } int iCaller; CPhysicalEntity* pHF; if (!phf) { for (iCaller = 0; iCaller <= MAX_PHYS_THREADS; iCaller++) { if (pHF = m_pHeightfield[iCaller]) { m_pHeightfield[iCaller] = 0; assert(pHF->m_parts[0].pPhysGeom); // analyzer:( pHF->m_parts[0].pPhysGeom->pGeom->Release(); delete pHF->m_parts[0].pPhysGeom; pHF->m_parts[0].pPhysGeom = 0; if (pHF->m_parts[0].pMatMapping) { delete[] pHF->m_parts[0].pMatMapping; } //If there are other physical entities in the world, most likely they hold reference to //the terrain as a collider or some other physics event. Before Deleting the terrain physical entities it is important //to make sure that its reference count is set to 0. RemoveFromAllColliders(pHF); RemoveFromAllEvents(pHF); pHF->Delete(); } } return 0; } for (iCaller = 0; iCaller <= MAX_PHYS_THREADS; iCaller++) { CGeometry* pGeom = (CGeometry*)CreatePrimitive(heightfield::type, phf); pHF = m_pHeightfield[iCaller]; m_pHeightfield[iCaller] = 0; if (pHF) { pHF->m_parts[0].pPhysGeom->pGeom->Release(); if (pHF->m_parts[0].pMatMapping) { delete[] pHF->m_parts[0].pMatMapping; } } else { pHF = CPhysicalEntity::Create<CPhysicalEntity>(this, NULL); pHF->m_parts = AllocEntityPart(); pHF->m_nPartsAlloc = 1; pHF->m_parts[0].pPhysGeom = pHF->m_parts[0].pPhysGeomProxy = new phys_geometry; memset(pHF->m_parts[0].pPhysGeom, 0, sizeof(phys_geometry)); pHF->m_parts[0].id = 0; pHF->m_parts[0].scale = 1.0; pHF->m_parts[0].mass = 0; pHF->m_parts[0].flags = geom_collides; pHF->m_parts[0].flagsCollider = geom_colltype0; pHF->m_parts[0].minContactDist = phf->step.x; pHF->m_parts[0].idmatBreakable = -1; pHF->m_parts[0].pLattice = 0; pHF->m_parts[0].nMats = 0; pHF->m_nParts = 1; pHF->m_id = -1; pHF->m_collisionClass.type |= collision_class_terrain; } if (pMatMapping) { memcpy(pHF->m_parts[0].pMatMapping = new int[nMats], pMatMapping, (pHF->m_parts[0].nMats = nMats) * sizeof(int)); } else { pHF->m_parts[0].pMatMapping = 0; } m_HeightfieldBasis = phf->Basis; m_HeightfieldOrigin = phf->origin; pHF->m_parts[0].pPhysGeom->pGeom = pGeom; pHF->m_parts[0].pos = phf->origin; pHF->m_parts[0].q = !quaternionf(phf->Basis); pHF->m_parts[0].BBox[0].zero(); pHF->m_parts[0].BBox[1].zero(); m_pHeightfield[iCaller] = pHF; } return m_pHeightfield[0]; } IPhysicalEntity* CPhysicalWorld::GetHeightfieldData(heightfield* phf) { if (m_pHeightfield[0]) { m_pHeightfield[0]->m_parts[0].pPhysGeom->pGeom->GetPrimitive(0, phf); phf->Basis = m_HeightfieldBasis; phf->origin = m_HeightfieldOrigin; } return m_pHeightfield[0]; } void CPhysicalWorld::SetHeightfieldMatMapping(int* pMatMapping, int nMats) { if (!pMatMapping) { return; } for (int iCaller = 0; iCaller <= MAX_PHYS_THREADS; iCaller++) { CPhysicalEntity* pHF = m_pHeightfield[iCaller]; if (pHF && pHF->m_parts && pHF->m_parts[0].pMatMapping) { memcpy(pHF->m_parts[0].pMatMapping, pMatMapping, nMats * sizeof(int)); pHF->m_parts[0].nMats = nMats; } } } void CPhysicalWorld::SetupEntityGrid(int axisz, Vec3 org, int nx, int ny, float stepx, float stepy, int log2PODscale, int bCyclic) { WriteLock lockGrid(m_lockGrid); if (m_pEntGrid) { int i; if (m_pEntsById) { for (i = 0; i < m_iNextId; i++) { if (m_pEntsById[i]) { DetachEntityGridThunks(m_pEntsById[i]); if (!m_pEntsById[i]->m_pEntBuddy || m_pEntsById[i]->m_pEntBuddy == m_pEntsById[i]) { CPhysicalEntity* pent = (CPhysicalEntity*)m_pEntsById[i]; for (int j = 0; j < pent->m_nParts; j++) { if (pent->m_parts[j].pPlaceholder) { DetachEntityGridThunks(pent->m_parts[j].pPlaceholder); } } } } } } for (CPhysArea* pArea = m_pGlobalArea; pArea; pArea = pArea->m_next) { DetachEntityGridThunks(pArea); } for (i = m_entgrid.size.x * m_entgrid.size.y; i >= 0; i--) { if (m_pEntGrid[i]) { m_gthunks[m_pEntGrid[i]].iprev = 0; } } DeallocateGrid(m_pEntGrid, m_entgrid.size); } InitGThunksPool(); //Before changing m_entgrid.size, delete the on demand grid so //we don't treat the old grid as valid and write/delete out of bounds. DeactivateOnDemandGrid(); nx = min(1024, nx); ny = min(1024, ny); #if !defined(GRID_AXIS_STANDARD) m_iEntAxisx = inc_mod3[axisz]; m_iEntAxisy = dec_mod3[axisz]; m_iEntAxisz = axisz; #endif m_entgrid.size.set(nx, ny); m_entgrid.stride.set(1, nx); m_entgrid.step.set(stepx, stepy); m_entgrid.stepr.set(1.0f / stepx, 1.0f / stepy); m_entgrid.origin = org; m_entgrid.Basis.SetIdentity(); AllocateGrid(m_pEntGrid, m_entgrid.size); m_log2PODscale = log2PODscale; m_PODstride.set(1, ny >> 3 + m_log2PODscale); if (m_entgrid.bCyclic = bCyclic) { m_vars.iOutOfBounds = raycast_out_of_bounds | get_entities_out_of_bounds; } } void CPhysicalWorld::DeactivateOnDemandGrid() { if (m_bHasPODGrid) { int i; m_pPODCells--; for (i = m_entgrid.size.x * m_entgrid.size.y >> (3 + m_log2PODscale) * 2; i >= 0; i--) { if (m_pPODCells[i]) { delete[] m_pPODCells[i]; } } delete[] m_pPODCells; m_pPODCells = &m_pDummyPODcell; m_dummyPODcell.lifeTime = 1E10f; m_iActivePODCell0 = -1; m_bHasPODGrid = 0; for (i = 0; i < 8; i++) { for (CPhysicalEntity* pent = m_pTypedEnts[i]; pent; pent = pent->m_next) { pent->m_nRefCount -= pent->m_nRefCountPOD; pent->m_nRefCountPOD = 0; } } } } void CPhysicalWorld::RegisterBBoxInPODGrid(const Vec3* BBox) { int i, ix, iy, igx[2], igy[2], imask; pe_PODcell* pPODcell; WriteLock lockPOD(m_lockPODGrid); if (!m_bHasPODGrid) { if ((m_entgrid.size.x | m_entgrid.size.y) & 7) { return; } i = (m_entgrid.size.x * m_entgrid.size.y >> (3 + m_log2PODscale) * 2) + 1; memset(m_pPODCells = new pe_PODcell*[i], 0, i * sizeof(m_pPODCells[0])); m_pPODCells++; m_iActivePODCell0 = -1; m_bHasPODGrid = 1; } for (i = 0; i < 2; i++) { igx[i] = max(-1, min(m_entgrid.size.x, float2int((BBox[i][m_iEntAxisx] - m_entgrid.origin[m_iEntAxisx]) * m_entgrid.stepr.x - 0.5f))); igy[i] = max(-1, min(m_entgrid.size.y, float2int((BBox[i][m_iEntAxisy] - m_entgrid.origin[m_iEntAxisy]) * m_entgrid.stepr.y - 0.5f))); igx[i] >>= m_log2PODscale; igy[i] >>= m_log2PODscale; } for (ix = igx[0]; ix <= igx[1]; ix++) { for (iy = igy[0]; iy <= igy[1]; iy++) { imask = ~negmask(ix) & ~negmask(iy) & negmask(ix - (m_entgrid.size.x >> m_log2PODscale)) & negmask(iy - (m_entgrid.size.y >> m_log2PODscale)); // (x>=0 && x<m_entgrid.size.x) ? 0xffffffff : 0; i = (ix >> 3) * m_PODstride.x + (iy >> 3) * m_PODstride.y; i = i + ((-1 - i) & ~imask); if (!m_pPODCells[i]) { memset(m_pPODCells[i] = new pe_PODcell[64], 0, sizeof(pe_PODcell) * 64); for (int j = 0; j < 64; j++) { m_pPODCells[i][j].zlim.set(1000.0f, -1000.0f); } } pPODcell = m_pPODCells[i] + ((ix & 7) + (iy & 7) * 8 & imask); pPODcell->zlim[0] = min(pPODcell->zlim[0], BBox[0][m_iEntAxisz]); pPODcell->zlim[1] = max(pPODcell->zlim[1], BBox[1][m_iEntAxisz]); if (pPODcell->lifeTime > 0) { MarkAsPODThread(this); Vec3 center, sz; GetPODGridCellBBox(ix << m_log2PODscale, iy << m_log2PODscale, center, sz); m_pPhysicsStreamer->DestroyPhysicalEntitiesInBox(center - sz, center + sz); pPODcell->lifeTime = -1; int* picellNext; pe_PODcell* pPODcell1; for (i = m_iActivePODCell0, picellNext = &m_iActivePODCell0; i >= 0; i = pPODcell1->inextActive) { if (pPODcell == (pPODcell1 = getPODcell(i & 0xFFFF, i >> 16))) { *picellNext = pPODcell->inextActive; break; } else { picellNext = &pPODcell1->inextActive; } } UnmarkAsPODThread(this); } } } } void CPhysicalWorld::UnregisterBBoxInPODGrid(const Vec3* BBox) { if (!m_bHasPODGrid) { return; } int i, ix, iy, igx[2], igy[2], imask; pe_PODcell* pPODcell; for (i = 0; i < 2; i++) { igx[i] = max(-1, min(m_entgrid.size.x, float2int((BBox[i][m_iEntAxisx] - m_entgrid.origin[m_iEntAxisx]) * m_entgrid.stepr.x - 0.5f))); igy[i] = max(-1, min(m_entgrid.size.y, float2int((BBox[i][m_iEntAxisy] - m_entgrid.origin[m_iEntAxisy]) * m_entgrid.stepr.y - 0.5f))); igx[i] >>= m_log2PODscale; igy[i] >>= m_log2PODscale; } for (ix = igx[0]; ix <= igx[1]; ix++) { for (iy = igy[0]; iy <= igy[1]; iy++) { imask = ~negmask(ix) & ~negmask(iy) & negmask(ix - (m_entgrid.size.x >> m_log2PODscale)) & negmask(iy - (m_entgrid.size.y >> m_log2PODscale)); // (x>=0 && x<m_entgrid.size.x) ? 0xffffffff : 0; i = (ix >> 3) * m_PODstride.x + (iy >> 3) * m_PODstride.y; i = i + ((-1 - i) & ~imask); if (!m_pPODCells[i]) { continue; } pPODcell = m_pPODCells[i] + ((ix & 7) + (iy & 7) * 8 & imask); pPODcell->zlim.set(1000.0f, -1000.0f); } } } int CPhysicalWorld::AddRefEntInPODGrid(IPhysicalEntity* _pent, const Vec3* BBox) { CPhysicalEntity* pent = (CPhysicalEntity*)_pent; WriteLock lockPOD(m_lockPODGrid); int i, ix, iy, nCells = 0; Vec2i ig[2]; const Vec3* pBBox = BBox ? BBox : pent->m_BBox; for (i = 0; i < 2; i++) { ig[i].x = max(-1, min(m_entgrid.size.x, float2int((pBBox[i][m_iEntAxisx] - m_entgrid.origin[m_iEntAxisx]) * m_entgrid.stepr.x - 0.5f))); ig[i].y = max(-1, min(m_entgrid.size.y, float2int((pBBox[i][m_iEntAxisy] - m_entgrid.origin[m_iEntAxisy]) * m_entgrid.stepr.y - 0.5f))); ig[i].x >>= m_log2PODscale; ig[i].y >>= m_log2PODscale; } for (ix = ig[0].x; ix <= ig[1].x; ix++) { for (iy = ig[0].y; iy <= ig[1].y; iy++) { if (getPODcell(ix << m_log2PODscale, iy << m_log2PODscale)->lifeTime > 0) { ++pent->m_nRefCount, ++pent->m_nRefCountPOD, ++nCells; } } } return nCells; } void CPhysicalWorld::GetPODGridCellBBox(int ix, int iy, Vec3& center, Vec3& size) { Vec2 step = m_entgrid.step * (1 << m_log2PODscale); pe_PODcell* pPODcell = getPODcell(ix, iy); center = m_entgrid.origin; center[m_iEntAxisx] += ((ix >> m_log2PODscale) + 0.5f) * step.x; center[m_iEntAxisy] += ((iy >> m_log2PODscale) + 0.5f) * step.y; center[m_iEntAxisz] = (pPODcell->zlim[1] + pPODcell->zlim[0]) * 0.5f; size[m_iEntAxisx] = step.x * 0.5f; size[m_iEntAxisy] = step.y * 0.5f; size[m_iEntAxisz] = (pPODcell->zlim[1] - pPODcell->zlim[0]) * 0.5f; } int CPhysicalWorld::SetSurfaceParameters(int surface_idx, float bounciness, float friction, unsigned int flags) { if ((unsigned int)surface_idx >= (unsigned int)NSURFACETYPES) { return 0; } m_BouncinessTable[surface_idx] = bounciness; m_FrictionTable[surface_idx] = friction; m_DynFrictionTable[surface_idx] = friction * (1.0 / 1.5); m_SurfaceFlagsTable[surface_idx] = flags; m_DamageReductionTable[surface_idx] = 0; m_RicochetAngleTable[surface_idx] = 0; m_RicDamReductionTable[surface_idx] = 0; m_RicVelReductionTable[surface_idx] = 0; return 1; } int CPhysicalWorld::GetSurfaceParameters(int surface_idx, float& bounciness, float& friction, unsigned int& flags) { if ((unsigned int)surface_idx >= (unsigned int)NSURFACETYPES) { return 0; } bounciness = m_BouncinessTable[surface_idx]; friction = m_FrictionTable[surface_idx]; flags = m_SurfaceFlagsTable[surface_idx]; return 1; } int CPhysicalWorld::SetSurfaceParameters(int surface_idx, float bounciness, float friction, float damage_reduction, float ric_angle, float ric_dam_reduction, float ric_vel_reduction, unsigned int flags) { if ((unsigned int)surface_idx >= (unsigned int)NSURFACETYPES) { return 0; } SetSurfaceParameters(surface_idx, bounciness, friction, flags); m_DamageReductionTable[surface_idx] = damage_reduction; m_RicochetAngleTable[surface_idx] = ric_angle; m_RicDamReductionTable[surface_idx] = ric_dam_reduction; m_RicVelReductionTable[surface_idx] = ric_vel_reduction; return 1; } int CPhysicalWorld::GetSurfaceParameters(int surface_idx, float& bounciness, float& friction, float& damage_reduction, float& ric_angle, float& ric_dam_reduction, float& ric_vel_reduction, unsigned int& flags) { if ((unsigned int)surface_idx >= (unsigned int)NSURFACETYPES) { return 0; } GetSurfaceParameters(surface_idx, bounciness, friction, flags); damage_reduction = m_DamageReductionTable[surface_idx]; ric_angle = m_RicochetAngleTable[surface_idx]; ric_dam_reduction = m_RicDamReductionTable[surface_idx]; ric_vel_reduction = m_RicVelReductionTable[surface_idx]; return 1; } ///////////////////////////////////////////////////////////////////////////////////////////////////// IPhysicalEntity* CPhysicalWorld::CreatePhysicalEntity(pe_type type, float lifeTime, pe_params* params, PhysicsForeignData pForeignData, int iForeignData, int id, IPhysicalEntity* pHostPlaceholder, IGeneralMemoryHeap* pHeap) { FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); CPhysicalEntity* res = 0; CPhysicalPlaceholder* pEntityHost = (CPhysicalPlaceholder*)pHostPlaceholder; //#ifdef _DEBUG // { WriteLockCond lock(m_lockStep, pForeignData!=0 || iForeignData!=0x5AFE); // since in debug memory manager is not thread-safe (but dll-specific) //#endif switch (type) { case PE_STATIC: res = CPhysicalEntity::Create<CPhysicalEntity>(this, pHeap); break; case PE_RIGID: res = CPhysicalEntity::Create<CRigidEntity>(this, pHeap); break; case PE_LIVING: res = CPhysicalEntity::Create<CLivingEntity>(this, pHeap); break; case PE_WHEELEDVEHICLE: res = CPhysicalEntity::Create<CWheeledVehicleEntity>(this, pHeap); break; case PE_PARTICLE: res = CPhysicalEntity::Create<CParticleEntity>(this, pHeap); break; case PE_ARTICULATED: res = CPhysicalEntity::Create<CArticulatedEntity>(this, pHeap); break; case PE_ROPE: res = CPhysicalEntity::Create<CRopeEntity>(this, pHeap); break; case PE_SOFT: res = CPhysicalEntity::Create<CSoftEntity>(this, pHeap); break; } m_nTypeEnts[type]++; if (!res) { return 0; } //#ifdef _DEBUG // } //#endif if (type != PE_STATIC) { m_nDynamicEntitiesDeleted = 0; } if (pEntityHost && lifeTime > 0) { res->m_pForeignData = pEntityHost->m_pForeignData; res->m_iForeignData = pEntityHost->m_iForeignData; res->m_iForeignFlags = pEntityHost->m_iForeignFlags; res->m_id = pEntityHost->m_id; res->m_pEntBuddy = pEntityHost; pEntityHost->m_pEntBuddy = res; res->m_maxTimeIdle = lifeTime; res->m_bPrevPermanent = res->m_bPermanent = 0; res->m_iGThunk0 = pEntityHost->m_iGThunk0; res->m_ig[0].x = pEntityHost->m_ig[0].x; res->m_ig[1].x = pEntityHost->m_ig[1].x; res->m_ig[0].y = pEntityHost->m_ig[0].y; res->m_ig[1].y = pEntityHost->m_ig[1].y; } else { res->m_bPrevPermanent = res->m_bPermanent = 1; res->m_pForeignData = pForeignData; res->m_iForeignData = iForeignData; m_lastExtId = max(m_lastExtId, id); m_nExtIds += 1 + (id >> 31); SetPhysicalEntityId(res, id >= 0 ? id : GetFreeEntId()); } res->m_flags |= 0x80000000u; if (params) { res->SetParams(params, iForeignData == 0x5AFE || get_iCaller() < MAX_PHYS_THREADS); } if (!m_lockStep && lifeTime == 0) { WriteLockCond lock1(m_lockCaller[MAX_PHYS_THREADS], !IsPODThread(this) && m_nEnts + 1 > m_nEntsAlloc - 1); WriteLock lock(m_lockStep); res->m_flags &= ~0x80000000u; RepositionEntity(res, 2); if (++m_nEnts > m_nEntsAlloc - 1) { int nEntsAllocNew = m_nEntsAlloc + 4096; m_nEntListAllocs++; m_bEntityCountReserved = 0; ReallocateList(m_pTmpEntList, m_nEnts - 1, nEntsAllocNew); ReallocateList(m_pTmpEntList1, m_nEnts - 1, nEntsAllocNew); if (m_threadData[MAX_PHYS_THREADS].szList < nEntsAllocNew) { ReallocateList(m_pTmpEntList2, m_threadData[MAX_PHYS_THREADS].szList, nEntsAllocNew); } ReallocateList(m_pGroupMass, m_nEnts - 1, nEntsAllocNew); ReallocateList(m_pMassList, m_nEnts - 1, nEntsAllocNew); ReallocateList(m_pGroupIds, m_nEnts - 1, nEntsAllocNew); ReallocateList(m_pGroupNums, m_nEnts - 1, nEntsAllocNew); m_nEntsAlloc = nEntsAllocNew; } } else if (!m_lockQueue || get_iCaller() >= MAX_PHYS_THREADS && iForeignData != 0x5AFE) { WriteLock lock(m_lockQueue); AllocRequestsQueue(sizeof(int) * 3 + sizeof(void*)); QueueData(4); // RepositionEntity opcode QueueData((int)(sizeof(int) * 3 + sizeof(void*))); // size QueueData(res); QueueData(2); // flags } else { res->m_timeIdle = -10.0f; m_nOnDemandListFailures++; } return res; } IPhysicalEntity* CPhysicalWorld::CreatePhysicalPlaceholder(pe_type type, pe_params* params, PhysicsForeignData pForeignData, int iForeignData, int id) { int i, j, iChunk; if (m_nPlaceholders * 10 < m_iLastPlaceholder * 7) { for (i = m_iLastPlaceholder >> 5; i >= 0 && m_pPlaceholderMap[i] == -1; i--) { ; } if (i >= 0) { for (j = 0; j < 32 && m_pPlaceholderMap[i] & 1 << j; j++) { ; } i = i << 5 | j; } i = i - (i >> 31) | m_iLastPlaceholder + 1 & i >> 31; } else { i = m_iLastPlaceholder + 1; } iChunk = i >> PLACEHOLDER_CHUNK_SZLG2; if (iChunk == m_nPlaceholderChunks) { m_nPlaceholderChunks++; ReallocateList(m_pPlaceholders, m_nPlaceholderChunks - 1, m_nPlaceholderChunks, true); ReallocateList(m_pPlaceholderMap, (m_iLastPlaceholder >> 5) + 1, m_nPlaceholderChunks << PLACEHOLDER_CHUNK_SZLG2 - 5, true); } if (!m_pPlaceholders[iChunk]) { m_pPlaceholders[iChunk] = new CPhysicalPlaceholder[PLACEHOLDER_CHUNK_SZ]; } CPhysicalPlaceholder* res = m_pPlaceholders[iChunk] + (i & PLACEHOLDER_CHUNK_SZ - 1); res->m_pForeignData = pForeignData; res->m_iForeignData = iForeignData; res->m_iForeignFlags = 0; res->m_iGThunk0 = 0; res->m_ig[0].x = res->m_ig[0].y = res->m_ig[1].x = res->m_ig[1].y = GRID_REG_PENDING; res->m_pEntBuddy = 0; res->m_id = -1; res->m_bProcessed = 0; switch (type) { case PE_STATIC: res->m_iSimClass = 0; break; case PE_RIGID: case PE_WHEELEDVEHICLE: res->m_iSimClass = 1; break; case PE_LIVING: res->m_iSimClass = 3; break; case PE_PARTICLE: case PE_ROPE: case PE_ARTICULATED: case PE_SOFT: res->m_iSimClass = 4; } m_pPlaceholderMap[i >> 5] |= 1 << (i & 31); if (id > -2) { SetPhysicalEntityId(res, id >= 0 ? id : GetFreeEntId()); } else { res->m_id = id; } if (params) { res->SetParams(params); } m_nPlaceholders++; m_iLastPlaceholder = max(m_iLastPlaceholder, i); return res; } int CPhysicalWorld::DestroyPhysicalEntity(IPhysicalEntity* _pent, int mode, int bThreadSafe) { FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); int idx; CPhysicalPlaceholder* ppc = (CPhysicalPlaceholder*)_pent; if (ppc->m_pEntBuddy && IsPlaceholder(ppc->m_pEntBuddy) && mode != 0 || m_nDynamicEntitiesDeleted && ppc->m_iSimClass > 0) { return 0; } if (!(idx = IsPlaceholder(ppc))) { if (ppc->m_iSimClass != 5) { if (mode & 4 && ((CPhysicalEntity*)ppc)->Release() > 0) { return 0; } if (!bThreadSafe && m_vars.lastTimeStep > 0.0f) { EventPhysEntityDeleted eped; eped.pEntity = ppc; eped.mode = mode; eped.pForeignData = ppc->m_pForeignData; eped.iForeignData = ppc->m_iForeignData; if (!SignalEvent(&eped, 0)) { return 0; } } ((CPhysicalEntity*)ppc)->m_iDeletionTime = mode + 1; if (mode == 0) { ((CPhysicalEntity*)ppc)->m_pForeignData = 0; ((CPhysicalEntity*)ppc)->m_iForeignData = -1; } } else { ((CPhysArea*)ppc)->m_bDeleted = 1; } } mode &= 3; //if (m_lockStep & (bThreadSafe^1)) if (m_vars.bMultithreaded & (bThreadSafe ^ 1) && (m_lockStep || m_lockTPR || m_vars.lastTimeStep > 0 || mode == 1 && ppc->m_bProcessed >= PENT_QUEUED)) { WriteLock lock(m_lockQueue); AtomicAdd(&ppc->m_bProcessed, PENT_QUEUED); AllocRequestsQueue(sizeof(int) * 3 + sizeof(void*)); QueueData(5); // DestroyPhysicalEntity opcode QueueData((int)(sizeof(int) * 3 + sizeof(void*))); // size QueueData(_pent); QueueData(mode); return 1; } WriteLockCond lock(m_lockStep, m_vars.bMultithreaded && !bThreadSafe && !IsPODThread(this)); if (ppc->m_iSimClass == 5) { if (mode == 0) { RemoveArea(_pent); } return 1; } if (idx) { if (mode != 0) { return 0; } if (ppc->m_pEntBuddy && ppc->m_pEntBuddy->m_pEntBuddy == ppc) { DestroyPhysicalEntity(ppc->m_pEntBuddy, mode, 1); } SetPhysicalEntityId(ppc, -1); { WriteLock lockGrid(m_lockGrid); DetachEntityGridThunks(ppc); } --idx; m_pPlaceholderMap[idx >> 5] &= ~(1 << (idx & 31)); m_nPlaceholders--; int i, j, iChunk = idx >> PLACEHOLDER_CHUNK_SZLG2; // if entire iChunk is empty, deallocate it for (i = j = 0; i < (PLACEHOLDER_CHUNK_SZ >> 5); i++) { j |= m_pPlaceholderMap[(iChunk << PLACEHOLDER_CHUNK_SZLG2 - 5) + i]; } if (!j) { delete[] m_pPlaceholders[iChunk]; m_pPlaceholders[iChunk] = 0; } j = m_nPlaceholderChunks; // make sure that m_iLastPlaceholder points to the last used placeholder slot for (; m_iLastPlaceholder >= 0 && !(m_pPlaceholderMap[m_iLastPlaceholder >> 5] & 1 << (m_iLastPlaceholder & 31)); m_iLastPlaceholder--) { if ((m_iLastPlaceholder ^ m_iLastPlaceholder - 1) + 1 >> 1 == PLACEHOLDER_CHUNK_SZ) { // if m_iLastPlaceholder points to the 1st chunk element, entire chunk is free and can be deallocated iChunk = m_iLastPlaceholder >> PLACEHOLDER_CHUNK_SZLG2; if (m_pPlaceholders[iChunk]) { delete[] m_pPlaceholders[iChunk]; m_pPlaceholders[iChunk] = 0; } m_nPlaceholderChunks = iChunk; } } if (m_nPlaceholderChunks < j) { ReallocateList(m_pPlaceholderMap, j << PLACEHOLDER_CHUNK_SZLG2 - 5, m_nPlaceholderChunks << PLACEHOLDER_CHUNK_SZLG2 - 5, true); } return 1; } CPhysicalEntity* pent = (CPhysicalEntity*)_pent; if (pent->m_iSimClass == 7) { return 0; } pent->m_iDeletionTime = max(4, m_iLastLogPump + 2); for (idx = m_nProfiledEnts - 1; idx >= 0; idx--) { if (m_pEntProfileData[idx].pEntity == pent) { memmove(m_pEntProfileData + idx, m_pEntProfileData + idx + 1, (--m_nProfiledEnts - idx) * sizeof(m_pEntProfileData[0])); } } for (idx = 0; idx <= MAX_PHYS_THREADS; idx++) { m_prevGEAobjtypes[idx] = -1; } if (pent->m_iSimClass < 0) { if (pent->m_next) { pent->m_next->m_prev = pent->m_prev; } if (pent->m_prev) { pent->m_prev->m_next = pent->m_next; } if (pent == m_pHiddenEnts) { m_pHiddenEnts = pent->m_next; } pent->m_next = pent->m_prev = 0; } if (mode == 2) { if (pent->m_iSimClass == -1 && pent->m_iPrevSimClass >= 0) { pent->m_ig[0].x = pent->m_ig[1].x = pent->m_ig[0].y = pent->m_ig[1].y = GRID_REG_PENDING; pent->m_iSimClass = pent->m_iPrevSimClass & 0x0F; pent->m_iPrevSimClass = -1; AtomicAdd(&m_lockGrid, -RepositionEntity(pent)); if (pent->m_pStructure && pent->m_pStructure->defparts) { for (int i = 0; i < pent->m_nParts; i++) { if (pent->m_pStructure->defparts[i].pSkinInfo && pent->m_pStructure->defparts[i].pSkelEnt) { DestroyPhysicalEntity(pent->m_pStructure->defparts[i].pSkelEnt, 2, 1); } } } } pent->m_iDeletionTime = 0; return 1; } if (m_pEntBeingDeleted == pent) { return 1; } m_pEntBeingDeleted = pent; if (mode == 0 && !pent->m_bPermanent && m_pPhysicsStreamer) { m_pPhysicsStreamer->DestroyPhysicalEntity(pent); } m_pEntBeingDeleted = 0; pent->AlertNeighbourhoodND(mode); if ((unsigned int)pent->m_iPrevSimClass < 8u && pent->m_iSimClass >= 0) { WriteLock lockl(m_lockList); if (pent->m_next) { pent->m_next->m_prev = pent->m_prev; } (pent->m_prev ? pent->m_prev->m_next : m_pTypedEnts[pent->m_iPrevSimClass]) = pent->m_next; if (pent == m_pTypedEntsPerm[pent->m_iPrevSimClass]) { m_pTypedEntsPerm[pent->m_iPrevSimClass] = pent->m_next; } } pent->m_next = pent->m_prev = 0; /*#ifdef _DEBUG CPhysicalEntity *ptmp = m_pTypedEnts[1]; for(;ptmp && ptmp!=m_pTypedEntsPerm[1]; ptmp=ptmp->m_next); if (ptmp!=m_pTypedEntsPerm[1]) DEBUG_BREAK; #endif*/ if (!pent->m_pEntBuddy) { { WriteLock lockGrid(m_lockGrid); DetachEntityGridThunks(pent); } for (int j = 0; j < pent->m_nParts; j++) { if (pent->m_parts[j].pPlaceholder) { DestroyPhysicalEntity(pent->ReleasePartPlaceholder(j), 0, 1); } } if (pent->m_flags & pef_parts_traceable) { (pent->m_flags &= ~pef_parts_traceable) |= pef_traceable; } pent->m_nUsedParts = 0; } pent->m_iGThunk0 = 0; if (mode == 0) { int bWasRegistered = !(pent->m_flags & 0x80000000u); pent->m_iPrevSimClass = -1; pent->m_iSimClass = 7; pent->m_pForeignData = 0; pent->m_iForeignData = -1; if (pent->m_next = m_pTypedEnts[7]) { pent->m_next->m_prev = pent; } if (pent->m_pEntBuddy) { pent->m_pEntBuddy->m_pEntBuddy = 0; } else { if (pent->m_id <= m_lastExtId) { --m_nExtIds; } SetPhysicalEntityId(pent, -1); } m_pTypedEnts[7] = pent; if (bWasRegistered) { m_nTypeEnts[pent->GetType()]--; } if (bWasRegistered && --m_nEnts < m_nEntsAlloc - 8192 && !m_bEntityCountReserved) { m_nEntsAlloc -= 8192; m_nEntListAllocs++; ReallocateList(m_pTmpEntList, m_nEntsAlloc + 8192, m_nEntsAlloc); ReallocateList(m_pTmpEntList1, m_nEntsAlloc + 8192, m_nEntsAlloc); ReallocateList(m_pTmpEntList2, m_nEntsAlloc + 8192, m_nEntsAlloc); ReallocateList(m_pGroupMass, 0, m_nEntsAlloc); ReallocateList(m_pMassList, 0, m_nEntsAlloc); ReallocateList(m_pGroupIds, 0, m_nEntsAlloc); ReallocateList(m_pGroupNums, 0, m_nEntsAlloc); m_threadData[0].szList = m_threadData[MAX_PHYS_THREADS].szList = m_nEntsAlloc; } } else if (pent->m_iSimClass >= 0) { pe_action_reset reset; pent->Action(&reset); pent->m_iPrevSimClass = pent->m_iSimClass | 0x100; pent->m_iSimClass = -1; } if (mode == 1) { if (m_pHiddenEnts) { m_pHiddenEnts->m_prev = pent; } pent->m_next = m_pHiddenEnts; m_pHiddenEnts = pent; pent->m_prev = 0; } return 1; } int CPhysicalWorld::ReserveEntityCount(int nNewEnts) { if (m_nEnts + nNewEnts > m_nEntsAlloc - 1) { m_nEntsAlloc = (m_nEnts + nNewEnts & ~4095) + 4096; m_nEntListAllocs++; m_bEntityCountReserved = 1; ReallocateList(m_pTmpEntList, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pTmpEntList1, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pTmpEntList2, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pGroupMass, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pMassList, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pGroupIds, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pGroupNums, m_nEnts - 1, m_nEntsAlloc); } return m_nEntsAlloc; } void CPhysicalWorld::CleanseEventsQueue() { WriteLock lock(m_lockEventsQueue); EventPhys* pEvent, ** ppPrevNext; for (pEvent = m_pEventFirst, ppPrevNext = &m_pEventFirst, m_pEventLast = 0; pEvent; pEvent = *ppPrevNext) { if (pEvent->idval <= EventPhysCollision::id && (((CPhysicalEntity*)((EventPhysStereo*)pEvent)->pEntity[0])->m_iDeletionTime || ((CPhysicalEntity*)((EventPhysStereo*)pEvent)->pEntity[1])->m_iDeletionTime) || pEvent->idval > EventPhysCollision::id && ((CPhysicalEntity*)((EventPhysMono*)pEvent)->pEntity)->m_iDeletionTime) { if (pEvent->idval == EventPhysCreateEntityPart::id) { DestroyPhysicalEntity(((EventPhysCreateEntityPart*)pEvent)->pEntNew, 0, 1); } *ppPrevNext = pEvent->next; pEvent->next = m_pFreeEvents[pEvent->idval]; m_pFreeEvents[pEvent->idval] = pEvent; } else { ppPrevNext = &pEvent->next; m_pEventLast = pEvent; } } } void CPhysicalWorld::PatchEventsQueue(IPhysicalEntity* pEntity, PhysicsForeignData pForeignData, int iForeignData) { WriteLock lock(m_lockEventsQueue); EventPhys* pEvent; EventPhysMono* pMono; EventPhysStereo* pStereo; int i; for (pEvent = m_pEventFirst; pEvent; pEvent = pEvent->next) { if (pEvent->idval >= 0 && pEvent->idval <= EventPhysCollision::id) { pStereo = (EventPhysStereo*)pEvent; for (i = 0; i < 2; ++i) { if (pStereo->pEntity[i] == pEntity) { pStereo->pForeignData[i] = pForeignData; pStereo->iForeignData[i] = iForeignData; } } } else if (pEvent->idval == EventPhysRWIResult::id || pEvent->idval == EventPhysPWIResult::id) { // Do nothing, the foreign data should not be changed } else if (pEvent->idval < EVENT_TYPES_NUM) { pMono = (EventPhysMono*)pEvent; if (pMono->pEntity == pEntity) { pMono->pForeignData = pForeignData; pMono->iForeignData = iForeignData; } } else { # if !defined(_RELEASE) // intentionally crashing here - please update the code if new events have been created DEBUG_BREAK; # endif } } } int CPhysicalWorld::GetFreeEntId() { int nPhysEnts = m_nEnts - m_nExtIds, nPhysSlots = m_iNextId - m_lastExtId; if (nPhysEnts * 2 > nPhysSlots) { return m_iNextId++; } int nTries; for (nTries = 100; nTries > 0 && m_iNextIdDown > m_lastExtId && m_pEntsById[m_iNextIdDown]; m_iNextIdDown--, nTries--) { ; } if (nTries <= 0) { return m_iNextId++; } if (m_iNextIdDown <= m_lastExtId) { for (m_iNextIdDown = m_iNextId - 2, nTries = 100; nTries > 0 && m_iNextIdDown > m_lastExtId && m_pEntsById[m_iNextIdDown]; m_iNextIdDown--, nTries--) { ; } } if (nTries <= 0 || m_iNextIdDown <= m_lastExtId) { return m_iNextId++; } return m_iNextIdDown--; } int CPhysicalWorld::SetPhysicalEntityId(IPhysicalEntity* _pent, int id, int bReplace, int bThreadSafe) { WriteLockCond lock(m_lockEntIdList, bThreadSafe ^ 1); CPhysicalPlaceholder* pent = (CPhysicalPlaceholder*)_pent; unsigned int previd = (unsigned int)pent->m_id; if (previd < (unsigned int)m_nIdsAlloc) { m_pEntsById[previd] = 0; if (previd == m_iNextId - 1) { for (; m_iNextId > 0 && m_pEntsById[m_iNextId - 1] == 0; m_iNextId--) { ; } } if (previd == m_lastExtId) { for (--m_lastExtId; m_lastExtId > 0 && !m_pEntsById[m_lastExtId]; m_lastExtId--) { ; } } } m_iNextId = max(m_iNextId, id + 1); if (id >= 0) { if (id >= m_nIdsAlloc) { int nAllocPrev = m_nIdsAlloc; ReallocateList(m_pEntsById, nAllocPrev, m_nIdsAlloc = (id & ~32767) + 32768, true); } if (m_pEntsById[id]) { if (bReplace) { SetPhysicalEntityId(m_pEntsById[id], GetFreeEntId(), 1, 1); } else { return 0; } } if (IsPlaceholder(pent->m_pEntBuddy)) { pent = pent->m_pEntBuddy; } (m_pEntsById[id] = pent)->m_id = id; if (pent->m_pEntBuddy) { pent->m_pEntBuddy->m_id = id; } return 1; } return 0; } int CPhysicalWorld::GetPhysicalEntityId(IPhysicalEntity* pent) { return pent == WORLD_ENTITY ? -2 : (pent ? ((CPhysicalEntity*)pent)->m_id : -1); } IPhysicalEntity* CPhysicalWorld::GetPhysicalEntityById(int id) { ReadLock lock(m_lockEntIdList); if (id == -1) { return m_pHeightfield[0]; } else if (id == -2) { return &g_StaticPhysicalEntity; } else { int bNoExpand = id >> 30; id &= ~(1 << 30); if ((unsigned int)id < (unsigned int)m_nIdsAlloc) { return m_pEntsById[id] ? (!bNoExpand ? m_pEntsById[id]->GetEntity() : m_pEntsById[id]->GetEntityFast()) : 0; } } return 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////// static inline void swap(CPhysicalEntity** pentlist, float* pmass, int* pids, int i1, int i2) { CPhysicalEntity* pent = pentlist[i1]; pentlist[i1] = pentlist[i2]; pentlist[i2] = pent; float m = pmass[i1]; pmass[i1] = pmass[i2]; pmass[i2] = m; if (pids) { int id = pids[i1]; pids[i1] = pids[i2]; pids[i2] = id; } } static void qsort(CPhysicalEntity** pentlist, float* pmass, int* pids, int ileft, int iright) { if (ileft >= iright) { return; } int i, ilast; float diff = 0.0f; swap(pentlist, pmass, pids, ileft, ileft + iright >> 1); for (ilast = ileft, i = ileft + 1; i <= iright; i++) { diff += fabs_tpl(pmass[i] - pmass[ileft]); if (pmass[i] > pmass[ileft]) { swap(pentlist, pmass, pids, ++ilast, i); } } swap(pentlist, pmass, pids, ileft, ilast); if (diff > 0) { qsort(pentlist, pmass, pids, ileft, ilast - 1); qsort(pentlist, pmass, pids, ilast + 1, iright); } } void GroupByOuterEntity(CPhysicalEntity** pentlist, int left, int right) { int i = left, j = right, mi = left + right >> 1; INT_PTR m = (INT_PTR)pentlist[mi]->m_pOuterEntity; while (i <= j) { while ((INT_PTR)pentlist[i]->m_pOuterEntity < m) { i++; } while (m < (INT_PTR)pentlist[j]->m_pOuterEntity) { j--; } if (i <= j) { CPhysicalEntity* pent = pentlist[i]; pentlist[i] = pentlist[j]; pentlist[j] = pent; i++; j--; } } if (left < j) { GroupByOuterEntity(pentlist, left, j); } if (i < right) { GroupByOuterEntity(pentlist, i, right); } } int CPhysicalWorld::ReallocTmpEntList(CPhysicalEntity**& pEntList, int iCaller, int szNew) { assert(iCaller <= MAX_PHYS_THREADS); ReallocateList(m_threadData[iCaller].pTmpEntList, m_threadData[iCaller].szList, szNew); pEntList = m_threadData[iCaller].pTmpEntList; if (iCaller == 0) { m_pTmpEntList = m_threadData[iCaller].pTmpEntList; } else if (iCaller == MAX_PHYS_THREADS) { m_pTmpEntList2 = m_threadData[iCaller].pTmpEntList; } return m_threadData[iCaller].szList = szNew; } inline bool AABB_overlap2d(const Vec2& min0, const Vec2& max0, const Vec2& min1, const Vec2& max1) { return max(fabs_tpl(min0.x + max0.x - min1.x - max1.x) - (max0.x - min0.x) - (max1.x - min1.x), fabs_tpl(min0.y + max0.y - min1.y - max1.y) - (max0.y - min0.y) - (max1.y - min1.y)) < 0; } static ILINE int getcell_safe(grid& g, int ix, int iy, int iOutOfBounds) { return (iOutOfBounds & get_entities_out_of_bounds) == 0 ? (iy * g.stride.y + ix * g.stride.x) : g.getcell_safe(ix, iy); } static ILINE Vec3 GetPermutated(const Vec3& in, const int axisZ) { #if defined(GRID_AXIS_STANDARD) return in; #else return in.GetPermutated(axisZ); #endif } static ILINE Vec3i ToVec3i(const Vec3& in) { return Vec3i(static_cast<int>(in.x), static_cast<int>(in.y), static_cast<int>(in.z)); } int CPhysicalWorld::GetEntitiesAround(const Vec3& ptmin, const Vec3& ptmax, CPhysicalEntity**& pList, int objtypes, CPhysicalEntity* pPetitioner, int szListPrealloc, int iCaller) { IF (!m_pEntGrid || !m_pTmpEntList, 0) { return 0; } CPhysicalEntity** pTmpEntList; int nout = 0, itypePetitioner; int maskCaller = 1 << iCaller, maskCaller4 = sqr(sqr(maskCaller)); EventPhysBBoxOverlap event; int szList = GetTmpEntList(pTmpEntList, iCaller); IF ((szListPrealloc | (objtypes & ent_allocate_list)) == 0, 1) { pList = pTmpEntList; } if (pPetitioner) { itypePetitioner = 1 << pPetitioner->m_iSimClass & - iszero((int)pPetitioner->m_flags & pef_never_affect_triggers); event.pEntity[0] = pPetitioner; event.pForeignData[0] = pPetitioner->m_pForeignData; event.iForeignData[0] = pPetitioner->m_iForeignData; } else { itypePetitioner = 0; } const int itype = itypePetitioner; const int bAreasOnly = iszero(objtypes - ent_areas); #ifndef _RELEASE m_nGEA[iCaller]++; #endif if ((ptmin - m_prevGEABBox[iCaller][0]).len2() + (ptmax - m_prevGEABBox[iCaller][1]).len2() == 0.0f && sqr(objtypes - m_prevGEAobjtypes[iCaller]) + 1 + (iCaller - MAX_PHYS_THREADS >> 31) == 0) { pList = pTmpEntList; return m_nprevGEAEnts[iCaller]; } FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); #ifndef PHYS_FUNC_PROFILER_DISABLED INT_PTR mask = (INT_PTR)pPetitioner; mask = mask >> sizeof(mask) * 8 - 1 ^ (mask - 1) >> sizeof(mask) * 8 - 1; PHYS_FUNC_PROFILER((const char*)((INT_PTR)"GetEntitiesAround(Physics)" & ~mask | (INT_PTR)"GetEntitiesAround(External)" & mask)); #endif const Vec3 scale(m_entgrid.stepr.x, m_entgrid.stepr.y, m_rzGran); Vec3 gmin = GetPermutated(ptmin - m_entgrid.origin, m_iEntAxisz).CompMul(scale); Vec3 gmax = GetPermutated(ptmax - m_entgrid.origin, m_iEntAxisz).CompMul(scale); int igx[2] = { float2int(gmin.x - 0.5f), float2int(gmax.x - 0.5f) }; int igy[2] = { float2int(gmin.y - 0.5f), float2int(gmax.y - 0.5f) }; if (m_entgrid.iscyclic()) { } else IF ((m_vars.iOutOfBounds & get_entities_out_of_bounds) == 0, 1) { int mask1, mask2, mask3, mask4, mask5, mask6, mask7, mask8; igx[0] = apply_clamp(igx[0], 0, m_entgrid.size.x - 1, mask1, mask2); igx[1] = apply_clamp(igx[1], 0, m_entgrid.size.x - 1, mask3, mask4); igy[0] = apply_clamp(igy[0], 0, m_entgrid.size.y - 1, mask5, mask6); igy[1] = apply_clamp(igy[1], 0, m_entgrid.size.y - 1, mask7, mask8); if ((mask1 & mask3) | (mask5 & mask7) | (mask2 & mask4) | (mask6 & mask8)) { return 0; // both x<0 or both y<0 or both x>=maxx or both y>=maxy } } else { igx[0] = max(-1, min(m_entgrid.size.x, igx[0])); igx[1] = max(-1, min(m_entgrid.size.x, igx[1])); igy[0] = max(-1, min(m_entgrid.size.y, igy[0])); igy[1] = max(-1, min(m_entgrid.size.y, igy[1])); } const int igx0 = igx[0]; const int igx1 = igx[1]; const int igy0 = igy[0]; const int igy1 = igy[1]; IF ((igx1 - igx0 + 1) * (igy1 - igy0 + 1) > m_vars.nGEBMaxCells, 0) { if (m_pLog) { m_pLog->Log("GetEntitiesInBox: too many cells requested by %s (%d, (%.1f,%.1f,%.1f)-(%.1f,%.1f,%.1f))", pPetitioner && m_pRenderer ? m_pRenderer->GetForeignName(pPetitioner->m_pForeignData, pPetitioner->m_iForeignData, pPetitioner->m_iForeignFlags) : "Game", (igx1 - igx0 + 1) * (igy1 - igy0 + 1), ptmin.x, ptmin.y, ptmin.z, ptmax.x, ptmax.y, ptmax.z); } if (m_vars.bBreakOnValidation) { DoBreak; } } { ReadLock lock0(m_lockGrid); for (int ix = igx0; ix <= igx1; ix++) { for (int iy = igy0; iy <= igy1; iy++) { if ((objtypes & (ent_static | ent_no_ondemand_activation)) == ent_static) { pe_PODcell* pPODcell = getPODcell(ix, iy); const float zrange = pPODcell->zlim[1] - pPODcell->zlim[0]; if (fabs_tpl((ptmax[m_iEntAxisz] + ptmin[m_iEntAxisz] - pPODcell->zlim[1] - pPODcell->zlim[0]) * zrange) < (ptmax[m_iEntAxisz] - ptmin[m_iEntAxisz] + zrange) * zrange) { CryInterlockedAdd(&m_lockGrid, -1); ReadLock lockPOD(m_lockPODGrid); if (pPODcell->lifeTime <= 0) { CryInterlockedAdd(&m_lockPODGrid, -1); { WriteLock lockPODw(m_lockPODGrid); if (pPODcell->lifeTime <= 0) { MarkAsPODThread(this); Vec3 center, size; GetPODGridCellBBox(ix, iy, center, size); m_nOnDemandListFailures = 0; ++m_iLastPODUpdate; if (m_pPhysicsStreamer->CreatePhysicalEntitiesInBox(center - size, center + size)) { pPODcell->lifeTime = m_nOnDemandListFailures ? 1E10f : 8.0f; pPODcell->inextActive = m_iActivePODCell0; m_iActivePODCell0 = iy << 16 | ix; szList = max(szList, GetTmpEntList(pTmpEntList, iCaller)); } } } ReadLockCond lockPODr(m_lockPODGrid, 1); lockPODr.SetActive(0); m_nOnDemandListFailures = 0; } else { pPODcell->lifeTime = max(8.0f, pPODcell->lifeTime); } UnmarkAsPODThread(this); ReadLockCond relock(m_lockGrid, 1); relock.SetActive(0); } } for (int ithunk = m_pEntGrid[getcell_safe(m_entgrid, ix, iy, m_vars.iOutOfBounds)], iObjTypesValid = objtypes & 1 << m_gthunks[ithunk].iSimClass, ithunk_next = 0; ithunk && iObjTypesValid | bAreasOnly ^ 1; ithunk = ithunk_next, iObjTypesValid = objtypes & 1 << m_gthunks[ithunk].iSimClass) { ithunk_next = m_gthunks[ithunk].inext; if (iObjTypesValid && (!m_entgrid.inrange(ix, iy) || AABB_overlap(gmin, gmax, Vec3(ix + m_gthunks[ithunk].BBox[0] * (1.0f / 256), iy + m_gthunks[ithunk].BBox[1] * (1.0f / 256), m_gthunks[ithunk].BBoxZ0), Vec3(ix + (m_gthunks[ithunk].BBox[2] + 1) * (1.0f / 256), iy + (m_gthunks[ithunk].BBox[3] + 1) * (1.0f / 256), m_gthunks[ithunk].BBoxZ1))) && !(m_gthunks[ithunk].pent->m_bProcessed & maskCaller) ) { CPhysicalPlaceholder* pGridEnt = m_gthunks[ithunk].pent; int bContact; { ReadLock lock1(pGridEnt->m_lockUpdate); bContact = AABB_overlap(ptmin, ptmax, pGridEnt->m_BBox[0], pGridEnt->m_BBox[1]); } if (bContact) { if (nout >= szList) { szList = ReallocTmpEntList(pTmpEntList, iCaller, szList + 1024); } if ((unsigned int)(pGridEnt->m_iSimClass - 5) > 1u) { m_bGridThunksChanged = 0; CPhysicalEntity* pent = pGridEnt->GetEntity(); if (m_bGridThunksChanged) { ithunk_next = m_pEntGrid[getcell_safe(m_entgrid, ix, iy, m_vars.iOutOfBounds)]; } m_bGridThunksChanged = 0; int bProcessed; if (!pGridEnt->m_pEntBuddy || pent->m_pEntBuddy == pGridEnt) { if (pGridEnt->m_id <= -2) { continue; // Placeholders shouldn't be here } if (objtypes & ent_ignore_noncolliding) { int i = 0; for (; i < pent->m_nParts && !(pent->m_parts[i].flags & geom_colltype_solid); i++) { ; } if (i == pent->m_nParts) { continue; } } pTmpEntList[nout] = pent; bProcessed = iszero(m_bUpdateOnlyFlagged & ((int)pent->m_flags ^ pef_update)) | iszero(pent->m_iSimClass); bProcessed &= iszero(((CPhysicalEntity*)pGridEnt)->m_iDeletionTime); nout += bProcessed; AtomicAdd(&pGridEnt->m_bProcessed, maskCaller & - bProcessed); } else if ((m_bUpdateOnlyFlagged & ((int)pent->m_flags ^ pef_update) & - pent->m_iSimClass >> 31) == 0) { bProcessed = isneg(-(int)(pent->m_bProcessed & maskCaller)); AtomicAdd(&pent->m_lockUpdate, bProcessed ^ 1); volatile char* pw = (volatile char*)&pent->m_lockUpdate + (1 + eBigEndian); for (; *pw; ) { ; // ReadLock(m_lockUpdate) } AtomicAdd(&pent->m_bProcessed, maskCaller & ~-bProcessed); AtomicAdd(&pent->m_nUsedParts, (pent->m_nUsedParts & 15 * maskCaller4) * (bProcessed - 1)); int nUsedParts = pent->m_nUsedParts >> iCaller * 4 & 15; int notFull = nUsedParts + 1 >> 4 ^ 1; notFull &= 1 - iszero((INT_PTR)pGridEnt->m_pEntBuddy); nUsedParts += notFull; AtomicAdd(&pent->m_nUsedParts, maskCaller4 & - notFull); pent->m_pUsedParts[iCaller][nUsedParts - 1] = -2 - pGridEnt->m_id; if (!bProcessed) { pTmpEntList[nout++] = pent; } AtomicAdd(&pGridEnt->m_bProcessed, maskCaller); } //bSortRequired += pent->m_pOuterEntity!=0; } else if (pGridEnt->m_iSimClass == 5) { if (!((CPhysArea*)pGridEnt)->m_bDeleted) { pTmpEntList[nout++] = (CPhysicalEntity*)pGridEnt; AtomicAdd(&pGridEnt->m_bProcessed, maskCaller); } } else if (pGridEnt->m_iForeignFlags & itype) { event.pEntity[1] = pGridEnt->m_iForeignData == PHYS_FOREIGN_ID_PHYS_AREA ? (IPhysicalEntity*)pGridEnt->m_pForeignData : pGridEnt; event.pForeignData[1] = pGridEnt->m_pForeignData; event.iForeignData[1] = pGridEnt->m_iForeignData; OnEvent(pPetitioner->m_flags, &event); //m_pEventClient->OnBBoxOverlap(pGridEnt,pGridEnt->m_pForeignData,pGridEnt->m_iForeignData, // pPetitioner,pPetitioner->m_pForeignData,pPetitioner->m_iForeignData); } } } } } } //listfull:; } for (int i = 0; i < nout; i++) { AtomicAdd(&(pTmpEntList[i]->m_pEntBuddy ? pTmpEntList[i]->m_pEntBuddy : pTmpEntList[i])->m_bProcessed, -maskCaller); int j, nUsedParts = pTmpEntList[i]->m_nUsedParts >> iCaller * 4 & 15; nUsedParts &= ~-iszero(pTmpEntList[i]->m_iSimClass - 5); if (nUsedParts == 15) { for (j = 0; j < pTmpEntList[i]->m_nParts; j++) { AtomicAdd(&pTmpEntList[i]->m_parts[j].pPlaceholder->m_bProcessed, -(int)(pTmpEntList[i]->m_parts[j].pPlaceholder->m_bProcessed & maskCaller)); } } else { for (j = 0; j < nUsedParts; j++) { AtomicAdd(&pTmpEntList[i]->m_parts[pTmpEntList[i]->m_pUsedParts[iCaller][j]].pPlaceholder->m_bProcessed, -maskCaller); } } AtomicAdd(&pTmpEntList[i]->m_lockUpdate, -nUsedParts >> 31); } /*if (bSortRequired) { CPhysicalEntity *pent,*pents,*pstart; for(i=0;i<nout;i++) pTmpEntList[i]->m_bProcessed_aux = 1; for(i=0,pent=0;i<nout-1;i++) { pTmpEntList[i]->m_prev_aux = pent; pTmpEntList[i]->m_next_aux = pTmpEntList[i+1]; pent = pTmpEntList[i]; } pstart = pTmpEntList[0]; pTmpEntList[nout-1]->m_prev_aux = pent; pTmpEntList[nout-1]->m_next_aux = 0; for(i=0;i<nout;i++) { if ((pent=pTmpEntList[i])->m_pOuterEntity && pent->m_pOuterEntity->m_bProcessed_aux>0) { // if entity has an outer entity, move it together with its children right before this outer entity for(pents=pent,j=pent->m_bProcessed_aux-1; j>0; pents=pents->m_prev_aux); // count back the number of pent children (pents->m_prev_aux ? pent->m_prev_aux->m_next_aux : pstart) = pent->m_next_aux; // cut pents-pent stripe from list ... if (pent->m_next_aux) pent->m_next_aux->m_prev_aux = pents->m_prev_aux; pent->m_next_aux = pent->m_pOuterEntity; // ... and insert if before pent pents->m_prev_aux = pent->m_pOuterEntity->m_prev_aux; (pent->m_pOuterEntity->m_prev_aux ? pent->m_pOuterEntity->m_prev_aux->m_next_aux : pstart) = pents; pent->m_pOuterEntity->m_prev_aux = pent; pent->m_pOuterEntity->m_bProcessed_aux += pent->m_bProcessed_aux; } } Vec3 ptc = (ptmin+ptmax)*0.5f; for(i=0;i<nout;i++) pTmpEntList[i]->m_bProcessed_aux = 0; for(pent=pstart,nout=0; pent; pent=pent->m_next_aux) if (!pent->m_bProcessed_aux) { pTmpEntList[nout] = pent; if (pent->m_pOuterEntity && pent->IsPointInside(ptc)) for(pent=pent->m_pOuterEntity; pent; pent=pent->m_pOuterEntity) pent->m_bProcessed_aux=-1; pent = pTmpEntList[nout++]; } }*/ if (m_pHeightfield[iCaller] && objtypes & ent_terrain) { if (nout >= szList) { szList = ReallocTmpEntList(pTmpEntList, iCaller, szList + 1024); } pTmpEntList[nout++] = m_pHeightfield[iCaller]; } if (objtypes & ent_sort_by_mass) { for (int i = 0; i < nout; i++) { m_pMassList[i] = pTmpEntList[i]->GetMassInv(); } // manually put all static (0-massinv) object to the end of the list, since qsort doesn't // perform very well on lists of same numbers int ilast; for (int i = ilast = nout - 1; i > 0; i--) { if (m_pMassList[i] == 0) { if (i != ilast) { swap(pTmpEntList, m_pMassList, 0, i, ilast); } --ilast; } } qsort(pTmpEntList, m_pMassList, 0, 0, ilast); } if (objtypes & 1 << 5) { ReadLock lock1(m_lockAreas); if (m_pGlobalArea) { for (CPhysArea* pArea = m_pGlobalArea->m_nextBig; pArea; pArea = pArea->m_nextBig) { if (!pArea->m_bDeleted && AABB_overlap(ptmin, ptmax, pArea->m_BBox[0], pArea->m_BBox[1]) && nout < szList) { pTmpEntList[nout++] = (CPhysicalEntity*)pArea; } } } } if (szListPrealloc < nout) { if (!(objtypes & ent_allocate_list)) { pList = pTmpEntList; } else if (nout > 0) // don't allocate 0-elements arrays { pList = new CPhysicalEntity*[nout]; for (int i = 0; i < nout; i++) { pList[i] = pTmpEntList[i]; } } } else { for (int i = 0; i < nout; i++) { pList[i] = pTmpEntList[i]; } } if (objtypes & ent_addref_results) { for (int i = 0; i < nout; i++) { pList[i]->AddRef(); } } m_prevGEABBox[iCaller][0] = ptmin; m_prevGEABBox[iCaller][1] = ptmax; m_prevGEAobjtypes[iCaller] = objtypes; m_nprevGEAEnts[iCaller] = nout; return nout; } void CPhysicalWorld::ScheduleForStep(CPhysicalEntity* pent, float time_interval) { WriteLock lock(m_lockAuxStepEnt); if (!(pent->m_flags & pef_step_requested)) { pent->m_flags |= pef_step_requested; pent->m_next_coll2 = m_pAuxStepEnt; pent->m_timeIdle = time_interval; m_pAuxStepEnt = pent; } else { pent->m_timeIdle = min(pent->m_timeIdle, time_interval); } } void CPhysicalWorld::UpdateDeformingEntities(float time_interval) { WriteLock lock3(m_lockDeformingEntsList); int i, j; int iCaller = get_iCaller_int(); if (time_interval >= 0) { for (i = j = 0; i < m_nDeformingEnts; i++) { if (m_pDeformingEnts[i]->m_iSimClass != 7 && m_pDeformingEnts[i]->UpdateStructure(time_interval, 0, iCaller)) { m_pDeformingEnts[j++] = m_pDeformingEnts[i]; } else { m_pDeformingEnts[i]->m_flags &= ~pef_deforming; } } m_nDeformingEnts = j; } else { for (i = 0; i < m_nDeformingEnts; i++) { m_pDeformingEnts[i]->m_flags &= ~pef_deforming; } m_nDeformingEnts = 0; } } void SPhysTask::OnUpdate() { m_pWorld->ThreadProc(m_idx, this); } void SPhysTask::Stop() { bStop = 1; m_pWorld->m_threadStart[m_idx].Set(); } int __cursubstep = 10; void CPhysicalWorld::ProcessIslandSolverResults(int i, int iter, float groupTimeStep, float Ebefore, int nEnts, float fixedDamping, int& bAllGroupsFinished, entity_contact** pContacts, int nContacts, int nBodies, int iCaller, int64 iticks0) { int i1, j, bGroupFinished, nBrokenParts, nParts0, idCurGroup = m_pGroupIds[i]; float Eafter, damping; CPhysicalEntity* pent; iter += m_rq.iter - iter | iter >> 31; for (j = 0; j < nContacts; j++) { if ((pContacts[j]->ipart[0] | pContacts[j]->ipart[1]) >= 0) { if (pContacts[j]->pent[0]->m_parts[pContacts[j]->ipart[0]].flags & geom_monitor_contacts) { pContacts[j]->pent[0]->OnContactResolved(pContacts[j], 0, idCurGroup); } if (pContacts[j]->pent[1]->m_parts[pContacts[j]->ipart[1]].flags & geom_monitor_contacts) { pContacts[j]->pent[1]->OnContactResolved(pContacts[j], 1, idCurGroup); } } } #ifdef ENTITY_PROFILER_ENABLED i1 = CryGetTicks() - iticks0; if (m_vars.bProfileEntities > 0 && iticks0 > 0) { for (pent = m_pTmpEntList1[i], j = 0; pent; pent = pent->m_next_coll) { j += -pent->m_iSimClass >> 31 & 1; } i1 /= max(1, j); for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { if (pent->m_iSimClass > 0) { AddEntityProfileInfo(pent, i1); } } } #endif damping = 1.0f - groupTimeStep * m_vars.groupDamping * isneg(m_vars.nGroupDamping - 1 - nEnts); for (pent = m_pTmpEntList1[i], bGroupFinished = 1, Eafter = 0.0f; pent; pent = pent->m_next_coll) { Eafter += pent->CalcEnergy(0); if (!(pent->m_flags & pef_fixed_damping)) { damping = min(damping, pent->GetDamping(groupTimeStep)); } else { damping = pent->GetDamping(groupTimeStep); break; } } //Ebefore *= isneg(-nAnimatedObjects)+1; // increase energy growth limit if we have animated bodies involved if (Eafter > Ebefore * (1.0f + 0.1f * isneg(nBodies - 15))) { damping = min(damping, sqrt_tpl(Ebefore / Eafter)); } if (fixedDamping > -0.5f) { damping = fixedDamping; } for (pent = m_pTmpEntList1[i], bGroupFinished = 1; pent; pent = pent->m_next_coll) { bGroupFinished &= pent->Update(groupTimeStep, damping); } bGroupFinished |= isneg(m_vars.nMaxSubstepsLargeGroup - iter - 2 & m_vars.nBodiesLargeGroup - nBodies - 1); bGroupFinished |= isneg(m_vars.nMaxSubsteps - iter - 2); if (!bGroupFinished) { for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { pent->m_bMoved = 0; } } else { WriteLock lock1(m_lockMovedEntsList); for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { pent->m_bMoved = 3; if (pent->m_iSimClass < 3 && !pent->m_next_coll2) { pent->m_next_coll2 = (CPhysicalEntity*)m_pMovedEnts; m_pMovedEnts = pent; } } } bAllGroupsFinished &= bGroupFinished; // process deforming (breaking) enities of this group { WriteLock lock3(m_lockDeformingEntsList); for (i1 = j = nBrokenParts = 0; i1 < m_nDeformingEnts; i1++) { if (m_pDeformingEnts[i1]->m_iGroup == idCurGroup) { if ((nParts0 = m_pDeformingEnts[i1]->m_nParts) && m_pDeformingEnts[i1]->m_iSimClass != 7) { if (m_pDeformingEnts[i1]->UpdateStructure(max(groupTimeStep, 0.01f), 0, iCaller)) { m_pDeformingEnts[j++] = m_pDeformingEnts[i1]; } else { m_pDeformingEnts[i1]->m_flags &= ~pef_deforming; } nBrokenParts += -iszero((int)m_pDeformingEnts[i1]->m_flags & aef_recorded_physics) & nParts0 - m_pDeformingEnts[i1]->m_nParts; } else { m_pDeformingEnts[i1]->m_flags &= ~pef_deforming; } m_pDeformingEnts[i1]->m_iGroup = -1; } else { m_pDeformingEnts[j++] = m_pDeformingEnts[i1]; } } if (m_nDeformingEnts) { m_threadData[iCaller].pTmpPartBVListOwner = 0; // Something broke: invalidate the precomputed bv data for ropes } m_nDeformingEnts = j; } // if some entities broke, step back the velocities, but don't re-execute the step immediately if (nBrokenParts) { for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { pent->StepBack(0); } } } int CPhysicalWorld::ReadDelayedSolverResults(CMemStream& stm, float& dt, float& Ebefore, int& nEnts, float& fixedDamping, entity_contact** pContacts, RigidBody** pBodies) { int iCaller = get_iCaller_int(); int iGroup, nContacts, nBodies; stm.Read(iGroup); stm.Read(dt); stm.Read(Ebefore); stm.Read(nEnts); stm.Read(fixedDamping); stm.Read(m_threadData[iCaller].bGroupInvisible); stm.Read(nContacts); stm.ReadRaw(pContacts, sizeof(void*) * nContacts); stm.Read(nBodies); stm.ReadRaw(pBodies, sizeof(void*) * nBodies); return iGroup; } void CPhysicalWorld::ProcessNextEntityIsland(float time_interval, int ipass, int iter, int& bAllGroupsFinished, int iCaller) { int i, j, n, nEnts, nAnimatedObjects, nBodies, bStepValid, bGroupInvisible; float Ebefore, groupTimeStep, fixedDamping, maxGroupFriction; CPhysicalEntity* pent, * pent_next, * phead, ** pentlist; do { { WriteLock lock(m_lockNextEntityGroup); if (m_iCurGroup >= m_nGroups) { break; } i = m_iCurGroup++; } m_threadData[iCaller].groupMass = m_curGroupMass = m_pGroupMass[i] - m_maxGroupMass * isneg(m_maxGroupMass - m_pGroupMass[i]); m_threadData[iCaller].bGroupInvisible = 0; groupTimeStep = time_interval * (ipass ^ 1); nAnimatedObjects = 0; fixedDamping = -1.0f; Ebefore = 0.0f; for (phead = m_pTmpEntList1[i], bGroupInvisible = pef_invisible, maxGroupFriction = 100.0f; phead; phead = phead->m_next_coll) { ReadLock lockcol(phead->m_lockColliders); bGroupInvisible &= phead->m_flags; for (j = 0, n = phead->GetColliders(pentlist); j < n; j++) { if (pentlist[j]->m_iSimClass > 1 && pentlist[j]->GetMassInv() <= 0) { if (ipass) { if (pentlist[j]->m_flags & pef_fixed_damping) { fixedDamping = max(fixedDamping, pentlist[j]->GetDamping(pentlist[j]->GetMaxTimeStep(time_interval))); } groupTimeStep = max(groupTimeStep, pentlist[j]->GetLastTimeStep(time_interval)); maxGroupFriction = min(maxGroupFriction, pentlist[j]->GetMaxFriction()); RigidBody* pbody = pentlist[j]->GetRigidBody(); Vec3 sz = pentlist[j]->m_BBox[1] - pentlist[j]->m_BBox[0], v(pbody->v), w(pbody->w); if (pentlist[j]->GetType() == PE_ARTICULATED) { for (int ibody = 0; ibody < pentlist[j]->m_nParts; ibody++) { pbody = pentlist[j]->GetRigidBody(ibody); v += (pbody->v - v) * isneg(v.len2() - pbody->v.len2()); w += (pbody->w - w) * isneg(w.len2() - pbody->w.len2()); } } Ebefore += m_curGroupMass * (v.len2() + w.len2() * sqr(max(max(sz.x, sz.y), sz.z))); } else { groupTimeStep = min(groupTimeStep, pentlist[j]->GetMaxTimeStep(time_interval)); } nAnimatedObjects++; bGroupInvisible &= pentlist[j]->m_flags | ~-iszero(pentlist[j]->m_iSimClass - 2); } } } m_threadData[iCaller].bGroupInvisible = -(-bGroupInvisible >> 31); m_threadData[iCaller].maxGroupFriction = maxGroupFriction; m_threadData[MAX_PHYS_THREADS].maxGroupFriction = maxGroupFriction; if (ipass == 0) { for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { groupTimeStep = min(groupTimeStep, pent->GetMaxTimeStep(time_interval)); } for (pent = m_pTmpEntList1[i], bStepValid = 1, phead = 0; pent; pent = pent_next) { pent_next = pent->m_next_coll; if (pent->m_iSimClass < 3) { bStepValid &= (phead = pent)->Step(groupTimeStep); } pent->m_bMoved = 1; } if (!bStepValid) { for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { pent->StepBack(groupTimeStep); } } for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { pent->m_bMoved = 2; } } else if (time_interval > 0) { for (pent = m_pTmpEntList1[i], groupTimeStep = 0; pent; pent = pent->m_next_coll) { if (pent->m_iSimClass > 1) { groupTimeStep = max(groupTimeStep, pent->GetLastTimeStep(time_interval)); } } if (groupTimeStep == 0) { groupTimeStep = time_interval; } InitContactSolver(groupTimeStep); if (m_vars.nMaxPlaneContactsDistress != m_vars.nMaxPlaneContacts) { for (pent = m_pTmpEntList1[i], j = nEnts = 0; pent; pent = pent->m_next_coll, nEnts++) { j += pent->GetContactCount(m_vars.nMaxPlaneContacts); Ebefore += pent->CalcEnergy(groupTimeStep); } n = j > m_vars.nMaxContacts ? m_vars.nMaxPlaneContactsDistress : m_vars.nMaxPlaneContacts; for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { pent->RegisterContacts(groupTimeStep, n); } } else { for (pent = m_pTmpEntList1[i], nEnts = 0; pent; pent = pent->m_next_coll, nEnts++) { pent->RegisterContacts(groupTimeStep, m_vars.nMaxPlaneContacts); Ebefore += pent->CalcEnergy(groupTimeStep); } } Ebefore = max(m_pGroupMass[i] * sqr(0.005f), Ebefore); int64 iticks0 = 0; #ifdef ENTITY_PROFILER_ENABLED iticks0 = CryGetTicks(); #endif entity_contact** pContacts; int nContacts = 0; nBodies = InvokeContactSolver(groupTimeStep, &m_vars, Ebefore, pContacts, nContacts); ProcessIslandSolverResults(i, iter, groupTimeStep, Ebefore, nEnts, fixedDamping, bAllGroupsFinished, pContacts, nContacts, nBodies, iCaller, iticks0); } } while (true); } void CPhysicalWorld::ProcessNextEngagedIndependentEntity(int iCaller) { CPhysicalEntity* pent, * pentEnd, * pentNext; do { { WriteLock lock(m_lockNextEntityGroup); if (!m_pCurEnt) { break; } pent = pentEnd = (CPhysicalEntity*)m_pCurEnt; m_pCurEnt = m_pCurEnt->m_next_coll2; if (pent->m_pOuterEntity || pent->m_next_coll2 && pent->m_next_coll2->m_pOuterEntity == pent) { if (!pent->m_pOuterEntity) { pentEnd = pent->m_next_coll2; } for (; pentEnd->m_next_coll2 && pentEnd->m_next_coll2->m_pOuterEntity == pentEnd->m_pOuterEntity; pentEnd = pentEnd->m_next_coll2) { ; } m_pCurEnt = pentEnd->m_next_coll2; } } do { pentNext = pent->m_next_coll2; pent->m_next_coll2 = 0; m_threadData[iCaller].bGroupInvisible = isneg(-((int)pent->m_flags & pef_invisible)); pent->m_flags &= ~pef_step_requested; float dtFull = pent->m_timeIdle, dt; for (int iter = 0; !pent->Step(dt = pent->GetMaxTimeStep(dtFull)) && ++iter<m_vars.nMaxSubsteps&& dtFull>0.001f; ) { dtFull -= dt; } pent->m_bMoved = 0; if (pent == pentEnd) { break; } } while (pent = pentNext); } while (true); } void CPhysicalWorld::ProcessNextLivingEntity(float time_interval, int bSkipFlagged, int iCaller) { CPhysicalEntity* pent, * pentEnd; Vec3 BBox[2], BBoxNew[2], velAbs; do { { WriteLock lock(m_lockNextEntityGroup); if (!m_pCurEnt) { break; } pent = pentEnd = (CPhysicalEntity*)m_pCurEnt; velAbs = ((CLivingEntity*)pent)->m_vel.abs(); BBox[0] = pent->m_BBox[0] - velAbs; BBox[1] = pent->m_BBox[1] + velAbs; while (pentEnd->m_next) { velAbs = ((CLivingEntity*)pentEnd->m_next)->m_vel.abs(); BBoxNew[0] = pentEnd->m_next->m_BBox[0] - velAbs; BBoxNew[1] = pentEnd->m_next->m_BBox[1] + velAbs; if (AABB_overlap(BBox, BBoxNew)) { BBox[0] = min(BBox[0], BBoxNew[0]); BBox[1] = max(BBox[1], BBoxNew[1]); pentEnd = pentEnd->m_next; } else { break; } } m_pCurEnt = pentEnd->m_next; } if (m_nWorkerThreads > 0) { assert(m_nWorkerThreads + FIRST_WORKER_THREAD <= MAX_PHYS_THREADS); int i; do { do { ReadLock lockr(m_lockPlayerGroups); for (i = 0; i < m_nWorkerThreads + FIRST_WORKER_THREAD && (i == iCaller || !AABB_overlap(m_BBoxPlayerGroup[i], BBox)); i++) { ; } if (i >= m_nWorkerThreads + FIRST_WORKER_THREAD) { break; } } while (true); { WriteLock lockw(m_lockPlayerGroups); for (i = 0; i < m_nWorkerThreads + FIRST_WORKER_THREAD && (i == iCaller || !AABB_overlap(m_BBoxPlayerGroup[i], BBox)); i++) { ; } if (i >= m_nWorkerThreads + FIRST_WORKER_THREAD) { m_BBoxPlayerGroup[iCaller][0] = BBox[0]; m_BBoxPlayerGroup[iCaller][1] = BBox[1]; break; } } } while (true); } do { if (!(m_bUpdateOnlyFlagged & (pent->m_flags ^ pef_update) | bSkipFlagged & pent->m_flags)) { pent->Step(pent->GetMaxTimeStep(time_interval * m_vars.timeScalePlayers)); } if (pent == pentEnd) { break; } } while (pent = pent->m_next); if (m_nWorkerThreads > 0) { WriteLock lock(m_lockPlayerGroups); m_BBoxPlayerGroup[iCaller][0] = m_BBoxPlayerGroup[iCaller][1] = Vec3(1e10f); } } while (true); } void CPhysicalWorld::ProcessNextIndependentEntity(float time_interval, int bSkipFlagged, int iCaller) { CPhysicalEntity* pent, * pentEnd; int iter; do { { WriteLock lock(m_lockNextEntityGroup); if (!m_pCurEnt) { break; } pent = pentEnd = (CPhysicalEntity*)m_pCurEnt; m_pCurEnt = m_pCurEnt->m_next_coll2; if (pent->m_pOuterEntity || pent->m_next_coll2 && pent->m_next_coll2->m_pOuterEntity == pent) { if (!pent->m_pOuterEntity) { pentEnd = pent->m_next_coll2; } for (; pentEnd->m_next_coll2 && pentEnd->m_next_coll2->m_pOuterEntity == pentEnd->m_pOuterEntity; pentEnd = pentEnd->m_next_coll2) { ; } m_pCurEnt = pentEnd->m_next_coll2; } } do { if (!(m_bUpdateOnlyFlagged & (pent->m_flags ^ pef_update) | bSkipFlagged & pent->m_flags)) { m_threadData[iCaller].bGroupInvisible = isneg(-((int)pent->m_flags & pef_invisible)); for (iter = 0; !pent->Step(pent->GetMaxTimeStep(time_interval)) && ++iter < m_vars.nMaxSubsteps; ) { ; } } if (pent == pentEnd) { break; } } while (pent = pent->m_next_coll2); } while (true); } void CPhysicalWorld::ProcessBreakingEntities(float time_interval) { WriteLock lock3(m_lockDeformingEntsList); int i, j; int iCaller = get_iCaller_int(); for (i = j = 0; i < m_nDeformingEnts; i++) { if (m_pDeformingEnts[i]->m_iSimClass != 7 && m_pDeformingEnts[i]->UpdateStructure(time_interval, 0, iCaller)) { m_pDeformingEnts[j++] = m_pDeformingEnts[i]; } else { m_pDeformingEnts[i]->m_flags &= ~pef_deforming; } } m_nDeformingEnts = j; } void CPhysicalWorld::ThreadProc(int ithread, SPhysTask* pTask) { if (pTask->bStop) { return; } AtomicAdd(&m_nWorkerThreads, 1); MarkAsPhysWorkerThread(&ithread); static const char* tname[] = { "Physics0", "Physics1", "Physics2", "Physics3" }; if (ithread - FIRST_WORKER_THREAD < 4) { GetISystem()->GetIThreadTaskManager()->MarkThisThreadForDebugging(tname[ithread - FIRST_WORKER_THREAD], true); } #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION PHYSICALWORLD_CPP_SECTION_1 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/physicalworld_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/physicalworld_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/physicalworld_cpp_salem.inl" #endif #endif while (true) { m_threadStart[ithread - FIRST_WORKER_THREAD].Wait(); if (pTask->bStop) { m_threadDone[ithread - FIRST_WORKER_THREAD].Set(); break; } switch (m_rq.ipass) { case 0: case 1: ProcessNextEntityIsland(m_rq.time_interval, m_rq.ipass, m_rq.iter, *m_rq.pbAllGroupsFinished, ithread); break; case 2: ProcessNextEngagedIndependentEntity(ithread); break; case 3: ProcessNextLivingEntity(m_rq.time_interval, m_rq.bSkipFlagged, ithread); break; case 4: ProcessNextIndependentEntity(m_rq.time_interval, m_rq.bSkipFlagged, ithread); break; case 5: ProcessBreakingEntities(m_rq.time_interval); break; } m_threadDone[ithread - FIRST_WORKER_THREAD].Set(); } if (ithread - FIRST_WORKER_THREAD < 4) { GetISystem()->GetIThreadTaskManager()->MarkThisThreadForDebugging(tname[ithread - FIRST_WORKER_THREAD], false); } AtomicAdd(&m_nWorkerThreads, -1); } int __curstep = 0; // debug void CPhysicalWorld::TimeStep(float time_interval, int flags) { FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); float m, /*m_groupTimeStep,*/ time_interval_org = time_interval; CPhysicalEntity* pent, * phead, * ptail, ** pentlist, * pent_next, * pent1, * pentmax; int i, i1, j, n, iter, ipass, nGroups, bHeadAdded, bAllGroupsFinished, bSkipFlagged; if (time_interval < 0) { return; } //if (m_vars.bMultithreaded) // m_pLog = 0; m_vars.numThreads = min(m_vars.numThreads, MAX_PHYS_THREADS); if (m_vars.numThreads != m_nWorkerThreads + FIRST_WORKER_THREAD) { for (i = m_nWorkerThreads - 1; i >= 0; i--) { m_threads[i]->bStop = 1, m_threadStart[i].Set(), m_threadDone[i].Wait(); GetISystem()->GetIThreadTaskManager()->UnregisterTask(m_threads[i]); delete m_threads[i]; } SThreadTaskParams ttp; ttp.name = "PhysicsWorkerThread"; ttp.nFlags = THREAD_TASK_BLOCKING; for (i = 0; i < m_vars.numThreads - FIRST_WORKER_THREAD; i++) { #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION PHYSICALWORLD_CPP_SECTION_2 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/physicalworld_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/physicalworld_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/physicalworld_cpp_salem.inl" #endif #endif GetISystem()->GetIThreadTaskManager()->RegisterTask(m_threads[i] = new SPhysTask(this, i + FIRST_WORKER_THREAD), ttp); } for (; m_nWorkerThreads != m_vars.numThreads - FIRST_WORKER_THREAD; ) { Sleep(1); } } { WriteLock lock1(m_lockCaller[MAX_PHYS_THREADS]), lock2(m_lockStep); char** pQueueSlots; int nQueueSlots; volatile int64 timer = CryGetTicks(); { WriteLock lock3(m_lockQueue); pQueueSlots = m_pQueueSlots; m_pQueueSlots = m_pQueueSlotsAux; m_pQueueSlotsAux = pQueueSlots; nQueueSlots = m_nQueueSlots; m_nQueueSlots = m_nQueueSlotsAux; m_nQueueSlotsAux = nQueueSlots; i = m_nQueueSlotsAlloc; m_nQueueSlotsAlloc = m_nQueueSlotsAllocAux; m_nQueueSlotsAllocAux = i; i = m_nQueueSlotSize; m_nQueueSlotSize = m_nQueueSlotSizeAux; m_nQueueSlotSizeAux = i; } if (time_interval > 0) { MarkAsPhysThread(); } phys_geometry* pgeom; for (i = 0; i < nQueueSlots; i++) { for (j = 0; (iter = *(int*)(pQueueSlots[i] + j)) != -1; j += *(int*)(pQueueSlots[i] + j + sizeof(int))) { if (iter < 0) { continue; } pent = *(CPhysicalEntity**)(pQueueSlots[i] + j + sizeof(int) * 2); if (!(pent->m_iSimClass == 7 || pent->m_iSimClass == 5 && pent->m_iDeletionTime == 2)) { switch (iter) { case 0: pent->SetParams((pe_params*)(pQueueSlots[i] + j + sizeof(int) * 2 + sizeof(void*)), 1); break; case 1: pent->Action((pe_action*)(pQueueSlots[i] + j + sizeof(int) * 2 + sizeof(void*)), 1); break; case 2: pent->AddGeometry(pgeom = *(phys_geometry**)(pQueueSlots[i] + j + sizeof(int) * 2 + sizeof(void*)), (pe_geomparams*)(pQueueSlots[i] + j + sizeof(int) * 3 + sizeof(void*) * 2), *(int*)(pQueueSlots[i] + j + sizeof(int) * 2 + sizeof(void*) * 2), 1); AtomicAdd(&pgeom->nRefCount, -1); break; case 3: pent->RemoveGeometry(*(int*)(pQueueSlots[i] + j + sizeof(int) * 2 + sizeof(void*) * 2), 1); break; case 4: pent->m_flags &= ~0x80000000u; AtomicAdd(&m_lockGrid, -RepositionEntity(pent, *(int*)(pQueueSlots[i] + j + sizeof(int) * 2 + sizeof(void*)), 0, 1)); if (++m_nEnts > m_nEntsAlloc - 1) { m_nEntsAlloc += 4096; m_nEntListAllocs++; m_bEntityCountReserved = 0; ReallocateList(m_pTmpEntList, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pTmpEntList1, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pTmpEntList2, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pGroupMass, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pMassList, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pGroupIds, m_nEnts - 1, m_nEntsAlloc); ReallocateList(m_pGroupNums, m_nEnts - 1, m_nEntsAlloc); } break; case 5: DestroyPhysicalEntity((IPhysicalEntity*)pent, *(int*)(pQueueSlots[i] + j + sizeof(int) * 2 + sizeof(void*)), 1); } } if (iter != 4) { AtomicAdd(&pent->m_bProcessed, -PENT_QUEUED); } } } if (nQueueSlots) { m_nQueueSlotsAux = 1; m_nQueueSlotSizeAux = 0; *(int*)m_pQueueSlotsAux[0] = -1; } m_grpProfileData[13].nTicksLast = CryGetTicks() - timer; } { WriteLock lock(m_lockStep); if (time_interval > 0 && !(flags & ent_flagged_only)) { MarkAsPhysThread(); } volatile int64 timer = CryGetTicks(); if (time_interval > m_vars.maxWorldStep) { time_interval = time_interval_org = m_vars.maxWorldStep; } if (m_vars.timeGranularity > 0) { i = float2int(time_interval_org * (m_vars.rtimeGranularity = 1.0f / m_vars.timeGranularity)); time_interval_org = time_interval = i * m_vars.timeGranularity; m_iTimePhysics += i; m_timePhysics = m_iTimePhysics * m_vars.timeGranularity; } else { m_timePhysics += time_interval; } if (m_vars.fixedTimestep > 0 && time_interval > 0) { time_interval = m_vars.fixedTimestep; } m_bUpdateOnlyFlagged = flags & ent_flagged_only; bSkipFlagged = flags >> 1 & pef_update; m_bWorldStep = 1; m_vars.bMultiplayer = gEnv->bMultiplayer; m_vars.bUseDistanceContacts &= m_vars.bMultiplayer ^ 1; m_lastTimeInterval = time_interval; if (!m_bUpdateOnlyFlagged) { m_vars.lastTimeStep = time_interval; } if (m_pGlobalArea && !is_unused(m_pGlobalArea->m_gravity)) { m_pGlobalArea->m_gravity = m_vars.gravity; } m_rq.time_interval = time_interval; m_rq.bSkipFlagged = bSkipFlagged; m_rq.pbAllGroupsFinished = &bAllGroupsFinished; m_pMovedEnts = 0; if (flags & ent_living) { for (pent = m_pTypedEnts[3]; pent; pent = pent->m_next) { if (!(m_bUpdateOnlyFlagged & (pent->m_flags ^ pef_update) | bSkipFlagged & pent->m_flags)) { pent->StartStep(time_interval_org * m_vars.timeScalePlayers); // prepare to advance living entities } } } if (!m_vars.bSingleStepMode || m_vars.bDoStep) { i = m_vars.timeScalePlayers != 1.0f && time_interval > 0.0f; if ((m_nSlowFrames = (m_nSlowFrames & - i) + i) > 4 && m_iLastLogPump < m_vars.nStartupOverloadChecks) { // force-freeze dynamic ents in cases of massive slowdowns pe_status_dynamics sd; for (pent = m_pTypedEnts[2]; pent; pent = pent->m_next) { if (pent->GetStatus(&sd) && sd.v.len2() < sqr(5.0f)) { pent->Awake(0); } } for (pent = m_pTypedEnts[4]; pent; pent = pent->m_next) { pent->Awake(0); } } { SBreakRequest curreq; do { { ReadLock lockbq(m_lockBreakQueue); if (m_breakQueueSz == 0) { break; } curreq = m_breakQueue[m_breakQueueTail]; m_breakQueueTail = m_breakQueueTail + 1 - (m_breakQueueAlloc & m_breakQueueAlloc - 2 - m_breakQueueTail >> 31); m_breakQueueSz--; } if (curreq.pent->m_iSimClass != 7) { int ipart; for (ipart = curreq.pent->m_nParts - 1; ipart >= 0 && curreq.pent->m_parts[ipart].id != curreq.partid; ipart--) { ; } if (ipart >= 0 && DeformEntityPart(curreq.pent, ipart, &curreq.expl, curreq.gwd, curreq.gwd + 1) && curreq.pent->UpdateStructure(0.01f, &curreq.expl, -1, curreq.gravity)) { MarkEntityAsDeforming(curreq.pent); } } curreq.pent->Release(); } while (true); } iter = 0; __curstep++; if (!(__curstep & 7)) { memset(g_idata[0].UsedNodesMap, 0, sizeof(g_idata[0].UsedNodesMap)); } if (flags & ent_independent) { for (pent = m_pTypedEnts[4]; pent; pent = pent->m_next) { if (!(m_bUpdateOnlyFlagged & (pent->m_flags ^ pef_update) | bSkipFlagged & pent->m_flags)) { pent->StartStep(time_interval); } } } if (flags & ent_rigid && time_interval > 0) { if (m_pTypedEnts[2]) { do // make as many substeps as required { bAllGroupsFinished = 1; m_pAuxStepEnt = 0; m_pGroupNums[m_nEntsAlloc - 1] = -1; // special group for rigid bodies w/ infinite mass m_threadData[0].bGroupInvisible = 0; m_iSubstep++; for (ipass = 0; ipass < 2; ipass++) { // build lists of intercolliding groups of entities for (pent = m_pTypedEnts[2], nGroups = 0, m_threadData[0].groupMass = 1.0f; pent; pent = pent_next) { pent_next = pent->m_next; if (!(pent->m_bMoved | m_bUpdateOnlyFlagged & (pent->m_flags ^ pef_update) | bSkipFlagged & pent->m_flags)) { if (pent->GetMassInv() <= 0) { if ((iter | ipass) == 0) // just make isolated step for rigids with infinite mass { pent->StartStep(time_interval); pent->m_iGroup = -1;//m_nEntsAlloc-1; } if (ipass == 0) { pent->Step(/*m_groupTimeStep = */ pent->GetMaxTimeStep(time_interval)); bAllGroupsFinished &= pent->Update(time_interval, 1); } } else { pent->m_iGroup = nGroups; pent->m_bMoved = 1; m_pGroupIds[nGroups] = 0; m_pGroupMass[nGroups] = 1.0f / pent->GetMassInv(); if ((iter | ipass) == 0) { pent->StartStep(time_interval); } pent->m_next_coll1 = pent->m_next_coll = 0; m_pTmpEntList1[nGroups] = 0; // initially m_pTmpEntList1 points to group entities that collide with statics (sorted by mass) - linked via m_next_coll // m_next_coll1 maintains a queue of current intercolliding objects for (phead = ptail = pentmax = pent; phead; phead = phead->m_next_coll1) { for (i = bHeadAdded = 0, n = phead->GetColliders(pentlist); i < n; i++) { if (pentlist[i]->GetMassInv() <= 0) { if (!bHeadAdded) { for (pent1 = m_pTmpEntList1[nGroups]; pent1 && pent1->m_next_coll && pent1->m_next_coll->GetMassInv() <= phead->GetMassInv(); pent1 = pent1->m_next_coll) { ; } if (!pent1 || pent1->GetMassInv() > phead->GetMassInv()) { phead->m_next_coll = pent1; m_pTmpEntList1[nGroups] = phead; } else { phead->m_next_coll = pent1->m_next_coll; pent1->m_next_coll = phead; } bHeadAdded = 1; } m_pGroupIds[nGroups] = 1; // tells that group has static entities } else if (!(pentlist[i]->m_bMoved | m_bUpdateOnlyFlagged & (pentlist[i]->m_flags ^ pef_update))) { pentlist[i]->m_flags &= ~bSkipFlagged; ptail->m_next_coll1 = pentlist[i]; ptail = pentlist[i]; ptail->m_next_coll1 = 0; ptail->m_next_coll = 0; ptail->m_iGroup = nGroups; ptail->m_bMoved = 1; if ((iter | ipass) == 0) { ptail->StartStep(time_interval); } m_pGroupMass[nGroups] += 1.0f / (m = ptail->GetMassInv()); if (pentmax->GetMassInv() > m) { pentmax = ptail; } } } } if (!m_pTmpEntList1[nGroups]) { m_pTmpEntList1[nGroups] = pentmax; } nGroups++; } } } // add maximum group mass to all groups that contain static entities for (i = 1, m = m_pGroupMass[0]; i < nGroups; i++) { m = max(m, m_pGroupMass[i]); } for (m *= 1.01f, i = 0; i < nGroups; i++) { m_pGroupMass[i] += m * m_pGroupIds[i]; } for (i = 0; i < nGroups; i++) { m_pGroupIds[i] = i; } // sort groups by decsending group mass qsort(m_pTmpEntList1, m_pGroupMass, m_pGroupIds, 0, nGroups - 1); for (i = 0; i < nGroups; i++) { m_pGroupNums[m_pGroupIds[i]] = i; } for (i = 0; i < nGroups; i++) { for (ptail = m_pTmpEntList1[i]; ptail->m_next_coll; ptail = ptail->m_next_coll) { ptail->m_bMoved = 0; } ptail->m_bMoved = 0; for (phead = m_pTmpEntList1[i]; phead; phead = phead->m_next_coll) { for (j = 0, n = phead->GetColliders(pentlist); j < n; j++) { if (pentlist[j]->GetMassInv() > 0) { if (pentlist[j]->m_bMoved == 1 && !(m_bUpdateOnlyFlagged & (pentlist[j]->m_flags ^ pef_update))) { ptail->m_next_coll = pentlist[j]; ptail = pentlist[j]; ptail->m_next_coll = 0; ptail->m_bMoved = 0; } time_interval = min(time_interval, pentlist[j]->GetMaxTimeStep(time_interval)); } } } } m_nGroups = nGroups; m_maxGroupMass = m; m_rq.iter = iter; m_iCurGroup = 0; THREAD_TASK(ipass, ProcessNextEntityIsland(time_interval, ipass, iter, bAllGroupsFinished, 0)); if (ipass == 0) { for (i = 0; i < m_nGroups; i++) { for (pent = m_pTmpEntList1[i]; pent; pent = pent->m_next_coll) { pent->m_bMoved = 0, pent->m_iGroup = -1; } } m_bWorldStep = 2; for (i = 0, pent = m_pAuxStepEnt; pent; pent = pent->m_next_coll2, i++) { m_pTmpEntList1[i] = pent; if (pent->GetType() == PE_LIVING) { ((CLivingEntity*)pent)->SyncWithGroundCollider(pent->m_timeIdle); } } if (i > 0) { GroupByOuterEntity(m_pTmpEntList1, 0, i - 1); for (m_pTmpEntList1[--i]->m_next_coll2 = 0, --i; i >= 0; i--) { m_pTmpEntList1[i]->m_next_coll2 = m_pTmpEntList1[i + 1]; } m_pAuxStepEnt = m_pTmpEntList1[0]; } m_pCurEnt = m_pAuxStepEnt; m_pAuxStepEnt = 0; THREAD_TASK(2, ProcessNextEngagedIndependentEntity(0)) m_bWorldStep = 1; } } } while (!bAllGroupsFinished && ++iter < m_vars.nMaxSubsteps); } for (pent = (CPhysicalEntity*)m_pMovedEnts; pent; pent = pent_next) { pent_next = pent->m_next_coll2; pent->m_bMoved = 0, pent->m_iGroup = -1; pent->m_next_coll2 = 0; } for (pent = m_pTypedEnts[4]; pent; pent = pent->m_next) { pent->m_bMoved = 0, pent->m_iGroup = -1; } m_updateTimes[1] = m_updateTimes[2] = m_timePhysics; } { ReadLock lockwm(m_lockWaterMan); for (CWaterMan* pWaterMan = m_pWaterMan; pWaterMan; pWaterMan = pWaterMan->m_next) { pWaterMan->TimeStep(time_interval); } } for (i = 0; i < m_nProfiledEnts; i++) { m_pEntProfileData[i].nTicksStep &= -m_pEntProfileData[i].nTicks >> 31; m_pEntProfileData[i].nTicksLast = m_pEntProfileData[i].nTicks; m_pEntProfileData[i].nTicks = 0; m_pEntProfileData[i].nCallsLast = m_pEntProfileData[i].nCalls; m_pEntProfileData[i].nCalls = 0; } } m_iSubstep++; if (flags & ent_living) { m_pCurEnt = m_pTypedEnts[3]; THREAD_TASK(3, ProcessNextLivingEntity(time_interval, bSkipFlagged, 0)) /*for(pent=m_pTypedEnts[3]; pent; pent=pent_next) { pent_next = pent->m_next; if (!(m_bUpdateOnlyFlagged&(pent->m_flags^pef_update) | bSkipFlagged&pent->m_flags)) pent->Step(pent->GetMaxTimeStep(time_interval_org*m_vars.timeScalePlayers)); // advance living entities }*/ m_updateTimes[3] = m_timePhysics; } if (!m_vars.bSingleStepMode || m_vars.bDoStep) { if (flags & ent_independent) { m_pCurEnt = m_pTypedEntsPerm[4]; for (pent = m_pTypedEntsPerm[4]; pent; pent = pent->m_next) { pent->m_next_coll2 = pent->m_next; } THREAD_TASK(4, ProcessNextIndependentEntity(time_interval, bSkipFlagged, 0)); m_updateTimes[4] = m_timePhysics; } } if (flags & ent_deleted) { if (!m_vars.bSingleStepMode || m_vars.bDoStep) { // process deforming (breaking) enities { WriteLock lock3(m_lockDeformingEntsList); for (i = j = 0; i < m_nDeformingEnts; i++) { if (m_pDeformingEnts[i]->m_iSimClass != 7 && m_pDeformingEnts[i]->UpdateStructure(time_interval, 0)) { m_pDeformingEnts[j++] = m_pDeformingEnts[i]; } else { m_pDeformingEnts[i]->m_flags &= ~pef_deforming; } } m_nDeformingEnts = j; m_updateTimes[0] = m_timePhysics; } CleanseEventsQueue(); // remove events that reference deleted entities for (pent = m_pTypedEnts[7]; pent; pent = pent_next) // purge deletion requests { pent_next = pent->m_next; if (m_iLastLogPump >= pent->m_iDeletionTime && pent->m_nRefCount <= 0) { if (pent->m_next) { pent->m_next->m_prev = pent->m_prev; } (pent->m_prev ? pent->m_prev->m_next : m_pTypedEnts[7]) = pent->m_next; pent->Delete(); } } //m_pTypedEnts[7] = 0; } // flush timeouted sectors for cell-based physics-on-demand { WriteLock lockPOD(m_lockPODGrid); MarkAsPODThread(this); pe_PODcell* pPODcell; int* picellNext; for (i = m_iActivePODCell0, picellNext = &m_iActivePODCell0; i >= 0; i = pPODcell->inextActive) { if (((pPODcell = getPODcell(i & 0xFFFF, i >> 16))->lifeTime -= time_interval_org) <= 0 || pPODcell->lifeTime > 1E9f) { Vec3 center, sz; ++m_iLastPODUpdate; GetPODGridCellBBox(i & 0xFFFF, i >> 16, center, sz); m_pPhysicsStreamer->DestroyPhysicalEntitiesInBox(center - sz, center + sz); *picellNext = pPODcell->inextActive; } else { picellNext = &pPODcell->inextActive; } } UnmarkAsPODThread(this); } // flush static and sleeping physical objects that have timeouted for (i = 0; i < 2; i++) { for (pent = m_pTypedEnts[i]; pent && pent != m_pTypedEntsPerm[i]; pent = pent_next) { pent_next = pent->m_next; { WriteLock lockEnt(pent->m_lockUpdate); for (j = 0; j < pent->m_nParts && !(pent->m_parts[j].flags & geom_can_modify); j++) { ; } if (j < pent->m_nParts || pent->m_pStructure && pent->m_pStructure->bModified) { j = -1; } } if (j == -1) { j -= pent_next == m_pTypedEntsPerm[i]; pent->m_bPermanent = 1; ChangeEntitySimClass(pent, 0); if (pent->m_pEntBuddy) { CPhysicalPlaceholder* ppc = pent->m_pEntBuddy; ppc->m_pEntBuddy = 0; ppc->m_iGThunk0 = 0; SetPhysicalEntityId(ppc, -1, 1, 1); ppc->m_id = -1; pent->m_pEntBuddy = 0; SetPhysicalEntityId(pent, pent->m_id, 1, 1); for (i1 = pent->m_iGThunk0; i1; i1 = m_gthunks[i1].inextOwned) { m_gthunks[i1].pent = pent; } DestroyPhysicalEntity(ppc, 0, 1); } if (j == -2) { break; } } else if (pent->m_nRefCount == 0 && ((pent->m_timeIdle += time_interval_org) > pent->m_maxTimeIdle || pent->m_timeIdle < 0)) { DestroyPhysicalEntity(pent, 0, 1); } } } for (pent = m_pTypedEnts[2]; pent != m_pTypedEntsPerm[2]; pent = pent->m_next) { pent->m_timeIdle = 0; // reset idle count for active physical entities } /*for(pent=m_pTypedEnts[4]; pent!=m_pTypedEntsPerm[4]; pent=pent_next) { assert(pent); pent_next = pent->m_next; if (pent->IsAwake()) pent->m_timeIdle = 0; // reset idle count for active detached entities else if (pent->m_nRefCount==0 && (pent->m_timeIdle+=time_interval_org)>pent->m_maxTimeIdle) DestroyPhysicalEntity(pent); }*/ { // update active areas CPhysArea* pArea, * pNextArea; for (pArea = m_pActiveArea, m_pActiveArea = 0; pArea; pArea = pNextArea) { // Update the chain to remove this item so it can add itself back pNextArea = pArea->m_nextActive; pArea->m_nextActive = pArea; // Run the update now. pArea->Update(time_interval); } } // flush deleted areas { WriteLock lockAreas(m_lockAreas); CPhysArea* pArea, ** ppNextArea = &m_pDeletedAreas; for (pArea = m_pDeletedAreas; pArea; pArea = *ppNextArea) { if (pArea->m_lockRef == 0) { *ppNextArea = pArea->m_next; delete pArea; } else { ppNextArea = &pArea->m_next; } } } // invalidate the precomputed bv data for ropes for (i = 0; i <= MAX_PHYS_THREADS; i++) { m_threadData[i].pTmpPartBVListOwner = 0; } m_updateTimes[7] = m_timePhysics; if (m_vars.bDoStep == 2) { m_vars.bDoStep = 0; SerializeWorld("worldents.txt", 1); SerializeGeometries("worldgeoms.txt", 1); } m_vars.bDoStep = 0; } m_bUpdateOnlyFlagged = 0; m_bWorldStep = 0; if (time_interval > 0) { ++m_idStep; } if (m_vars.bProfileGroups) { for (i = 0, m_grpProfileData[12].nTicksLast = 0; i <= 10; i++) { m_grpProfileData[12].nTicksLast += (m_grpProfileData[i].nTicksLast = m_grpProfileData[i].nTicks); m_grpProfileData[i].nTicks = 0; m_grpProfileData[i].nCallsLast = 0; } for (pent = m_pTypedEnts[2]; pent; pent = pent->m_next) { m_grpProfileData[GetEntityProfileType(pent)].nCallsLast++; } for (pent = m_pTypedEnts[3]; pent; pent = pent->m_next) { m_grpProfileData[PE_LIVING - 2].nCallsLast++; } for (pent = m_pTypedEntsPerm[4]; pent; pent = pent->m_next) { m_grpProfileData[GetEntityProfileType(pent)].nCallsLast++; } m_grpProfileData[14].nTicksLast = CryGetTicks() - timer; } } // m_lockStep FlushOldThunks(); } void CPhysicalWorld::FlushOldThunks() { WriteLock lock(m_lockOldThunks); pe_gridthunk* pthunks = m_oldThunks, * pthunksNext; for (; pthunks; pthunks = pthunksNext) { pthunksNext = *(pe_gridthunk**)pthunks; FreeThunks(pthunks); } m_oldThunks = 0; geom* pparts = m_oldEntParts, * ppartsNext; for (m_oldEntParts = 0; pparts; pparts = ppartsNext) { ppartsNext = (geom*)pparts->pLattice; delete[] pparts; } FlushOldGeoms(); } #define TrackThunkUsageAlloc(thunk) #define TrackThunkUsageFree(thunk) #if defined(__GNUC__) #if __GNUC__ >= 4 && __GNUC__MINOR__ < 7 #pragma GCC diagnostic ignored "-Woverflow" #else #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverflow" #endif #endif void CPhysicalWorld::DetachEntityGridThunks(CPhysicalPlaceholder* pobj) { if (pobj->m_iGThunk0) { int ithunk, ithunk_next, ithunk_last, icell, iprev, inext; for (ithunk = pobj->m_iGThunk0; ithunk; ithunk = ithunk_next) { TrackThunkUsageFree(ithunk); ithunk_next = m_gthunks[ithunk].inextOwned; iprev = m_gthunks[ithunk].iprev; inext = m_gthunks[ithunk].inext; m_gthunks[ithunk].pent = 0; #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconstant-conversion" #endif m_gthunks[ithunk].inext = m_gthunks[ithunk].iprev = -1; #if defined(__clang__) #pragma clang diagnostic pop #endif m_gthunks[inext].iprev = iprev & - (int)inext >> 31; m_gthunks[inext].bFirstInCell = m_gthunks[ithunk].bFirstInCell; if (m_gthunks[ithunk].bFirstInCell) { icell = m_entgrid.size.x * m_entgrid.size.y; if (m_pEntGrid[icell] != ithunk) { icell = Vec2i(iprev & 1023, iprev >> 10 & 1023) * m_entgrid.stride; } m_pEntGrid[(unsigned int)icell] = inext; } else { m_gthunks[iprev].inext = inext; } ithunk_last = ithunk; } m_gthunks[ithunk_last].inextOwned = m_iFreeGThunk0; m_iFreeGThunk0 = pobj->m_iGThunk0; pobj->m_iGThunk0 = 0; } } #if defined(__GNUC__) #if __GNUC__ >= 4 && __GNUC__MINOR__ < 7 #pragma GCC diagnostic error "-Woverflow" #else #pragma GCC diagnostic pop #endif #endif void CPhysicalWorld::SortThunks() { WriteLock lock(m_lockGrid); if (!m_thunkPoolSz) { //Nothing to sort. return; } int i, j, icell, nthunks = 1; int* new2old = new int[m_thunkPoolSz], * old2new = new int[m_thunkPoolSz]; for (icell = 0; icell <= m_entgrid.size.x * m_entgrid.size.y; icell++) { for (i = m_pEntGrid[icell]; i; i = m_gthunks[i].inext) { new2old[nthunks++] = i; } } for (i = m_iFreeGThunk0; i; i = m_gthunks[i].inextOwned) { PREFAST_SUPPRESS_WARNING(6386) new2old[nthunks++] = i; } assert(nthunks == m_thunkPoolSz); PREFAST_ASSUME(nthunks == m_thunkPoolSz); for (i = 1, old2new[0] = 0; i < nthunks; i++) { old2new[new2old[i]] = i; } for (i = 0; i < nthunks; i++) { if (m_gthunks[i].pent) { m_gthunks[i].inext = old2new[m_gthunks[i].inext]; if (m_gthunks[i].bFirstInCell) { icell = Vec2i(max(0, min(m_entgrid.size.x - 1, (int)m_gthunks[i].iprev & 1023)), max(0, min(m_entgrid.size.y - 1, (int)m_gthunks[i].iprev >> 10 & 1023))) * m_entgrid.stride; if (m_pEntGrid[(unsigned int)icell] != i) { icell = m_entgrid.size.x * m_entgrid.size.y; } m_pEntGrid[(unsigned int)icell] = old2new[i]; } else { m_gthunks[i].iprev = old2new[m_gthunks[i].iprev]; } } m_gthunks[i].inextOwned = old2new[m_gthunks[i].inextOwned]; } for (i = 1; i < nthunks; i++) { if (m_gthunks[i].pent && !(m_gthunks[i].pent->m_bProcessed & 1 << 31)) { int idxNew = old2new[m_gthunks[i].pent->m_iGThunk0]; if (m_gthunks[i].pent->m_pEntBuddy && m_gthunks[i].pent->m_pEntBuddy->m_iGThunk0 == m_gthunks[i].pent->m_iGThunk0) { m_gthunks[i].pent->m_pEntBuddy->m_iGThunk0 = idxNew; } m_gthunks[i].pent->m_iGThunk0 = idxNew; m_gthunks[i].pent->m_bProcessed |= 1 << 31; } } for (i = 1; i < nthunks; i++) { if (m_gthunks[i].pent) { m_gthunks[i].pent->m_bProcessed &= ~(1 << 31); } } m_iFreeGThunk0 = old2new[m_iFreeGThunk0]; for (i = 1; i < nthunks; i++) { if (old2new[i] != i) { pe_gridthunk thunk = m_gthunks[i]; j = old2new[i]; do { pe_gridthunk thunkNext = m_gthunks[j]; int jnext = old2new[j]; m_gthunks[j] = thunk; old2new[j] = j; if (j == i) { break; } thunk = thunkNext; j = jnext; } while (true); } } //for(i=0;i<nthunks && old2new[i]==i;i++); //assert(i==nthunks); delete[] old2new; delete[] new2old; } int CPhysicalWorld::ChangeEntitySimClass(CPhysicalEntity* pent, int bGridLocked) { // This function should NEVER be called from a non-physics thread // // Uncomment this code here to force a crash to catch incorrect accesses //if (get_iCaller()==MAX_PHYS_THREADS && m_bWorldStep>0 && m_vars.lastTimeStep>0) //{ // volatile int *p = NULL; // *p = 10; //} if ((unsigned int)(pent->m_iPrevSimClass - 0x100) < 8u) { VALIDATOR_LOG(m_pLog, "Error: *major* problem - trying to change iSimClass of a hidden entity!"); if (m_vars.iDrawHelpers & 0x100 << ent_rigid) { DoBreak; } return 0; } { WriteLock lock(m_lockList); const unsigned int iSimClass = pent->m_iSimClass, iPrevSimClass = pent->m_iPrevSimClass; CPhysicalEntity* pent0 = pent, * pent1 = pent; int bPermanent = pent->m_bPermanent; if (iSimClass == 4 && iPrevSimClass == 4 && pent->m_pOuterEntity) { for (; pent0->m_prev && pent0->m_prev->m_pOuterEntity == pent->m_pOuterEntity; bPermanent |= (pent0 = pent0->m_prev)->m_bPermanent) { ; } for (; pent1->m_next && pent1->m_next->m_pOuterEntity == pent->m_pOuterEntity; bPermanent |= (pent1 = pent1->m_next)->m_bPermanent) { ; } } if (iPrevSimClass < 8u) { if (pent1->m_next) { pent1->m_next->m_prev = pent0->m_prev; } (pent0->m_prev ? pent0->m_prev->m_next : m_pTypedEnts[iPrevSimClass]) = pent1->m_next; for (CPhysicalEntity* penti = pent0; penti != pent1->m_next; penti = penti->m_next) { if (penti == m_pTypedEntsPerm[iPrevSimClass]) { m_pTypedEntsPerm[iPrevSimClass] = pent1->m_next; break; } } } PREFAST_ASSUME(pent0); if (!bPermanent) { pent1->m_next = m_pTypedEnts[pent->m_iSimClass]; pent0->m_prev = 0; if (pent1->m_next) { pent1->m_next->m_prev = pent1; } m_pTypedEnts[iSimClass] = pent0; } else { pent1->m_next = m_pTypedEntsPerm[iSimClass]; if (m_pTypedEntsPerm[iSimClass]) { if (pent0->m_prev = m_pTypedEntsPerm[iSimClass]->m_prev) { pent0->m_prev->m_next = pent0; } pent1->m_next->m_prev = pent1; } else if (m_pTypedEnts[iSimClass]) { for (pent0->m_prev = m_pTypedEnts[iSimClass]; pent0->m_prev->m_next; pent0->m_prev = pent0->m_prev->m_next) { ; } pent0->m_prev->m_next = pent0; } else { pent0->m_prev = 0; } if (m_pTypedEntsPerm[iSimClass] == m_pTypedEnts[iSimClass]) { m_pTypedEnts[iSimClass] = pent0; } m_pTypedEntsPerm[iSimClass] = pent0; } for (; pent0 != pent1; pent0 = pent0->m_next) { pent0->m_bPrevPermanent = bPermanent; pent0->m_iPrevSimClass = iSimClass; } pent1->m_bPrevPermanent = bPermanent; pent1->m_iPrevSimClass = iSimClass; } { ReadLockCond gridLock(m_lockGrid, !bGridLocked); for (int ithunk = pent->m_iGThunk0; ithunk; ithunk = m_gthunks[ithunk].inextOwned) { m_gthunks[ithunk].iSimClass = pent->m_iSimClass; } } return 1; } int CPhysicalWorld::RepositionEntity(CPhysicalPlaceholder* pobj, int flags, Vec3* BBox, int bQueued) { int i, j, igx[2], igy[2], igxInner[2], igyInner[2], igz[2], ix, iy, ithunk, ithunk0; unsigned int n; if ((unsigned int)pobj->m_iSimClass >= 7u) { return 0; // entity is frozen } int bGridLocked = 0; int bBBoxUpdated = 0; EventPhysStateChange event; if (flags & 1 && m_pEntGrid) { i = -iszero((INT_PTR)BBox); Vec3* pBBox = (Vec3*)((INT_PTR)pobj->m_BBox & (INT_PTR)i | (INT_PTR)BBox & ~(INT_PTR)i); for (i = 0; i < 2; i++) { float x = (pBBox[i][m_iEntAxisx] - m_entgrid.origin[m_iEntAxisx]) * m_entgrid.stepr.x; igx[i] = m_entgrid.crop(float2int(x - 0.5f), 0); igxInner[i] = max(0, min(255, float2int((x - igx[i]) * 256.0f - 0.5f))); x = (pBBox[i][m_iEntAxisy] - m_entgrid.origin[m_iEntAxisy]) * m_entgrid.stepr.y; igy[i] = m_entgrid.crop(float2int(x - 0.5f), 1); igyInner[i] = max(0, min(255, float2int((x - igy[i]) * 256.0f - 0.5f))); igz[i] = (int)((pBBox[i][m_iEntAxisz] - m_entgrid.origin[m_iEntAxisz]) * m_rzGran) + i; } if (pobj->m_ig[0].x != NO_GRID_REG) // if m_igx[0] is NO_GRID_REG, the entity should not be registered in grid at all { if (igx[0] - pobj->m_ig[0].x | igy[0] - pobj->m_ig[0].y | igx[1] - pobj->m_ig[1].x | igy[1] - pobj->m_ig[1].y | flags & 4 | flags >> 2 & 1 ^ pobj->m_bOBBThunks) { CPhysicalPlaceholder* pcurobj = pobj; if (IsPlaceholder(pobj->m_pEntBuddy)) { goto skiprepos; //pcurobj = pobj->m_pEntBuddy; } SpinLock(&m_lockGrid, 0, bGridLocked = WRITE_LOCK_VAL); m_bGridThunksChanged = 1; DetachEntityGridThunks(pobj); n = (igx[1] - igx[0] + 1) * (igy[1] - igy[0] + 1); if (pobj->m_iSimClass != 5) { if (n == 0 || n > (unsigned int)m_vars.nMaxEntityCells) { Vec3 pos = (pcurobj->m_BBox[0] + pcurobj->m_BBox[1]) * 0.5f; char buf[256]; sprintf_s(buf, "Error: %s @ %.1f,%.1f,%.1f is too large or invalid", !m_pRenderer ? "entity" : m_pRenderer->GetForeignName(pcurobj->m_pForeignData, pcurobj->m_iForeignData, pcurobj->m_iForeignFlags), pos.x, pos.y, pos.z); VALIDATOR_LOG(m_pLog, buf); if (m_vars.bBreakOnValidation) { DoBreak; } pobj->m_ig[0].x = pobj->m_ig[1].x = pobj->m_ig[0].y = pobj->m_ig[1].y = GRID_REG_PENDING; goto skiprepos; } } else if (n > (unsigned int)m_vars.nMaxAreaCells) { return -1; } for (ix = igx[0]; ix <= igx[1]; ix++) { for (iy = igy[0]; iy <= igy[1]; iy++) { if ((flags & 4) && (ix | iy) >= 0) { float xMin = (ix * m_entgrid.step.x) + m_entgrid.origin[m_iEntAxisx]; float yMin = (iy * m_entgrid.step.y) + m_entgrid.origin[m_iEntAxisy]; float zMin = igz[0] * m_zGran + m_entgrid.origin[m_iEntAxisz]; float zMax = igz[1] * m_zGran + m_entgrid.origin[m_iEntAxisz]; Vec3 bbmin(xMin, yMin, zMin), bbmax(xMin + m_entgrid.step.x, yMin + m_entgrid.step.y, zMax); if (m_iEntAxisz < 2) { int newz = m_iEntAxisz ^ 1; bbmin.GetPermutated(newz); bbmax.GetPermutated(newz); } AABB bbox(bbmin, bbmax); if (!((CPhysicalEntity*)pobj)->OccupiesEntityGridSquare(bbox)) { continue; } } j = m_entgrid.getcell_safe(ix, iy); if (!m_iFreeGThunk0) { if (m_thunkPoolSz >= 1 << 20) { static bool g_bSpammed = false; if (!g_bSpammed) { VALIDATOR_LOG(m_pLog, "Error: too many entity grid thunks created, further repositions ignored"); } g_bSpammed = true; goto skiprepos; } const int increaseThunks = (64 * 1024) / sizeof(pe_gridthunk); // Increase in multiples of 64K to avoid fragmentation AllocGThunksPool(m_thunkPoolSz + increaseThunks); } ithunk = m_iFreeGThunk0; m_iFreeGThunk0 = m_gthunks[m_iFreeGThunk0].inextOwned; TrackThunkUsageAlloc(ithunk); pe_gridthunk* __restrict pNewGThunk = &m_gthunks[ithunk]; pNewGThunk->inextOwned = pcurobj->m_iGThunk0; pcurobj->m_iGThunk0 = ithunk; pcurobj->m_bOBBThunks = flags >> 2 & 1; int ithunkGrid = m_pEntGrid[j]; if (!ithunkGrid || m_gthunks[ithunkGrid].iSimClass != 5) { int& entGridEntry = m_pEntGrid[(unsigned int)j]; pNewGThunk->bFirstInCell = 1; pNewGThunk->iprev = ((iy & m_entgrid.size.y - 1) << 10 | ix & m_entgrid.size.x - 1); pNewGThunk->inext = entGridEntry; m_gthunks[ithunkGrid].iprev = ithunk & - ithunkGrid >> 31; m_gthunks[ithunkGrid].bFirstInCell = 0; entGridEntry = ithunk; } else { for (ithunk0 = ithunkGrid; m_gthunks[m_gthunks[ithunk0].inext].iSimClass == 5; ithunk0 = m_gthunks[ithunk0].inext) { ; } pNewGThunk->bFirstInCell = 0; pNewGThunk->inext = m_gthunks[ithunk0].inext; pNewGThunk->iprev = ithunk0; m_gthunks[m_gthunks[ithunk0].inext].iprev = ithunk & - (int)m_gthunks[ithunk0].inext >> 31; m_gthunks[ithunk0].inext = ithunk; } pNewGThunk->iSimClass = pcurobj->m_iSimClass; pNewGThunk->BBox[0] = igxInner[0] & ~(igx[0] - ix >> 31); pNewGThunk->BBox[1] = igyInner[0] & ~(igy[0] - iy >> 31); pNewGThunk->BBox[2] = igxInner[1] + (255 - igxInner[1] & ix - igx[1] >> 31); pNewGThunk->BBox[3] = igyInner[1] + (255 - igyInner[1] & iy - igy[1] >> 31); pNewGThunk->BBoxZ0 = igz[0]; pNewGThunk->BBoxZ1 = igz[1]; pNewGThunk->pent = pcurobj; } } pcurobj->m_ig[0].x = igx[0]; pcurobj->m_ig[1].x = igx[1]; pcurobj->m_ig[0].y = igy[0]; pcurobj->m_ig[1].y = igy[1]; if (pcurobj->m_pEntBuddy && pcurobj->m_pEntBuddy->m_pEntBuddy == pcurobj) { pcurobj->m_pEntBuddy->m_iGThunk0 = pcurobj->m_iGThunk0; pcurobj->m_pEntBuddy->m_ig[0].x = igx[0]; pcurobj->m_pEntBuddy->m_ig[1].x = igx[1]; pcurobj->m_pEntBuddy->m_ig[0].y = igy[0]; pcurobj->m_pEntBuddy->m_ig[1].y = igy[1]; } skiprepos:; } else if (pobj->m_iGThunk0) { for (ix = igx[1], ithunk = pobj->m_iGThunk0; ix >= igx[0]; ix--) { for (iy = igy[1]; iy >= igy[0]; iy--, ithunk = m_gthunks[ithunk].inextOwned) { m_gthunks[ithunk].BBox[0] = igxInner[0] & ~(igx[0] - ix >> 31); m_gthunks[ithunk].BBox[1] = igyInner[0] & ~(igy[0] - iy >> 31); m_gthunks[ithunk].BBox[2] = igxInner[1] + (255 - igxInner[1] & ix - igx[1] >> 31); m_gthunks[ithunk].BBox[3] = igyInner[1] + (255 - igyInner[1] & iy - igy[1] >> 31); m_gthunks[ithunk].BBoxZ0 = igz[0]; m_gthunks[ithunk].BBoxZ1 = igz[1]; } } } } if (bBBoxUpdated = BBox && (((pobj->m_BBox[0] - BBox[0]).len2() + (pobj->m_BBox[1] - BBox[1]).len2()) > 0)) { event.BBoxNew[0] = BBox[0]; event.BBoxNew[1] = BBox[1]; } else { event.BBoxNew[0] = pobj->m_BBox[0]; event.BBoxNew[1] = pobj->m_BBox[1]; } } else { event.BBoxNew[0] = pobj->m_BBox[0]; event.BBoxNew[1] = pobj->m_BBox[1]; } int bSimClassUpdated = 0; if (flags & 2) { CPhysicalEntity* pent = (CPhysicalEntity*)pobj; if (pent->m_iPrevSimClass != pent->m_iSimClass || pent->m_bPermanent != pent->m_bPrevPermanent) { bSimClassUpdated = 1; i = pent->m_iPrevSimClass; ChangeEntitySimClass(pent, bGridLocked); /*#ifdef _DEBUG CPhysicalEntity *ptmp = m_pTypedEnts[1]; for(;ptmp && ptmp!=m_pTypedEntsPerm[1]; ptmp=ptmp->m_next); if (ptmp!=m_pTypedEntsPerm[1]) DEBUG_BREAK; #endif*/ } } if (bBBoxUpdated | bSimClassUpdated) { CPhysicalEntity* pent = (CPhysicalEntity*)pobj; if (pent->m_flags & (pef_monitor_state_changes | pef_log_state_changes)) { event.pEntity = pent; event.pForeignData = pent->m_pForeignData; event.iForeignData = pent->m_iForeignData; event.BBoxOld[0] = pent->m_BBox[0]; event.BBoxOld[1] = pent->m_BBox[1]; event.iSimClass[0] = bSimClassUpdated ? i : pent->m_iSimClass; event.iSimClass[1] = pent->m_iSimClass; event.timeIdle = pent->m_timeIdle; OnEvent(pent->m_flags, &event); } } return bGridLocked; } ///////////////////////////////////////////////////////////////////////////////////////////////////// float CPhysicalWorld::IsAffectedByExplosion(IPhysicalEntity* pobj, Vec3* impulse) { int i; CPhysicalEntity* pent = ((CPhysicalPlaceholder*)pobj)->GetEntityFast(); for (i = 0; i < m_nExplVictims && m_pExplVictims[i] != pent; i++) { ; } if (i < m_nExplVictims) { if (impulse) { *impulse = m_pExplVictimsImp[i]; } return m_pExplVictimsFrac[i]; } if (impulse) { impulse->zero(); } return 0.0f; } int CPhysicalWorld::DeformPhysicalEntity(IPhysicalEntity* pient, const Vec3& ptHit, const Vec3& dirHit, float r, int flags) { // craig - experimental fix: i think the random number in GetExplosionShape() is upsetting things in MP if (m_vars.bMultiplayer) { cry_random_seed(1234567); } int i, bEntChanged, bPartChanged; CPhysicalEntity* pent = (CPhysicalEntity*)pient; pe_explosion expl; geom_world_data gwd, gwd1; box bbox; Vec3 zaxWorld(0, 0, 1), zaxObj, zax; gwd1.offset = ptHit; (zaxWorld -= dirHit * (zaxWorld * dirHit)).normalize(); expl.epicenter = expl.epicenterImp = ptHit; expl.impulsivePressureAtR = 0; expl.r = expl.rmin = expl.holeSize = r; if (flags & 2) // special values for explosion { expl.explDir = dirHit; expl.impulsivePressureAtR = -1; } expl.iholeType = 0; { WriteLockCond lockc(m_lockCaller[get_iCaller()], m_vars.bLogStructureChanges); WriteLockCond lock(pent->m_lockUpdate, m_vars.bLogStructureChanges); for (i = bEntChanged = 0; i < pent->m_nParts; i++) { if ((pent->m_parts[i].flags & (geom_colltype_explosion | geom_removed)) == geom_colltype_explosion && pent->m_parts[i].idmatBreakable >= 0) { pent->m_parts[i].pPhysGeomProxy->pGeom->GetBBox(&bbox); zaxObj = pent->m_qrot * (pent->m_parts[i].q * bbox.Basis.GetRow(idxmax3(bbox.size))); if (pent->m_iSimClass > 0 || fabs_tpl(zaxObj * zaxWorld) < 0.7f) { (zax = zaxObj - dirHit * (zaxObj * dirHit)).normalize(); } else { zax = zaxWorld; } gwd1.R.SetColumn(0, dirHit ^ zax); gwd1.R.SetColumn(1, dirHit); gwd1.R.SetColumn(2, zax); gwd.R = Matrix33(pent->m_qrot * pent->m_parts[i].q); gwd.offset = pent->m_pos + pent->m_qrot * pent->m_parts[i].pos; gwd.scale = pent->m_parts[i].scale; bEntChanged += (bPartChanged = DeformEntityPart(pent, i, &expl, &gwd, &gwd1, 1)); if (bPartChanged) { pent->m_parts[i].flags &= ~(flags >> 16 & 0xFFFF); } } } } if (bEntChanged && pent->UpdateStructure(0.01f, &expl, MAX_PHYS_THREADS)) { MarkEntityAsDeforming(pent); } return bEntChanged; } void CPhysicalWorld::ClonePhysGeomInEntity(CPhysicalEntity* pent, int i, IGeometry* pNewGeom) { phys_geometry* pgeom; if (pNewGeom->GetType() == GEOM_TRIMESH && pent->m_parts[i].pLattice) { (pent->m_parts[i].pLattice = new CTetrLattice(pent->m_parts[i].pLattice, 1))->SetMesh((CTriMesh*)pNewGeom); } { WriteLock lock(m_lockGeoman); *(pgeom = GetFreeGeomSlot()) = *pent->m_parts[i].pPhysGeomProxy; pgeom->pGeom = pNewGeom; pgeom->nRefCount = 1; pgeom->surface_idx = 0; if (pgeom->pMatMapping) { memcpy(pgeom->pMatMapping = new int[pgeom->nMats], pent->m_parts[i].pPhysGeomProxy->pMatMapping, pgeom->nMats * sizeof(int)); } } if (pent->m_parts[i].pPhysGeom->pMatMapping == pent->m_parts[i].pMatMapping) { pent->m_parts[i].pMatMapping = pgeom->pMatMapping; } UnregisterGeometry(pent->m_parts[i].pPhysGeom); if (pent->m_parts[i].pPhysGeomProxy != pent->m_parts[i].pPhysGeom) { UnregisterGeometry(pent->m_parts[i].pPhysGeomProxy); } pent->m_parts[i].pPhysGeomProxy = pent->m_parts[i].pPhysGeom = pgeom; pent->m_parts[i].flags |= geom_can_modify; } int CPhysicalWorld::DeformEntityPart(CPhysicalEntity* pent, int i, pe_explosion* pexpl, geom_world_data* gwd, geom_world_data* gwd1, int iSource) { IGeometry* pGeom, * pHole; CTriMesh* pNewGeom = 0; EventPhysUpdateMesh epum; int bCreateConstraint = 0; epum.pEntity = pent; epum.pForeignData = pent->m_pForeignData; epum.iForeignData = pent->m_iForeignData; epum.partid = pent->m_parts[i].id; epum.iReason = iSource ? EventPhysUpdateMesh::ReasonRequest : EventPhysUpdateMesh::ReasonExplosion; epum.bInvalid = 0; if ((pent->m_parts[i].idmatBreakable >> 7 | pexpl->iholeType) != 0 && !(pent->m_parts[i].idmatBreakable & 128 << pexpl->iholeType) || pent->m_parts[i].pPhysGeomProxy->pGeom->GetiForeignData() == DATA_UNSCALED_GEOM) { return 0; } for (int j = 0; j < min(pent->m_nParts, 5); j++) { if (i != j && pent->m_parts[j].flags & geom_log_interactions && pent->m_parts[j].pPhysGeom->pGeom->PointInsideStatus( ((pexpl->epicenter - pent->m_pos) * pent->m_qrot - pent->m_parts[j].pos) * pent->m_parts[j].q * ( pent->m_parts[j].scale == 1.0f ? 1.0f : 1.0f / pent->m_parts[j].scale))) { return 0; } } if (pHole = GetExplosionShape(pexpl->holeSize, pent->m_parts[i].idmatBreakable, gwd1->scale, bCreateConstraint)) { pGeom = pent->m_parts[i].pPhysGeomProxy->pGeom; if (!(pent->m_parts[i].flags & geom_can_modify)) { if (!(pNewGeom = (CTriMesh*)((CGeometry*)pGeom)->GetTriMesh())) { return 0; } if (pent->m_parts[i].flags & geom_break_approximation) { pNewGeom->m_flags |= mesh_force_AABB; } pGeom = pNewGeom; } else { box bbox, bbox1; pGeom->GetBBox(&bbox); pHole->GetBBox(&bbox1); float szmin0 = min(min(bbox.size.x, bbox.size.y), bbox.size.z); if (szmin0 > max(max(bbox.size.x, bbox.size.y), bbox.size.z) * 0.4f && szmin0 < max(max(bbox1.size.x, bbox1.size.y), bbox1.size.z) * 1.5f && ++((CTriMesh*)pGeom)->m_nMessyCutCount >= 5) { return 0; } } #ifdef _DEBUG1 static CTriMesh* g_pPrevGeom = 0; if (m_vars.iDrawHelpers & 0x4000) { pent->m_parts[i].pPhysGeomProxy->pGeom = pGeom = g_pPrevGeom; } else if (g_pPrevGeom) { g_pPrevGeom->Release(); } (g_pPrevGeom = new CTriMesh())->Clone((CTriMesh*)pGeom, 0); g_pPrevGeom->RebuildBVTree(((CTriMesh*)pGeom)->m_pTree); #endif if (pGeom->Subtract(pHole, gwd, gwd1)) { if (pNewGeom) { ClonePhysGeomInEntity(pent, i, pNewGeom); } if (pent->m_parts[i].pLattice) { pent->m_parts[i].pLattice->Subtract(pHole, gwd, gwd1); } (epum.pMesh = pGeom)->Lock(0); epum.pLastUpdate = (bop_meshupdate*)epum.pMesh->GetForeignData(DATA_MESHUPDATE); for (; epum.pLastUpdate && epum.pLastUpdate->next; epum.pLastUpdate = epum.pLastUpdate->next) { ; } epum.pMesh->Unlock(0); OnEvent(m_vars.bLogStructureChanges + 1, &epum); pent->m_parts[i].flags |= geom_structure_changes | (geom_constraint_on_break & - bCreateConstraint); return 1; } else if (pNewGeom) { pNewGeom->Release(); } } return 0; } void CPhysicalWorld::MarkEntityAsDeforming(CPhysicalEntity* pent) { if (!(pent->m_flags & pef_deforming)) { WriteLock lock(m_lockDeformingEntsList); pent->m_flags |= pef_deforming; if (m_nDeformingEnts == m_nDeformingEntsAlloc) { ReallocateList(m_pDeformingEnts, m_nDeformingEnts, m_nDeformingEntsAlloc += 16); } m_pDeformingEnts[m_nDeformingEnts++] = pent; } } void CPhysicalWorld::UnmarkEntityAsDeforming(CPhysicalEntity* pent) { if (pent->m_flags & pef_deforming) { WriteLock lock(m_lockDeformingEntsList); pent->m_flags &= ~pef_deforming; int i; for (i = m_nDeformingEnts - 1; i >= 0 && m_pDeformingEnts[i] != pent; i--) { ; } if (i >= 0) { m_pDeformingEnts[i] = m_pDeformingEnts[--m_nDeformingEnts]; } } } void CPhysicalWorld::SimulateExplosion(pe_explosion* pexpl, IPhysicalEntity** pSkipEnts, int nSkipEnts, int iTypes, int iCaller) { FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); CPhysicalEntity** pents; int nents, nents1, i, j, i1, bBreak, bEntChanged; RigidBody* pbody; float kr = pexpl->impulsivePressureAtR * sqr(pexpl->r), maxspeed = 15, E, frac = 1.0f, sumFrac, sumV, Minv; Vec3 gravity; pe_params_buoyancy pb; geom_world_data gwd, gwd1; box bboxPart, bbox; sphere sphExpl; CPhysicalPlaceholder** pSkipPcs = (CPhysicalPlaceholder**)pSkipEnts; pe_action_impulse shockwave; shockwave.iApplyTime = 2; shockwave.iSource = 2; EventPhysCollision epc; epc.pEntity[0] = &g_StaticPhysicalEntity; epc.pForeignData[0] = 0; epc.iForeignData[0] = 0; epc.vloc[1].zero(); epc.mass[0] = 1E10f; epc.partid[0] = 0; epc.idmat[0] = 0; epc.penetration = epc.radius = 0; if (!CheckAreas(pexpl->epicenter, gravity, &pb, 1, -1, Vec3(ZERO), 0, iCaller) || is_unused(gravity)) { gravity = m_vars.gravity; } WriteLock lock(m_lockCaller[iCaller]); bboxPart.bOriented = 0; bboxPart.Basis.SetIdentity(); sphExpl.center = pexpl->epicenter; sphExpl.r = pexpl->rmax; if (pexpl->rmin < FLT_EPSILON) { pexpl->rmin = 0.1f; } if (m_vars.bDebugExplosions) { m_vars.bSingleStepMode = 1; } CPhysicalEntity** pSkipPhysEnts = (CPhysicalEntity**)pSkipEnts; for (i = 0; i < nSkipEnts; i++) { if (pSkipPhysEnts[i]->m_flags & pef_traceable) { AtomicAdd(&((!pSkipPhysEnts[i]->m_pEntBuddy || IsPlaceholder(pSkipPhysEnts[i])) ? pSkipPhysEnts[i] : pSkipPhysEnts[i]->m_pEntBuddy)->m_bProcessed, 1 << iCaller); } else { CPhysicalPlaceholder* pPartPlaceholder; int numParts = pSkipPhysEnts[i]->m_nParts; for (int p = 0; p < numParts; p++) { if (pPartPlaceholder = pSkipPhysEnts[i]->m_parts[p].pPlaceholder) { AtomicAdd(&pPartPlaceholder->m_bProcessed, 1 << iCaller); } } } } #ifdef _DEBUG if (m_vars.iDrawHelpers & 0x4000) { pexpl->epicenter = m_lastEpicenter; pexpl->epicenterImp = m_lastEpicenterImp; pexpl->explDir = m_lastExplDir; } #endif if (pexpl->holeSize > 0) { gwd1.R.SetRotationV0V1(Vec3(0, 1, 0), pexpl->explDir); gwd1.offset = pexpl->epicenter; } if (pexpl->nOccRes > 0) { m_cubeMapStatic.Init(pexpl->nOccRes, pexpl->rminOcc, pexpl->rmax); m_cubeMapStatic.Reset(); m_cubeMapDynamic.Init(pexpl->nOccRes, pexpl->rminOcc, pexpl->rmax); m_lastEpicenter = pexpl->epicenter; m_lastEpicenterImp = pexpl->epicenterImp; m_lastExplDir = pexpl->explDir; } if (pexpl->nOccRes > 0 || pexpl->holeSize > 0) { for (nents = GetEntitiesAround(pexpl->epicenter - Vec3(1, 1, 1) * pexpl->rmax, pexpl->epicenter + Vec3(1, 1, 1) * pexpl->rmax, pents, ent_terrain | ent_static | ent_rigid, 0, 0, iCaller) - 1; nents >= 0; nents--) { if (pents[nents]->m_iSimClass < 1 || pents[nents]->GetMassInv() <= 0) { int bMarkDeforming = 0; { WriteLock lock0(pents[nents]->m_lockUpdate); for (i1 = bEntChanged = 0; i1 < pents[nents]->GetUsedPartsCount(iCaller); i1++) { if (pents[nents]->m_parts[i = pents[nents]->GetUsedPart(iCaller, i1)].flags & geom_colltype_explosion && (pents[nents]->m_nParts <= 1 || (bboxPart.center = (pents[nents]->m_parts[i].BBox[1] + pents[nents]->m_parts[i].BBox[0]) * 0.5f, bboxPart.size = (pents[nents]->m_parts[i].BBox[1] - pents[nents]->m_parts[i].BBox[0]) * 0.5f, box_sphere_overlap_check(&bboxPart, &sphExpl)))) { bBreak = (m_vars.breakImpulseScale || pents[nents]->m_flags & pef_override_impulse_scale || pexpl->forceDeformEntities) && iTypes & 1 << pents[nents]->m_iSimClass && pexpl->holeSize > 0 && pents[nents]->m_parts[i].idmatBreakable >= 0 && !(pents[nents]->m_parts[i].flags & geom_manually_breakable); if (pexpl->nOccRes <= 0 && !bBreak) { continue; } gwd.R = Matrix33(pents[nents]->m_qrot * pents[nents]->m_parts[i].q); gwd.offset = pents[nents]->m_pos + pents[nents]->m_qrot * pents[nents]->m_parts[i].pos - pexpl->epicenter; gwd.scale = pents[nents]->m_parts[i].scale; if (pexpl->nOccRes > 0 && (!(pents[nents]->m_parts[i].flags & geom_manually_breakable) || (pents[nents]->m_parts[i].pPhysGeomProxy->pGeom->GetBBox(&bbox), min(min(bbox.size.x, bbox.size.y), bbox.size.z) > pexpl->rminOcc))) { pents[nents]->m_parts[i].pPhysGeomProxy->pGeom->BuildOcclusionCubemap(&gwd, 0, &m_cubeMapStatic, &m_cubeMapDynamic, pexpl->nGrow); } if (bBreak) { gwd.offset += pexpl->epicenter; if (!(iTypes & ent_delayed_deformations)) { bEntChanged += DeformEntityPart(pents[nents], i, pexpl, &gwd, &gwd1); } else { WriteLock lockbq(m_lockBreakQueue); ReallocQueue(m_breakQueue, m_breakQueueSz, m_breakQueueAlloc, m_breakQueueHead, m_breakQueueTail, 4); (m_breakQueue[m_breakQueueHead].pent = pents[nents])->AddRef(); m_breakQueue[m_breakQueueHead].partid = pents[nents]->m_parts[i].id; m_breakQueue[m_breakQueueHead].expl = *pexpl; m_breakQueue[m_breakQueueHead].gwd[0] = gwd; m_breakQueue[m_breakQueueHead].gwd[1] = gwd1; m_breakQueue[m_breakQueueHead].gravity = gravity; m_breakQueueSz++; } } if (pents[nents]->m_pStructure && pents[nents]->m_pStructure->defparts && pents[nents]->m_pStructure->defparts[i].pSkelEnt) { Vec3 pt = pents[nents]->m_pStructure->defparts[i].lastUpdateq * (!pents[nents]->m_qrot * (pexpl->epicenterImp - pents[nents]->m_pos)) + pents[nents]->m_pStructure->defparts[i].lastUpdatePos; pents[nents]->m_pStructure->defparts[i].pSkelEnt->ApplyVolumetricPressure(pt, kr, pexpl->rmin); bMarkDeforming = 1; } } } } if (bEntChanged && pents[nents]->UpdateStructure(0.01f, pexpl, -1, gravity) || bMarkDeforming) { MarkEntityAsDeforming(pents[nents]); } } } } nents = GetEntitiesAround(pexpl->epicenter - Vec3(1, 1, 1) * pexpl->rmax, pexpl->epicenter + Vec3(1, 1, 1) * pexpl->rmax, pents, iTypes, 0, 0, iCaller); if (pexpl->nOccRes < 0 && m_cubeMapStatic.N >= 0) { // special case: reuse the previous cubeMapStatic and process only entities that were not affected by the previous call for (i = nents1 = 0; i < nents; i++) { for (j = 0; j < m_nExplVictims && m_pExplVictims[j] != pents[i]; j++) { ; } if (j == m_nExplVictims) { pents[nents1++] = pents[i]; } } pexpl->nOccRes = m_cubeMapStatic.N; nents = nents1; } if (m_nExplVictimsAlloc < nents) { if (m_nExplVictimsAlloc) { delete[] m_pExplVictims; delete[] m_pExplVictimsFrac; delete[] m_pExplVictimsImp; } m_pExplVictims = new CPhysicalEntity*[m_nExplVictimsAlloc = nents]; m_pExplVictimsFrac = new float[m_nExplVictimsAlloc]; m_pExplVictimsImp = new Vec3[m_nExplVictimsAlloc]; } for (nents--, m_nExplVictims = 0; nents >= 0; nents--) { int bMarkDeforming = 0; { ReadLock lock0(pents[nents]->m_lockUpdate); m_pExplVictimsImp[m_nExplVictims].zero(); for (i1 = bEntChanged = 0, sumFrac = sumV = 0.0f; i1 < pents[nents]->GetUsedPartsCount(iCaller); i1++) { if ((pents[nents]->m_parts[i = pents[nents]->GetUsedPart(iCaller, i1)].flags & geom_colltype_explosion || pents[nents]->m_flags & pef_use_geom_callbacks) && (pents[nents]->m_nParts <= 1 || (bboxPart.center = (pents[nents]->m_parts[i].BBox[1] + pents[nents]->m_parts[i].BBox[0]) * 0.5f, bboxPart.size = (pents[nents]->m_parts[i].BBox[1] - pents[nents]->m_parts[i].BBox[0]) * 0.5f, box_sphere_overlap_check(&bboxPart, &sphExpl)))) { bBreak = pents[nents]->GetMassInv() > 0 && !(pents[nents]->m_parts[i].flags & geom_manually_breakable); if (bBreak || pents[nents]->m_parts[i].flags & (geom_monitor_contacts | geom_manually_breakable)) { gwd.R = Matrix33(pents[nents]->m_qrot * pents[nents]->m_parts[i].q); gwd.offset = pents[nents]->m_pos + pents[nents]->m_qrot * pents[nents]->m_parts[i].pos; gwd.scale = pents[nents]->m_parts[i].scale; IGeometry* pGeom = pents[nents]->m_parts[i].pPhysGeomProxy->pGeom; if (pexpl->nOccRes > 0) { gwd.offset -= pexpl->epicenter; frac = static_cast<CGeometry*>(pGeom)->BuildOcclusionCubemap(&gwd, 1, &m_cubeMapStatic, &m_cubeMapDynamic, pexpl->nGrow); gwd.offset += pexpl->epicenter; float Vsafe = max(0.0001f, pents[nents]->m_parts[i].pPhysGeomProxy->V); sumFrac += Vsafe * frac; sumV += Vsafe; } if (bBreak) { if (pexpl->holeSize > 0 && pents[nents]->m_parts[i].idmatBreakable >= 0) { bEntChanged += DeformEntityPart(pents[nents], i, pexpl, &gwd, &gwd1); } } if (kr > 0) { if (!(pents[nents]->m_flags & pef_use_geom_callbacks)) { shockwave.impulse.zero(); shockwave.angImpulse.zero(); pbody = pents[nents]->GetRigidBody(i); Minv = pents[nents]->GetMassInv(); pGeom->CalcVolumetricPressure(&gwd, pexpl->epicenterImp, kr, pexpl->rmin, pbody->pos, shockwave.impulse, shockwave.angImpulse); shockwave.impulse *= frac; shockwave.angImpulse *= frac; shockwave.ipart = i; if ((E = shockwave.impulse.len2() * sqr(Minv)) > sqr(maxspeed)) { shockwave.impulse *= sqrt_tpl(sqr(maxspeed) / E); } if ((E = shockwave.angImpulse * (pbody->Iinv * shockwave.angImpulse) * Minv) > sqr(maxspeed)) { shockwave.angImpulse *= sqrt_tpl(sqr(maxspeed) / E); } if (pents[nents]->m_pStructure && pents[nents]->m_pStructure->defparts && pents[nents]->m_pStructure->defparts[i].pSkelEnt) { Vec3 pt = pents[nents]->m_pStructure->defparts[i].lastUpdateq * (!pents[nents]->m_qrot * (pexpl->epicenterImp - pents[nents]->m_pos)) + pents[nents]->m_pStructure->defparts[i].lastUpdatePos; pents[nents]->m_pStructure->defparts[i].pSkelEnt->ApplyVolumetricPressure(pt, kr * frac, pexpl->rmin); bMarkDeforming = 1; } pents[nents]->Action(&shockwave, -(iCaller - MAX_PHYS_THREADS >> 31)); m_pExplVictimsImp[m_nExplVictims] += shockwave.impulse; } else { pents[nents]->ApplyVolumetricPressure(pexpl->epicenterImp, kr * frac, pexpl->rmin); } } } else if (pents[nents]->m_flags & pef_use_geom_callbacks) { pents[nents]->ApplyVolumetricPressure(pexpl->epicenterImp, kr * frac, pexpl->rmin); } if ((pents[nents]->m_parts[i].flags & (geom_manually_breakable | geom_structure_changes)) == geom_manually_breakable && (pexpl->nOccRes == 0 || sumFrac > 0)) { int iprim = 0, ifeat, ncont, bMultipart; Vec3 ptdst[2]; CBoxGeom boxGeom; intersection_params ip; geom_contact* pcontacts; float rscale; mesh_data* pmd = 0; pents[nents]->m_parts[i].pPhysGeomProxy->pGeom->GetBBox(&bbox); if (pents[nents]->m_parts[i].idmatBreakable >= 0 || pents[nents]->m_parts[i].flags & geom_break_approximation || pents[nents]->m_parts[i].pPhysGeomProxy->pGeom->GetPrimitiveCount() <= 1 || !(pmd = (mesh_data*)pents[nents]->m_parts[i].pPhysGeomProxy->pGeom->GetData()) || pmd->nIslands <= 1) { if (!pmd || !pmd->pMats) { epc.idmat[1] = pents[nents]->GetMatId(-1, i); } else { for (iprim = pmd->nTris - 1; iprim >= 0 && !(m_SurfaceFlagsTable[epc.idmat[1] = pents[nents]->GetMatId(pmd->pMats[iprim], i)] & sf_manually_breakable); iprim--) { ; } epc.iPrim[1] = iprim; } epc.n.zero(); bMultipart = 0; goto single_island; } else { for (j = 0; j < pmd->nIslands; j++) { if (((ptdst[0] = gwd.R * pmd->pIslands[j].center * gwd.scale + gwd.offset) - sphExpl.center).len2() < sqr(sphExpl.r * 2)) { epc.n.zero(); bMultipart = 1; if (!pmd || !pmd->pMats) { epc.idmat[1] = pents[nents]->GetMatId(-1, i); } else { Vec3 BBox[2] = { pmd->pIslands[j].center, pmd->pIslands[j].center }; for (iprim = pmd->pIslands[j].itri, epc.idmat[1] = -1, epc.iPrim[1] = -1; iprim < pmd->nTris && pmd->pMats[iprim] >= 0; iprim = pmd->pTri2Island[iprim].inext) { int idmat = pents[nents]->GetMatId(pmd->pMats[iprim], i); int bBreakable = -((int)m_SurfaceFlagsTable[idmat] & sf_manually_breakable) >> 31; epc.idmat[1] += idmat - epc.idmat[1] & bBreakable; epc.iPrim[1] += iprim - epc.iPrim[1] & bBreakable; for (int ivtx = 0; ivtx < 3; ivtx++) { Vec3 vtx = pmd->pVertices[pmd->pIndices[iprim * 3 + ivtx]]; BBox[0] = min(BBox[0], vtx); BBox[1] = max(BBox[1], vtx); } } if (iprim < pmd->nTris || epc.idmat[1] < 0) { continue; } epc.n[idxmin3(bbox.size = BBox[1] - BBox[0])] = 1.0f; epc.n = gwd.R * epc.n; epc.vloc[0] = -(epc.n *= sgnnz(epc.n * (sphExpl.center - ptdst[0]))); } goto post_event; single_island: boxGeom.CreateBox(&bbox); if (boxGeom.FindClosestPoint(&gwd, iprim, ifeat, pexpl->epicenter, pexpl->epicenter, ptdst, 1) < 0 || (pexpl->epicenter - ptdst[0]) * (ptdst[0] - gwd.offset - gwd.R * bbox.center * gwd.scale) < 0) { ptdst[0] = pexpl->epicenter; } if (pents[nents]->m_parts[i].idmatBreakable >= 0) { if ((ptdst[0] - ptdst[1]).len2() > sqr(pexpl->rmin * 0.7f + pexpl->rmax * 0.3f)) { goto next_part; } j = idxmax3(bbox.size); rscale = gwd.scale == 1.0f ? 1.0f : 1.0f / gwd.scale; ptdst[1].z = (bbox.Basis.GetRow(j) * ((ptdst[0] - gwd.offset) * gwd.R - bbox.center)) * rscale; ptdst[1].z = max(-bbox.size[j] * 0.8f, min(bbox.size[j] * 0.8f, ptdst[1].z)); bbox.center += bbox.Basis.GetRow(j) * ptdst[1].z; bbox.size[inc_mod3[j]] *= 1.01f; bbox.size[dec_mod3[j]] *= 1.01f; bbox.size[j] *= 0.002f; boxGeom.CreateBox(&bbox); if (ncont = pents[nents]->m_parts[i].pPhysGeomProxy->pGeom->Intersect(&boxGeom, 0, 0, &ip, pcontacts) && pcontacts->idxborder) { ptdst[0].Set(1E10f, 1E10f, 1E10f); ptdst[1] = ((pexpl->epicenter - gwd.offset) * gwd.R) * rscale; float dist, mindist = 1E10f; for (ncont--; ncont >= 0; ncont--) { for (j = 0; j < pcontacts[ncont].nborderpt; j++) { Vec3 vtx0 = pcontacts[ncont].ptborder[j], vtx1 = pcontacts[ncont].ptborder[j + 1 - (pcontacts[ncont].nborderpt & pcontacts[ncont].nborderpt - j - 2 << 31)]; if ((dist = (vtx0 - ptdst[1]).len2()) < mindist) { epc.n = pents[nents]->m_parts[i].pPhysGeomProxy->pGeom->GetNormal(pcontacts[ncont].idxborder[j][0] & IDXMASK, ptdst[0] = vtx0), mindist = dist; } float proj = (ptdst[1] - vtx0) * (vtx1 - vtx0), edgelen = (vtx1 - vtx0).len2(); if (inrange(proj, 0.0f, edgelen) && (dist = (ptdst[1] - vtx0 ^ vtx1 - vtx0).len2()) < mindist * edgelen) { mindist = dist * (edgelen = 1.0f / edgelen); ptdst[0] = vtx0 + (vtx1 - vtx0) * (proj * edgelen); epc.n = pents[nents]->m_parts[i].pPhysGeomProxy->pGeom->GetNormal(pcontacts[ncont].idxborder[j][0] & IDXMASK, ptdst[0]); } } } ptdst[0] = gwd.R * ptdst[0] * gwd.scale + gwd.offset; epc.vloc[0] = -(epc.n = gwd.R * epc.n); } } post_event: epc.pt = ptdst[0]; epc.pEntity[1] = pents[nents]; epc.pForeignData[1] = pents[nents]->m_pForeignData; epc.iForeignData[1] = pents[nents]->m_iForeignData; if ((epc.pt - pexpl->epicenter).len2() < sqr(pexpl->holeSize * 0.2f) * (1 - bMultipart)) { epc.pt = pexpl->epicenter; if (!epc.n.len2()) { epc.n = -(epc.vloc[0] = pexpl->explDir); } } else if (!epc.n.len2()) { epc.vloc[0] = -(epc.n = (pexpl->epicenter - epc.pt).normalized()); } epc.mass[0] = bbox.size.x * bbox.size.y + bbox.size.x * bbox.size.z + bbox.size.y * bbox.size.z; epc.vloc[0] *= kr / max(sqr(pexpl->rmin), (pexpl->epicenter - epc.pt).len2()); epc.vloc[0] *= epc.mass[0] * 100.0f; epc.mass[0] = 0.01f; epc.mass[1] = pents[nents]->GetMass(i); epc.partid[1] = pents[nents]->m_parts[i].id; epc.idmat[0] = -1; epc.normImpulse = epc.penetration = 0; epc.radius = pexpl->rmax; //pents[nents]->m_parts[i].flags |= geom_will_be_destroyed; OnEvent(pef_log_collisions, &epc); if (!bMultipart) { break; } } } } // multi-island loop end next_part:; } } } } if (pents[nents]->m_nParts == 0) { pents[nents]->ApplyVolumetricPressure(pexpl->epicenterImp, kr, pexpl->rmin); } m_pExplVictims[m_nExplVictims] = pents[nents]; m_pExplVictimsFrac[m_nExplVictims++] = sumV > 0 ? sumFrac / sumV : 1.0f; if (bEntChanged && pents[nents]->UpdateStructure(0.01f, pexpl, -1, gravity) || bMarkDeforming) { MarkEntityAsDeforming(pents[nents]); } } pexpl->pAffectedEnts = (IPhysicalEntity**)m_pExplVictims; pexpl->pAffectedEntsExposure = m_pExplVictimsFrac; pexpl->nAffectedEnts = m_nExplVictims; for (i = 0; i < nSkipEnts; i++) { if (pSkipPhysEnts[i]->m_flags & pef_traceable) { AtomicAdd(&((!pSkipPhysEnts[i]->m_pEntBuddy || IsPlaceholder(pSkipPhysEnts[i])) ? pSkipPhysEnts[i] : pSkipPhysEnts[i]->m_pEntBuddy)->m_bProcessed, -(1 << iCaller)); } else { CPhysicalPlaceholder* pPartPlaceholder; int numParts = pSkipPhysEnts[i]->m_nParts; for (int p = 0; p < numParts; p++) { if (pPartPlaceholder = pSkipPhysEnts[i]->m_parts[p].pPlaceholder) { AtomicAdd(&pPartPlaceholder->m_bProcessed, -(1 << iCaller)); } } } } } float CPhysicalWorld::CalculateExplosionExposure(pe_explosion* pexpl, IPhysicalEntity* pient) { if (pexpl->nOccRes <= 0) { return 1.0f; } if (pient->GetType() == PE_AREA) { return 0.0f; } CPhysicalEntity* pent = (CPhysicalEntity*)pient; WriteLock lockc(m_lockCaller[get_iCaller()]); ReadLock lock(pent->m_lockUpdate); int i; float sumV, sumFrac, frac; geom_world_data gwd; for (i = 0, sumFrac = sumV = 0.0f; i < pent->m_nParts; i++) { if (pent->m_parts[i].flags & geom_colltype_explosion) { gwd.R = Matrix33(pent->m_qrot * pent->m_parts[i].q); gwd.offset = pent->m_pos + pent->m_qrot * pent->m_parts[i].pos - pexpl->epicenter; gwd.scale = pent->m_parts[i].scale; frac = pent->m_parts[i].pPhysGeomProxy->pGeom->BuildOcclusionCubemap(&gwd, 1, &m_cubeMapStatic, &m_cubeMapDynamic, pexpl->nGrow); sumFrac += pent->m_parts[i].pPhysGeomProxy->V * frac; sumV += pent->m_parts[i].pPhysGeomProxy->V; } } return sumV > 0 ? sumFrac / sumV : 1.0f; } void CPhysicalWorld::ResetDynamicEntities() { int i; CPhysicalEntity* pent; pe_action_reset reset; reset.bClearContacts = 2; { WriteLock lock(m_lockStep); for (i = 1; i <= 4; i++) { for (pent = m_pTypedEnts[i]; pent; pent = pent->m_next) { pent->Action(&reset); } } } { WriteLock lock(m_lockAreas); for (CPhysArea* pArea = m_pGlobalArea; pArea; pArea = pArea->m_next) { pArea->Action(&reset); } } } void CPhysicalWorld::DestroyDynamicEntities() { int i; CPhysicalEntity* pent, * pent_next; m_nDynamicEntitiesDeleted = 0; for (i = 1; i <= 4; i++) { for (pent = m_pTypedEnts[i]; pent; pent = pent_next) { pent_next = pent->m_next; if (pent->m_pEntBuddy) { pent->m_pEntBuddy->m_pEntBuddy = 0; DestroyPhysicalEntity(pent->m_pEntBuddy); } else { SetPhysicalEntityId(pent, -1); } DetachEntityGridThunks(pent); for (int j = 0; j < pent->m_nParts; j++) { if (pent->m_parts[j].pPlaceholder) { DetachEntityGridThunks(pent->m_parts[j].pPlaceholder); } } if (pent->m_next = m_pTypedEnts[7]) { pent->m_next->m_prev = pent; } m_pTypedEnts[7] = pent; pent->m_iPrevSimClass = -1; pent->m_iSimClass = 7; m_nDynamicEntitiesDeleted++; } m_pTypedEnts[i] = m_pTypedEntsPerm[i] = 0; } m_nEnts -= m_nDynamicEntitiesDeleted; if (m_nEnts < m_nEntsAlloc - 8192 && !m_bEntityCountReserved) { int nEntsAlloc = m_nEntsAlloc; m_nEntsAlloc = (m_nEnts - 1 & ~8191) + 8192; m_nEntListAllocs++; ReallocateList(m_pTmpEntList, nEntsAlloc, m_nEntsAlloc); ReallocateList(m_pTmpEntList1, nEntsAlloc, m_nEntsAlloc); ReallocateList(m_pTmpEntList2, nEntsAlloc, m_nEntsAlloc); ReallocateList(m_pGroupMass, 0, m_nEntsAlloc); ReallocateList(m_pMassList, 0, m_nEntsAlloc); ReallocateList(m_pGroupIds, 0, m_nEntsAlloc); ReallocateList(m_pGroupNums, 0, m_nEntsAlloc); } } void CPhysicalWorld::PurgeDeletedEntities() { int i, j; { WriteLock lock1(m_lockQueue); for (i = 0; i < m_nQueueSlots; i++) { for (j = 0; *(int*)(m_pQueueSlots[i] + j) != -1; j += *(int*)(m_pQueueSlots[i] + j + sizeof(int))) { int* pCmdId = (int*)(m_pQueueSlots[i] + j); CPhysicalEntity* pent = *(CPhysicalEntity**)(m_pQueueSlots[i] + j + sizeof(int) * 2); if (*pCmdId != -2 && pent->m_iSimClass == 7) { *pCmdId = -2; } else if (*pCmdId == 5) { DestroyPhysicalEntity((IPhysicalEntity*)pent, *(int*)(m_pQueueSlots[i] + j + sizeof(int) * 2 + sizeof(void*)), 1); *pCmdId = -2; } } } } { WriteLock lock3(m_lockDeformingEntsList); for (i = j = 0; i < m_nDeformingEnts; i++) { if (m_pDeformingEnts[i]->m_iSimClass != 7) { m_pDeformingEnts[j++] = m_pDeformingEnts[i]; } else { m_pDeformingEnts[i]->m_flags &= ~pef_deforming; } } m_nDeformingEnts = j; } TracePendingRays(0); WriteLock lock(m_lockStep); CleanseEventsQueue(); CPhysicalEntity* pent, * pent_next; /*for(pent=m_pTypedEnts[7]; pent; pent=pent_next) { // purge deletion requests pent_next = pent->m_next; delete pent; } m_pTypedEnts[7] = 0;*/ for (pent = m_pTypedEnts[7]; pent; pent = pent_next) // purge deletion requests { pent_next = pent->m_next; if (pent->m_nRefCount <= 0) { if (pent->m_next) { pent->m_next->m_prev = pent->m_prev; } (pent->m_prev ? pent->m_prev->m_next : m_pTypedEnts[7]) = pent->m_next; pent->Delete(); } } } void CPhysicalWorld::DrawPhysicsHelperInformation(IPhysRenderer* pRenderer, int iCaller) { #ifndef _RELEASE int entype; CPhysicalEntity* pent = 0; (m_pRenderer = pRenderer)->SetOffset(m_vars.helperOffset); if (m_vars.iDrawHelpers) { assert(iCaller <= MAX_PHYS_THREADS); int i, n = 0, nEntListAllocs, nGEA; CPhysicalEntity** pEntList; { WriteLock lock0(m_lockCaller[iCaller]); if (m_pHeightfield[iCaller] && m_vars.iDrawHelpers & 128) { pRenderer->DrawGeometry(m_pHeightfield[iCaller]->m_parts[0].pPhysGeom->pGeom, 0, 0); } nEntListAllocs = m_nEntListAllocs; nGEA = m_nGEA[iCaller]; pEntList = iCaller ? m_pTmpEntList2 : m_pTmpEntList; { ReadLock lock(m_lockList); for (entype = 0; entype <= 6; entype++) { if (m_vars.iDrawHelpers & 0x100 << entype) { for (pent = m_pTypedEnts[entype]; pent && nEntListAllocs == m_nEntListAllocs && nGEA == m_nGEA[iCaller]; pent = pent->m_next) { pEntList[n++] = (CPhysicalEntity*)(EXPAND_PTR)pent->m_id; } } } if (pent) { return; } } } for (i = 0; i < n; i++) { int id = *(int*)(pEntList + i); if (nEntListAllocs != m_nEntListAllocs || nGEA != m_nGEA[iCaller]) { break; } if ((pent = (CPhysicalEntity*)GetPhysicalEntityById(id | 1 << 30)) && m_vars.iDrawHelpers & 0x80 << pent->m_iSimClass + 1) { pent->DrawHelperInformation(pRenderer, m_vars.iDrawHelpers); } } } if (m_vars.iDrawHelpers & 8192 && m_cubeMapStatic.N) { float xscale, /*xoffs,*/ z, maxlength = 0.7f, length; int i, ix, iy, cx, cy, cz, nOccRes = m_cubeMapStatic.N; Vec3 pt0, pt1, dir; xscale = 2.0f / nOccRes; // xoffs = 1.0f-xscale; for (i = 0; i < 6; i++) { cz = i >> 1; cx = inc_mod3[cz]; cy = dec_mod3[cz]; for (iy = 0; iy < nOccRes; iy++) { for (ix = 0; ix < nOccRes; ix++) { z = m_cubeMapStatic.ConvertToDistance(m_cubeMapStatic.grid[i][iy * nOccRes + ix]); if (z < m_cubeMapStatic.rmax) { pt0[cz] = z * ((i & 1) * 2 - 1); pt0[cx] = ((ix + 0.5f) * xscale - 1.0f) * z; pt0[cy] = ((iy + 0.5f) * xscale - 1.0f) * z; dir = pt0; length = pt0.len(); if (length > maxlength) { dir = pt0 * (maxlength / length); } pt0 += m_lastEpicenter; pRenderer->DrawLine(pt0 - dir, pt0, 7); pt0[cx] -= z * xscale * 0.5f; pt0[cy] -= z * xscale * 0.5f; pt1 = pt0; pt1[cx] += z * xscale; pt1[cy] += z * xscale; pRenderer->DrawLine(pt0, pt1, 7); pt0[cy] += z * xscale; pt1[cy] -= z * xscale; pRenderer->DrawLine(pt0, pt1, 7); } } } } m_cubeMapStatic.DebugDrawToScreen(60.f, 200.f, 120.f); } if (m_vars.iDrawHelpers & 32) { ReadLock lock(m_lockAreas); for (CPhysArea* pArea = m_pGlobalArea; pArea; pArea = pArea->m_next) { pArea->DrawHelperInformation(pRenderer, m_vars.iDrawHelpers); } } if (m_vars.iDrawHelpers & 32768 && m_pWaterMan && !m_pWaterMan->m_pArea) { m_pWaterMan->DrawHelpers(pRenderer); } if (m_vars.bLogActiveObjects) { ReadLock lock(m_lockList); m_vars.bLogActiveObjects = 0; int i, nPrims, nCount = 0; RigidBody* pbody; for (pent = m_pTypedEnts[2]; pent; pent = pent->m_next) { if (pent->GetMassInv() > 0) { for (i = nPrims = 0; i < pent->m_nParts; i++) { if (pent->m_parts[i].flags & geom_colltype0) { nPrims += ((CGeometry*)pent->m_parts[i].pPhysGeomProxy->pGeom)->GetPrimitiveCount(); } } pbody = pent->GetRigidBody(); ++nCount; CryLogAlways("%s @ %7.2f,%7.2f,%7.2f, mass %.2f, v %.1f, w %.1f, #polies %d, id %d", m_pRenderer ? m_pRenderer->GetForeignName(pent->m_pForeignData, pent->m_iForeignData, pent->m_iForeignFlags) : "", pent->m_pos.x, pent->m_pos.y, pent->m_pos.z, pbody->M, pbody->v.len(), pbody->w.len(), nPrims, pent->m_id); } } CryLogAlways("%d active object(s)", nCount); } #endif//_RELEASE } void CPhysicalWorld::DrawEntityHelperInformation(IPhysRenderer* pRenderer, int entityId, int iDrawHelpers) { #ifndef _RELEASE CPhysicalEntity* pent = (CPhysicalEntity*)GetPhysicalEntityById(entityId); if (pent) { pent->DrawHelperInformation(pRenderer, iDrawHelpers); } #endif//_RELEASE } int CPhysicalWorld::CollideEntityWithBeam(IPhysicalEntity* _pent, Vec3 org, Vec3 dir, float r, ray_hit* phit) { if (!_pent) { return 0; } FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); CPhysicalEntity* pent = (CPhysicalEntity*)_pent; WriteLock lockc(m_lockCaller[get_iCaller()]); ReadLock lock(pent->m_lockUpdate); CSphereGeom SweptSph; geom_contact* pcontacts; geom_world_data gwd[2]; sphere asph; asph.r = r; asph.center.zero(); SweptSph.CreateSphere(&asph); intersection_params ip; ip.bSweepTest = dir.len2() > 0; gwd[0].R.SetIdentity(); gwd[0].offset = org; gwd[0].v = dir; ip.time_interval = 1.0f; phit->dist = 1E10; for (int i = 0; i < pent->m_nParts; i++) { if (pent->m_parts[i].flags & geom_collides) { gwd[1].offset = pent->m_pos + pent->m_qrot * pent->m_parts[i].pos; gwd[1].R = Matrix33(pent->m_qrot * pent->m_parts[i].q); gwd[1].scale = pent->m_parts[i].scale; if (SweptSph.Intersect(pent->m_parts[i].pPhysGeom->pGeom, gwd, gwd + 1, &ip, pcontacts)) { if (pcontacts->t < phit->dist) { phit->dist = pcontacts->t; phit->pCollider = pent; phit->partid = pent->m_parts[phit->ipart = i].id; phit->surface_idx = pent->GetMatId(pcontacts->id[1], i); phit->idmatOrg = pcontacts->id[1] + (pent->m_parts[i].surface_idx + 1 & pcontacts->id[1] >> 31); phit->foreignIdx = pent->m_parts[i].pPhysGeom->pGeom->GetForeignIdx(pcontacts->iPrim[1]); phit->pt = pcontacts->pt; phit->n = -pcontacts->n; } } } } return isneg(phit->dist - 1E9f); } int CPhysicalWorld::CollideEntityWithPrimitive(IPhysicalEntity* _pent, int itype, primitive* pprim, Vec3 dir, ray_hit* phit, intersection_params* pip) { if (!_pent || ((CPhysicalPlaceholder*)_pent)->m_iSimClass == 5) { return 0; } FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); CPhysicalEntity* pent = (CPhysicalEntity*)_pent; int j; int iCaller = get_iCaller(); geom_contact* pcontacts; geom_world_data gwd[2]; CBoxGeom gbox; CCylinderGeom gcyl; CCapsuleGeom gcaps; CSphereGeom gsph; CGeometry* pgeom; gwd[0].R.SetIdentity(); gwd[0].offset.zero(); gwd[0].v = dir; phit->dist = 1E10; switch (itype) { case box::type: gwd[0].offset = ((box*)pprim)->center; ((box*)pprim)->center.zero(); gbox.CreateBox((box*)pprim); pgeom = &gbox; ((box*)pprim)->center = gwd[0].offset; break; case cylinder::type: gwd[0].offset = ((cylinder*)pprim)->center; ((cylinder*)pprim)->center.zero(); gcyl.CreateCylinder((cylinder*)pprim); pgeom = &gcyl; ((cylinder*)pprim)->center = gwd[0].offset; break; case capsule::type: gwd[0].offset = ((capsule*)pprim)->center; ((capsule*)pprim)->center.zero(); gcaps.CreateCapsule((capsule*)pprim); pgeom = &gcaps; ((capsule*)pprim)->center = gwd[0].offset; break; case sphere::type: gwd[0].offset = ((sphere*)pprim)->center; ((sphere*)pprim)->center.zero(); gsph.CreateSphere((sphere*)pprim); pgeom = &gsph; ((sphere*)pprim)->center = gwd[0].offset; break; default: return 0; } intersection_params ip; if (!pip) { ip.bSweepTest = dir.len2() > 0; ip.time_interval = 1.0f; pip = &ip; } Vec3 BBox[2], sz; box bbox; pgeom->GetBBox(&bbox); sz = bbox.size * bbox.Basis.Fabs(); BBox[0] = gwd[0].offset + bbox.center - sz; BBox[1] = gwd[0].offset + bbox.center + sz; for (int i = 0; i < 3; i++) { BBox[0][i] += min(0.0f, dir[i]), BBox[1][i] += max(0.0f, dir[i]); } if ((pent->m_BBox[1] - pent->m_BBox[0]).len2() >= 0.0f) { assert(iCaller <= MAX_PHYS_THREADS); // sca WriteLockCond lockc(m_lockCaller[iCaller], iCaller == MAX_PHYS_THREADS); ReadLock lockEnt(pent->m_lockUpdate); for (j = 0; j < pent->m_nParts; ++j) { if ((pent->m_parts[j].flags & geom_collides) && ((pent->m_parts[j].BBox[1] - pent->m_parts[j].BBox[0]).len2() == 0 || AABB_overlap(pent->m_parts[j].BBox, BBox))) { gwd[1].offset = pent->m_pos + pent->m_qrot * pent->m_parts[j].pos; gwd[1].R = Matrix33(pent->m_qrot * pent->m_parts[j].q); gwd[1].scale = pent->m_parts[j].scale; if (pgeom->Intersect(pent->m_parts[j].pPhysGeom->pGeom, gwd, gwd + 1, pip, pcontacts)) { if (pcontacts->t < phit->dist) { phit->dist = pcontacts->t; phit->pCollider = pent; phit->partid = pent->m_parts[phit->ipart = j].id; phit->surface_idx = pent->GetMatId(pcontacts->id[1], j); phit->idmatOrg = pcontacts->id[1] + (pent->m_parts[j].surface_idx + 1 & pcontacts->id[1] >> 31); phit->foreignIdx = pent->m_parts[j].pPhysGeom->pGeom->GetForeignIdx(pcontacts->iPrim[1]); phit->pt = pcontacts->pt; phit->n = -pcontacts->n; } } } } } return isneg(phit->dist - 1E9f); } static inline void swap(CPhysicalEntity** pentlist, int i1, int i2) { CPhysicalEntity* pent = pentlist[i1]; pentlist[i1] = pentlist[i2]; pentlist[i2] = pent; } static void qsort(CPhysicalEntity** pentlist, const Vec3& mask0, const Vec3& mask1, int ileft, int iright) { if (ileft >= iright) { return; } int i, ilast; swap(pentlist, ileft, ileft + iright >> 1); for (ilast = ileft, i = ileft + 1; i <= iright; i++) { if (pentlist[i]->m_BBox[0] * mask0 + pentlist[i]->m_BBox[1] * mask1 < pentlist[ileft]->m_BBox[0] * mask0 + pentlist[ileft]->m_BBox[1] * mask1) { swap(pentlist, ++ilast, i); } } swap(pentlist, ileft, ilast); qsort(pentlist, mask0, mask1, ileft, ilast - 1); qsort(pentlist, mask0, mask1, ilast + 1, iright); } // Wrapper to create a tri-mesh from one triangle static void CreateTriMesh(CTriMesh& gtrimesh, primitives::triangle* tri) { static const unsigned short indices[3] = {0, 1, 2}; strided_pointer<unsigned short> pIndices(const_cast<unsigned short*>(&indices[0])); strided_pointer<Vec3> pVerts(&tri->pt[0]); gtrimesh.CreateTriMesh(pVerts, pIndices, NULL, 0, 1, mesh_SingleBB | mesh_shared_vtx | /*mesh_shared_idx|*/ mesh_no_vtx_merge); } float CPhysicalWorld::PrimitiveWorldIntersection(const SPWIParams& pp, WriteLockCond* pLockContactsExp, const char* pNameTag) { int i, j, j1, ncont, nents, iActive = 0; int iCaller = get_iCaller(); Vec3 BBox[2], sz, mask[2] = { Vec3(ZERO), Vec3(ZERO) }; box bbox; CPhysicalEntity** pents; CBoxGeom gbox; CCylinderGeom gcyl; CCapsuleGeom gcaps; CSphereGeom gsph; CTriMesh gtri; CGeometry* pgeom; intersection_params ip; geom_world_data gwd[2]; geom_contact* pcontacts; static geom_contact contactsBest[MAX_PHYS_THREADS + 1]; geom_contact& contactBest = contactsBest[iCaller]; contactBest.t = 0; contactBest.pt.zero(); if (pp.entTypes & rwi_queue) { WriteLock lockQ(m_lockPwiQueue); if (pp.ppcontact || pp.pip) { return 0; } ReallocQueue(m_pwiQueue, m_pwiQueueSz, m_pwiQueueAlloc, m_pwiQueueHead, m_pwiQueueTail, 64); m_pwiQueue[m_pwiQueueHead].pprim = (primitives::primitive*)m_pwiQueue[m_pwiQueueHead].primbuf; switch (m_pwiQueue[m_pwiQueueHead].itype = pp.itype) { case box::type: *(box*)m_pwiQueue[m_pwiQueueHead].primbuf = *(box*)pp.pprim; break; case cylinder::type: *(cylinder*)m_pwiQueue[m_pwiQueueHead].primbuf = *(cylinder*)pp.pprim; break; case capsule::type: *(capsule*)m_pwiQueue[m_pwiQueueHead].primbuf = *(capsule*)pp.pprim; break; case sphere::type: *(sphere*)m_pwiQueue[m_pwiQueueHead].primbuf = *(sphere*)pp.pprim; break; case triangle::type: *(triangle*)m_pwiQueue[m_pwiQueueHead].primbuf = *(triangle*)pp.pprim; break; default: return 0; } m_pwiQueue[m_pwiQueueHead].sweepDir = pp.sweepDir; m_pwiQueue[m_pwiQueueHead].entTypes = pp.entTypes & ~rwi_queue; m_pwiQueue[m_pwiQueueHead].geomFlagsAll = pp.geomFlagsAll; m_pwiQueue[m_pwiQueueHead].geomFlagsAny = pp.geomFlagsAny; m_pwiQueue[m_pwiQueueHead].pForeignData = pp.pForeignData; m_pwiQueue[m_pwiQueueHead].iForeignData = pp.iForeignData; m_pwiQueue[m_pwiQueueHead].OnEvent = pp.OnEvent; m_pwiQueue[m_pwiQueueHead].nSkipEnts = min((int)(sizeof(m_pwiQueue[0].idSkipEnts) / sizeof(m_pwiQueue[0].idSkipEnts[0])), pp.nSkipEnts); for (i = 0; i < m_pwiQueue[m_pwiQueueHead].nSkipEnts; i++) { m_pwiQueue[m_pwiQueueHead].idSkipEnts[i] = pp.pSkipEnts[i] ? GetPhysicalEntityId(pp.pSkipEnts[i]) : -3; } m_pwiQueueSz++; return 1; } FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); PHYS_FUNC_PROFILER(pNameTag); WriteLockCond lock(m_lockCaller[iCaller]), & lockContacts = pLockContactsExp ? *pLockContactsExp : const_cast<SPWIParams&>(pp).lockContacts; if (pp.pip) { ip = *pp.pip; lockContacts.prw = &m_lockCaller[iCaller]; gwd[0].v = pp.sweepDir; } else if (pp.sweepDir.len2() > 0) { ip.bSweepTest = true; gwd[0].v = pp.sweepDir; ip.time_interval = 1.0f; contactBest.t = 1E10f; } else { ip.bStopAtFirstTri = true; ip.bNoBorder = true; ip.bNoAreaContacts = true; } if (pp.ppcontact) { *pp.ppcontact = 0; } switch (pp.itype) { case box::type: gwd[0].offset = ((box*)pp.pprim)->center; ((box*)pp.pprim)->center.zero(); gbox.CreateBox((box*)pp.pprim); pgeom = &gbox; ((box*)pp.pprim)->center = gwd[0].offset; break; case cylinder::type: gwd[0].offset = ((cylinder*)pp.pprim)->center; ((cylinder*)pp.pprim)->center.zero(); gcyl.CreateCylinder((cylinder*)pp.pprim); pgeom = &gcyl; ((cylinder*)pp.pprim)->center = gwd[0].offset; break; case capsule::type: gwd[0].offset = ((capsule*)pp.pprim)->center; ((capsule*)pp.pprim)->center.zero(); gcaps.CreateCapsule((capsule*)pp.pprim); pgeom = &gcaps; ((capsule*)pp.pprim)->center = gwd[0].offset; break; case sphere::type: gwd[0].offset = ((sphere*)pp.pprim)->center; ((sphere*)pp.pprim)->center.zero(); gsph.CreateSphere((sphere*)pp.pprim); pgeom = &gsph; ((sphere*)pp.pprim)->center = gwd[0].offset; break; case triangle::type: CreateTriMesh(gtri, (triangle*)pp.pprim); pgeom = >ri; break; default: return 0; } pgeom->GetBBox(&bbox); sz = bbox.size * bbox.Basis.Fabs(); BBox[0] = gwd[0].offset + bbox.center - sz; BBox[1] = gwd[0].offset + bbox.center + sz; for (i = 0; i < 3; i++) { BBox[0][i] += min(0.0f, pp.sweepDir[i]), BBox[1][i] += max(0.0f, pp.sweepDir[i]); } if (pp.nSkipEnts >= 0) { for (i = 0; i < pp.nSkipEnts; i++) { if (pp.pSkipEnts[i]) { if (!(((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_bProcessed >> iCaller & 1)) { AtomicAdd(&((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_bProcessed, 1 << iCaller); } if (((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_pEntBuddy && !(((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_pEntBuddy->m_bProcessed >> iCaller & 1)) { AtomicAdd(&((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_pEntBuddy->m_bProcessed, 1 << iCaller); } } } nents = GetEntitiesAround(BBox[0], BBox[1], pents, pp.entTypes, 0, 0, iCaller); } else { pents = (CPhysicalEntity**)pp.pSkipEnts; nents = -pp.nSkipEnts; for (i = 0; i < nents; i++) { if (pents[i]->m_flags & pef_parts_traceable) { AtomicAdd(&pents[i]->m_nUsedParts, (15 << iCaller * 4) - (pents[i]->m_nUsedParts & 15 << iCaller * 4)); } } } if (ip.bSweepTest && nents > 0) { i = idxmax3(pp.sweepDir.abs()); j = isneg(pp.sweepDir[i]); mask[j][i] = 1 - j * 2; qsort(pents, mask[0], mask[1], 0, nents - 1); contactBest.pt = pents[nents - 1]->m_BBox[j]; } for (i = 0; i < nents; i++) { if (!IgnoreCollision(pents[i]->m_collisionClass, pp.collclass)) { if (sz.x = (pents[i]->m_BBox[1] - pents[i]->m_BBox[0]).len2(), sz.x * (pents[i]->m_BBox[0] * mask[0] + pents[i]->m_BBox[1] * mask[1]) <= sz.x * (contactBest.pt * (mask[0] + mask[1]))) { ReadLock lockEnt(pents[i]->m_lockUpdate); for (j1 = 0; j1 < pents[i]->GetUsedPartsCount(iCaller); j1++) { if ((pents[i]->m_parts[j = pents[i]->GetUsedPart(iCaller, j1)].flags & pp.geomFlagsAll) == pp.geomFlagsAll && (pents[i]->m_parts[j].flags & pp.geomFlagsAny) && ((pents[i]->m_parts[j].BBox[1] - pents[i]->m_parts[j].BBox[0]).len2() == 0 || AABB_overlap(pents[i]->m_parts[j].BBox, BBox))) { gwd[1].offset = pents[i]->m_pos + pents[i]->m_qrot * pents[i]->m_parts[j].pos; gwd[1].R = Matrix33(pents[i]->m_qrot * pents[i]->m_parts[j].q); gwd[1].scale = pents[i]->m_parts[j].scale; if (ncont = pgeom->Intersect(pents[i]->m_parts[j].pPhysGeom->pGeom, gwd, gwd + 1, &ip, pcontacts)) { for (int ic = 0; ic < ncont; ic++) { pcontacts[ic].iNode[0] = pcontacts[ic].iPrim[1]; pcontacts[ic].iPrim[0] = pents[i]->m_id; pcontacts[ic].iPrim[1] = pents[i]->m_parts[j].id; pcontacts[ic].id[1] = pents[i]->GetMatId(pcontacts[ic].id[1], j); } if (ip.bStopAtFirstTri) { if (pp.ppcontact) { *pp.ppcontact = ip.pGlobalContacts; } contactBest.t = 1.f; goto Finished; } if (ip.bSweepTest) { if (pcontacts[0].t < contactBest.t) { contactBest = pcontacts[0]; } } else { ip.bKeepPrevContacts = true, contactBest.t += ncont; } } } } } } } if (m_vars.iDrawHelpers & 64 && m_pRenderer) { if (!ip.bSweepTest) { m_pRenderer->DrawGeometry(pgeom, gwd, 7, 1); } else if (contactBest.t < 1E10f) { m_pRenderer->DrawGeometry(pgeom, gwd, 7, 1, sz = gwd[0].v.normalized() * contactBest.t); gwd[0].offset += sz; m_pRenderer->DrawGeometry(pgeom, gwd, 7, 1, gwd[0].v - sz); } else { m_pRenderer->DrawGeometry(pgeom, gwd, 7, 1, gwd[0].v); } } if (pp.ppcontact) { *pp.ppcontact = ip.bSweepTest ? &contactBest : ip.pGlobalContacts; } Finished: if (pp.pip && !pp.pip->bThreadSafe && contactBest.t > 0.0f && contactBest.t < 1E10f) { lock.SetActive(0); lockContacts.SetActive(1); } for (i = 0; i < pp.nSkipEnts; i++) { if (pp.pSkipEnts[i]) { if (((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_bProcessed >> iCaller & 1) { AtomicAdd(&((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_bProcessed, -(1 << iCaller)); } if (((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_pEntBuddy && (((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_pEntBuddy->m_bProcessed >> iCaller & 1)) { AtomicAdd(&((CPhysicalPlaceholder**)pp.pSkipEnts)[i]->m_pEntBuddy->m_bProcessed, -(1 << iCaller)); } } } return contactBest.t < 1E10f ? contactBest.t : 0; } int CPhysicalWorld::RayTraceEntity(IPhysicalEntity* pient, Vec3 origin, Vec3 dir, ray_hit* pHit, pe_params_pos* pp, unsigned int geomFlagsAny /*=geom_colltype0|geom_colltype_player*/) { if (!(dir.len2() > 0 && origin.len2() >= 0)) { return 0; } int i, ncont; Vec3 BBox[2], sz; box bbox; WriteLock lock(m_lockCaller[get_iCaller()]); Vec3 pos; quaternionf qrot; float scale = 1.0f; CRayGeom aray(origin, dir); ray bray; geom_world_data gwd; geom_contact* pcontacts; intersection_params ip; pHit->dist = 1E10; if (((CPhysicalPlaceholder*)pient)->m_iSimClass != 5) { CPhysicalEntity* pent = ((CPhysicalPlaceholder*)pient)->GetEntity(); if (pp) { pos = pp->pos; qrot = pp->q; if (!is_unused(pp->scale)) { scale = pp->scale; } get_xqs_from_matrices(pp->pMtx3x4, pp->pMtx3x3, pos, qrot, scale); } else { pos = pent->m_pos; qrot = pent->m_qrot; } bray.dir = dir; bray.origin = origin; bbox.Basis.SetIdentity(); bbox.bOriented = 0; for (i = 0; i < pent->m_nParts; ++i) { const geom& part = pent->m_parts[i]; if (part.flags & geomFlagsAny) { const Vec3& ptmin = part.BBox[0]; const Vec3& ptmax = part.BBox[1]; if ((ptmax - ptmin).len2() != 0.0f) { bbox.center = (ptmin + ptmax) * 0.5f; bbox.size = (ptmax - ptmin) * 0.5f; if (!box_ray_overlap_check(&bbox, &bray)) { continue; } } gwd.R = Matrix33(qrot * part.q); gwd.offset = pos + qrot * part.pos; gwd.scale = scale * part.scale; ncont = part.pPhysGeom->pGeom->Intersect(&aray, &gwd, 0, &ip, pcontacts); for (; ncont > 0 && (pcontacts[ncont - 1].t > pHit->dist || pcontacts[ncont - 1].n * dir > 0); ncont--) { ; } if (ncont > 0) { pHit->dist = pcontacts[ncont - 1].t; pHit->pCollider = pent; pHit->partid = pent->m_parts[pHit->ipart = i].id; pHit->surface_idx = pent->GetMatId(pcontacts[ncont - 1].id[0], i); pHit->idmatOrg = pcontacts[ncont - 1].id[0] + (part.surface_idx + 1 & pcontacts[ncont - 1].id[0] >> 31); pHit->foreignIdx = part.pPhysGeom->pGeom->GetForeignIdx(pcontacts[ncont - 1].iPrim[0]); pHit->pt = pcontacts[ncont - 1].pt; pHit->n = pcontacts[ncont - 1].n; } } } } else { return ((CPhysArea*)pient)->RayTrace(origin, dir, pHit, pp); } return isneg(pHit->dist - 1E9); } CPhysicalEntity* CPhysicalWorld::CheckColliderListsIntegrity() { int i, j, k; CPhysicalEntity* pent; for (i = 1; i <= 2; i++) { for (pent = m_pTypedEnts[i]; pent; pent = pent->m_next) { for (j = 0; j < pent->m_nColliders; j++) { if (pent->m_pColliders[j]->m_iSimClass > 0) { for (k = 0; k < pent->m_pColliders[j]->m_nColliders && pent->m_pColliders[j]->m_pColliders[k] != pent; k++) { ; } if (k == pent->m_pColliders[j]->m_nColliders) { return pent; } } } } } return 0; } void CPhysicalWorld::GetMemoryStatistics(ICrySizer* pSizer) { static const char* entnames[] = { "static entities", "physical entities", "physical entities", "living entities", "detached entities", "areas", "triggers", "deleted entities" }; int i, j, n; CPhysicalEntity* pent; /*#ifdef WIN32 static char *sec_ids[] = { ".text",".textbss",".data",".idata" }; static char *sec_names[] = { "code section","code section","data section","data section" }; _IMAGE_DOS_HEADER *pMZ = (_IMAGE_DOS_HEADER*)GetModuleHandle("CryPhysics.dll"); _IMAGE_NT_HEADERS *pPE = (_IMAGE_NT_HEADERS*)((char*)pMZ+pMZ->e_lfanew); IMAGE_SECTION_HEADER *sections = IMAGE_FIRST_SECTION(pPE); for(i=0;i<pPE->FileHeader.NumberOfSections;i++) for(j=0;j<sizeof(sec_ids)/sizeof(sec_ids[0]);j++) if (!strncmp((char*)sections[i].Name, sec_ids[j], min(8,strlen(sec_ids[j])+1))) { SIZER_COMPONENT_NAME(pSizer, sec_names[j]); pSizer->AddObject((void*)sections[i].VirtualAddress, sections[i].Misc.VirtualSize); } #endif*/ { SIZER_COMPONENT_NAME(pSizer, "world structures"); pSizer->AddObject(this, sizeof(CPhysicalWorld)); pSizer->AddObject(m_pTmpEntList, m_nEntsAlloc * sizeof(m_pTmpEntList[0])); pSizer->AddObject(m_pTmpEntList1, m_nEntsAlloc * sizeof(m_pTmpEntList1[0])); pSizer->AddObject(m_pTmpEntList2, m_nEntsAlloc * sizeof(m_pTmpEntList2[0])); pSizer->AddObject(m_pGroupMass, m_nEntsAlloc * sizeof(m_pGroupMass[0])); pSizer->AddObject(m_pMassList, m_nEntsAlloc * sizeof(m_pMassList[0])); pSizer->AddObject(m_pGroupIds, m_nEntsAlloc * sizeof(m_pGroupIds[0])); pSizer->AddObject(m_pGroupNums, m_nEntsAlloc * sizeof(m_pGroupNums[0])); pSizer->AddObject(m_pEntsById, m_nIdsAlloc * sizeof(m_pEntsById[0])); pSizer->AddObject(&m_pEntGrid, GetGridSize(m_pEntGrid, m_entgrid.size)); pSizer->AddObject(m_gthunks, m_thunkPoolSz * sizeof(m_gthunks[0])); m_cubeMapStatic.GetMemoryStatistics(pSizer); m_cubeMapDynamic.GetMemoryStatistics(pSizer); pSizer->AddObject(m_pExplVictims, m_nExplVictimsAlloc * (sizeof(m_pExplVictims[0]) + sizeof(m_pExplVictimsFrac[0]) + sizeof(m_pExplVictimsImp[0]))); pSizer->AddObject(m_pFreeContact, m_nContactsAlloc * sizeof(m_pFreeContact[0])); pSizer->AddObject(m_pFreeEntPart, m_nEntPartsAlloc * sizeof(m_pFreeEntPart[0])); if (m_bHasPODGrid) { for (i = j = 0; i < (m_entgrid.size.x * m_entgrid.size.y >> (3 + m_log2PODscale) * 2); j += m_pPODCells[i++] != 0) { ; } pSizer->AddObject(m_pPODCells, j * sizeof(pe_PODcell) * 64 + (m_entgrid.size.x * m_entgrid.size.y * sizeof(m_pPODCells[0]) >> (3 + m_log2PODscale) * 2)); } } { SIZER_COMPONENT_NAME(pSizer, "world queues"); EventChunk* pChunk; for (n = 0, pChunk = m_pFirstEventChunk; pChunk; pChunk = pChunk->next, n++) { ; } pSizer->AddObject(pChunk, n * EVENT_CHUNK_SZ); pSizer->AddObject(m_pQueueSlots, m_nQueueSlots * (sizeof(int*) + QUEUE_SLOT_SZ)); pSizer->AddObject(m_pQueueSlotsAux, m_nQueueSlotsAux * (sizeof(int*) + QUEUE_SLOT_SZ)); pSizer->AddObject(m_rwiQueue, m_rwiQueueAlloc * sizeof(m_rwiQueue[0])); pSizer->AddObject(m_pwiQueue, m_pwiQueueAlloc * sizeof(m_pwiQueue[0])); pSizer->AddObject(m_pRwiHitsHead, m_rwiHitsPoolSize * sizeof(ray_hit)); } void GetRBMemStats(ICrySizer * pSizer); GetRBMemStats(pSizer); { SIZER_COMPONENT_NAME(pSizer, "placeholders"); pSizer->AddObject(m_pPlaceholders, m_nPlaceholderChunks * (sizeof(CPhysicalPlaceholder) * PLACEHOLDER_CHUNK_SZ + sizeof(CPhysicalPlaceholder*))); pSizer->AddObject(m_pPlaceholderMap, ((size_t)m_nPlaceholderChunks << PLACEHOLDER_CHUNK_SZLG2 - 5) * sizeof(int)); } { SIZER_COMPONENT_NAME(pSizer, "entities"); for (i = 0; i <= 7; i++) { SIZER_COMPONENT_NAME(pSizer, entnames[i]); for (pent = m_pTypedEnts[i]; pent; pent = pent->m_next) { pent->GetMemoryStatistics(pSizer); } } { SIZER_COMPONENT_NAME(pSizer, "hidden entities"); for (pent = m_pHiddenEnts; pent; pent = pent->m_next) { pent->GetMemoryStatistics(pSizer); } } } { SIZER_COMPONENT_NAME(pSizer, "areas"); for (CPhysArea* pArea = m_pGlobalArea; pArea; pArea = pArea->m_next) { pArea->GetMemoryStatistics(pSizer); } } { SIZER_COMPONENT_NAME(pSizer, "geometries"); if (m_pHeightfield[0]) { m_pHeightfield[0]->m_parts[0].pPhysGeom->pGeom->GetMemoryStatistics(pSizer); } if (m_pHeightfield[1]) { m_pHeightfield[1]->m_parts[0].pPhysGeom->pGeom->GetMemoryStatistics(pSizer); } pSizer->AddObject(m_pGeoms, m_nGeomChunks * sizeof(m_pGeoms[0])); for (i = 0; i < m_nGeomChunks; i++) { n = GEOM_CHUNK_SZ & i - m_nGeomChunks + 1 >> 31 | m_nGeomsInLastChunk & m_nGeomChunks - 2 - i >> 31; pSizer->AddObject(m_pGeoms[i], n * sizeof(m_pGeoms[i][0])); for (j = 0; j < n; j++) { if (m_pGeoms[i][j].pGeom) { m_pGeoms[i][j].pGeom->GetMemoryStatistics(pSizer); } } } } { SIZER_COMPONENT_NAME(pSizer, "external geometries"); pSizer->AddObject(&m_sizeExtGeoms, m_sizeExtGeoms); } { SIZER_COMPONENT_NAME(pSizer, "Static TriMesh Data"); TriMeshStaticData::GetMemoryUsage(pSizer); } } void CPhysicalWorld::AddEntityProfileInfo(CPhysicalEntity* pent, int nTicks) { if (m_vars.bProfileGroups) { m_grpProfileData[GetEntityProfileType(pent)].nTicks += nTicks; } if (m_nProfiledEnts == sizeof(m_pEntProfileData) / sizeof(m_pEntProfileData[0]) && nTicks <= m_pEntProfileData[m_nProfiledEnts - 1].nTicksStep || m_vars.bSingleStepMode) { return; } int i; WriteLock lock(m_lockEntProfiler); phys_profile_info ppi; for (i = 0; i < m_nProfiledEnts && m_pEntProfileData[i].pEntity != pent; i++) { ; } if (i == m_nProfiledEnts) { ppi.pEntity = pent; ppi.nTicks = ppi.nTicksStep = nTicks; ppi.nCalls = 1; ppi.nTicksPeak = ppi.nCallsPeak = ppi.nTicksAvg = ppi.peakAge = ppi.nTicksLast = 0; ppi.nCallsAvg = ppi.nCallsLast = 0; ppi.id = pent->m_id; ppi.pName = m_pRenderer ? m_pRenderer->GetForeignName(pent->m_pForeignData, pent->m_iForeignData, pent->m_iForeignFlags) : "noname"; } else { ppi = m_pEntProfileData[i]; ppi.nTicksStep &= -ppi.nTicks >> 31; ppi.nTicks += nTicks; ppi.nCalls++; nTicks = (ppi.nTicksStep = max(ppi.nTicksStep, nTicks)); memmove(m_pEntProfileData + i, m_pEntProfileData + i + 1, (--m_nProfiledEnts - i) * sizeof(m_pEntProfileData[0])); } int iBound[2] = { -1, m_nProfiledEnts }; do { i = iBound[0] + iBound[1] >> 1; PREFAST_ASSUME(i > 0 && i < sizeof(m_pEntProfileData) / sizeof(m_pEntProfileData[0])); iBound[isneg(m_pEntProfileData[i].nTicksStep - nTicks)] = i; } while (iBound[1] > iBound[0] + 1); m_nProfiledEnts = min(m_nProfiledEnts + 1, (int)(sizeof(m_pEntProfileData) / sizeof(m_pEntProfileData[0]))); if ((i = iBound[0] + 1) < m_nProfiledEnts) { memmove(m_pEntProfileData + i + 1, m_pEntProfileData + i, (m_nProfiledEnts - 1 - i) * sizeof(m_pEntProfileData[0])); m_pEntProfileData[i] = ppi; } } void CPhysicalWorld::AddFuncProfileInfo(const char* name, int nTicks) { WriteLock lock(m_lockFuncProfiler); int i, iBound[2] = { -1, m_nProfileFunx }; if (m_nProfileFunx) { do { i = iBound[0] + iBound[1] >> 1; iBound[isneg((int)(name - m_pFuncProfileData[i].pName))] = i; } while (iBound[1] > iBound[0] + 1); } if ((i = iBound[0]) < 0 || m_pFuncProfileData[i].pName != name) { ++i; if (m_nProfileFunx == m_nProfileFunxAlloc) { if (m_nProfileFunx >= 64) { return; } ReallocateList(m_pFuncProfileData, m_nProfileFunx, m_nProfileFunxAlloc += 16); } memmove(m_pFuncProfileData + i + 1, m_pFuncProfileData + i, sizeof(phys_profile_info) * (m_nProfileFunx - i)); m_pFuncProfileData[i].nTicks = m_pFuncProfileData[i].nTicksPeak = m_pFuncProfileData[i].peakAge = 0; m_pFuncProfileData[i].nCalls = m_pFuncProfileData[i].nCallsPeak = 0; m_pFuncProfileData[i].pName = name; m_nProfileFunx++; } m_pFuncProfileData[i].nTicks += nTicks; m_pFuncProfileData[i].nCalls++; m_pFuncProfileData[i].id = 0; } void CPhysicalWorld::AddEventClient(int type, int (* func)(const EventPhys*), int bLogged, float priority) { AZ_Assert(type < EVENT_TYPES_NUM, "Expected type wihin limits"); AZ_Assert(bLogged == 0 || bLogged == 1, "Expected bLogged 1 or 0"); RemoveEventClient(type, func, bLogged); WriteLock lock(m_lockEventClients); EventClient* pSlot = new EventClient, * pCurSlot, * pSlot0 = m_pEventClients[type][bLogged]; memset(pSlot, 0, sizeof(EventClient)); pSlot->priority = priority; pSlot->OnEvent = func; if (pSlot0 && pSlot0->priority > priority) { for (pCurSlot = m_pEventClients[type][bLogged]; pCurSlot->next && pCurSlot->next->priority > priority; pCurSlot = pCurSlot->next) { ; } pSlot->next = pCurSlot->next; pCurSlot->next = pSlot; } else { (m_pEventClients[type][bLogged] = pSlot)->next = pSlot0; } } int CPhysicalWorld::RemoveEventClient(int type, int (* func)(const EventPhys*), int bLogged) { AZ_Assert(type < EVENT_TYPES_NUM, "Expected type wihin limits"); AZ_Assert(bLogged == 0 || bLogged == 1, "Expected bLogged 1 or 0"); WriteLock lock(m_lockEventClients); EventClient* pSlot = m_pEventClients[type][bLogged]; if (!pSlot) { return 0; } if (pSlot->OnEvent == func) { m_pEventClients[type][bLogged] = pSlot->next; delete pSlot; return 1; } for (; pSlot->next && pSlot->next->OnEvent != func; pSlot = pSlot->next) { ; } if (pSlot->next) { EventClient* pDelSlot = pSlot->next; pSlot->next = pSlot->next->next; delete pDelSlot; return 1; } return 0; } EventPhys* CPhysicalWorld::AllocEvent(int id, int sz) { if (m_pFreeEvents[id] == 0) { if (m_szCurEventChunk + sz > EVENT_CHUNK_SZ) { EventChunk* pNewChunk = (EventChunk*)(new char[sizeof(EventChunk) + max(sz, EVENT_CHUNK_SZ)]); pNewChunk->next = 0; m_pCurEventChunk->next = pNewChunk; m_pCurEventChunk = pNewChunk; m_szCurEventChunk = 0; } m_pFreeEvents[id] = (EventPhys*)((char*)(m_pCurEventChunk + 1) + m_szCurEventChunk); m_szCurEventChunk += sz; m_pFreeEvents[id]->idval = id; m_pFreeEvents[id]->next = 0; } EventPhys* pSlot = m_pFreeEvents[id]; m_pFreeEvents[id] = m_pFreeEvents[id]->next; m_nEvents[id]++; return pSlot; } uint32 CPhysicalWorld::GetPumpLoggedEventsTicks() { uint32 ticks = m_nPumpLoggedEventsHits; m_nPumpLoggedEventsHits = 0; return ticks; } void CPhysicalWorld::PumpLoggedEvents() { FUNCTION_PROFILER(GetISystem(), PROFILE_PHYSICS); #ifdef ENABLE_LW_PROFILERS //simple timer shown in r_DisplayInfo=3 LARGE_INTEGER pumpStart, pumpEnd; QueryPerformanceCounter(&pumpStart); uint64 startYields = 0ULL; #endif EventPhys* pEventFirst, * pEvent, * pEventLast, * pEvent_next; ray_hit* pLastPoolHit = 0; int lastPoolHitBlockSize; { WriteLock lock(m_lockEventsQueue); pEventFirst = m_pEventFirst; m_pEventFirst = m_pEventLast = 0; for (int i = 0; i < EVENT_TYPES_NUM; i++) { m_nEvents[i] = 0; } m_iLastLogPump++; } unsigned int bNotZeroStep = m_vars.lastTimeStep == 0.f ? 0 : (unsigned int)-1; EventClient* pClient; for (pEvent = pEventFirst; pEvent; pEvent = pEvent->next) { if (!(pEvent->idval <= EventPhysCollision::id && (((CPhysicalEntity*)((EventPhysStereo*)pEvent)->pEntity[0])->m_iDeletionTime | ((CPhysicalEntity*)((EventPhysStereo*)pEvent)->pEntity[1])->m_iDeletionTime) || pEvent->idval > EventPhysCollision::id && ((CPhysicalEntity*)((EventPhysMono*)pEvent)->pEntity)->m_iDeletionTime)) { if (pEvent->idval == EventPhysPostStep::id) { EventPhysPostStep* pepps = (EventPhysPostStep*)pEvent; if (iszero(pepps->idStep - m_idStep) & bNotZeroStep) // Dont emit logs past the current frame, unless timestep is zero { break; } else if (pepps->idStep < m_idStep - 1 && ((CPhysicalPlaceholder*)pepps->pEntity)->m_iSimClass > 1 + m_vars.bSingleStepMode * 8 || ((CPhysicalPlaceholder*)pepps->pEntity)->m_bProcessed & PENT_SETPOSED || ((CPhysicalEntity*)pepps->pEntity)->m_iDeletionTime) { continue; } } int bRWIorPWI = iszero(pEvent->idval - EventPhysRWIResult::id) + iszero(pEvent->idval - EventPhysPWIResult::id); if (bRWIorPWI && ((EventPhysRWIResult*)pEvent)->OnEvent) { ((EventPhysRWIResult*)pEvent)->OnEvent((EventPhysRWIResult*)pEvent); } else { for (pClient = m_pEventClients[pEvent->idval][1]; pClient; pClient = pClient->next) { BLOCK_PROFILER(pClient->ticks); int bContinue = pClient->OnEvent(pEvent); bContinue += bRWIorPWI; if (!bContinue) { break; } } } if (pEvent->idval == EventPhysRWIResult::id && ((EventPhysRWIResult*)pEvent)->bHitsFromPool) { pLastPoolHit = ((EventPhysRWIResult*)pEvent)->pHits + (lastPoolHitBlockSize = ((EventPhysRWIResult*)pEvent)->nMaxHits) - 1; } } } pEventLast = pEvent; for (pClient = m_pEventClients[EventPhysPostPump::id][1]; pClient; pClient = pClient->next) { BLOCK_PROFILER(pClient->ticks); int bContinue = pClient->OnEvent(NULL); } #ifndef PHYS_FUNC_PROFILER_DISABLED int iEvent, iClient; for (iEvent = 0; iEvent < EVENT_TYPES_NUM; iEvent++) { for (pClient = m_pEventClients[iEvent][1], iClient = 0; pClient; pClient = pClient->next, iClient++) { if (pClient->ticks * 300 > m_vars.ticksPerSecond) { sprintf_s(pClient->tag, 32, "PhysEventHandler(%d,%d)", iEvent, iClient); AddFuncProfileInfo(pClient->tag, pClient->ticks); } pClient->ticks = 0; } } #endif { WriteLock lock(m_lockEventsQueue); for (pEvent = pEventFirst; pEvent != pEventLast; pEvent = pEvent_next) { pEvent_next = pEvent->next; pEvent->next = m_pFreeEvents[pEvent->idval]; m_pFreeEvents[pEvent->idval] = pEvent; } if (pEventLast) { CPhysicalEntity* pent; for (pEvent = pEventLast; pEvent; pEvent = pEvent->next) { if (pEvent->idval == EventPhysRWIResult::id) { EventPhysRWIResult* pEventRWI = (EventPhysRWIResult*)pEvent; int i, bNoSolidHits = isneg(pEventRWI->pHits[0].dist); for (i = 0; i < pEventRWI->nHits; i++) { if (pent = (CPhysicalEntity*)pEventRWI->pHits[i + bNoSolidHits].pCollider) { pent->m_iDeletionTime += isneg(3 - pent->m_iDeletionTime); } } } else if (pEvent->idval > EventPhysCollision::id) { pent = (CPhysicalEntity*)((EventPhysMono*)pEvent)->pEntity; pent->m_iDeletionTime += isneg(3 - pent->m_iDeletionTime); // only increase for "real" deletion times (>3) } else { pent = (CPhysicalEntity*)((EventPhysStereo*)pEvent)->pEntity[0]; pent->m_iDeletionTime += isneg(3 - pent->m_iDeletionTime); pent = (CPhysicalEntity*)((EventPhysStereo*)pEvent)->pEntity[1]; pent->m_iDeletionTime += isneg(3 - pent->m_iDeletionTime); } } for (pEvent = pEventLast; pEvent->next; pEvent = pEvent->next) { ; } pEvent->next = m_pEventFirst; m_pEventFirst = pEventLast; if (!m_pEventLast) { m_pEventLast = pEvent; } //(m_pEventLast ? m_pEventLast->next : m_pEventFirst) = pEventLast; //for(m_pEventLast=pEventLast; m_pEventLast->next; m_pEventLast=m_pEventLast->next); } } if (pLastPoolHit) { WriteLock lockH(m_lockRwiHitsPool); int i; for (i = 1; i < lastPoolHitBlockSize && pLastPoolHit[-i].next == pLastPoolHit - i + 1; i++) { ; } if (i < lastPoolHitBlockSize)// || pLastPoolHit->next->next!=pLastPoolHit->next+1) { CryLog("Error: queued RWI hits pool corrupted"); } else { m_pRwiHitsHead = pLastPoolHit->next; m_rwiPoolEmpty = m_pRwiHitsTail->next == m_pRwiHitsHead; } } { WriteLock lockfp(m_lockFuncProfiler); int i, j; for (i = j = 0; i < m_nProfileFunx; i++) { if (++m_pFuncProfileData[i].id < 20) { if (i != j) { m_pFuncProfileData[j] = m_pFuncProfileData[i]; } ++j; } } m_nProfileFunx = j; } #ifdef ENABLE_LW_PROFILERS QueryPerformanceCounter(&pumpEnd); uint64 endYields = 0ULL; m_nPumpLoggedEventsHits += (uint32)(pumpEnd.QuadPart - pumpStart.QuadPart) - (uint32)(endYields - startYields); #endif } void CPhysicalWorld::ClearLoggedEvents() { EventPhys* pEvent, * pEvent_next; WriteLock lock(m_lockEventsQueue); for (pEvent = m_pEventFirst; pEvent; pEvent = pEvent_next) { pEvent_next = pEvent->next; pEvent->next = m_pFreeEvents[pEvent->idval]; m_pFreeEvents[pEvent->idval] = pEvent; } m_pEventFirst = m_pEventLast = 0; m_iLastLogPump = -1; for (CPhysicalEntity* pent = m_pTypedEnts[7]; pent; pent = pent->m_next) { pent->m_iDeletionTime = min(4, pent->m_iDeletionTime); } m_rwiPoolEmpty = 1; m_pRwiHitsHead = m_pRwiHitsTail->next; } #ifdef PHYSWORLD_SERIALIZATION void SerializeGeometries(CPhysicalWorld* pWorld, const char* fname, int bSave); void SerializeWorld(CPhysicalWorld* pWorld, const char* fname, int bSave); int CPhysicalWorld::SerializeWorld(const char* fname, int bSave) { ::SerializeWorld(this, fname, bSave); return 1; } int CPhysicalWorld::SerializeGeometries(const char* fname, int bSave) { ::SerializeGeometries(this, fname, bSave); return 1; } #else int CPhysicalWorld::SerializeWorld(const char* fname, int bSave) { return 0; } int CPhysicalWorld::SerializeGeometries(const char* fname, int bSave) { return 0; } #endif int CPhysicalWorld::AddExplosionShape(IGeometry* pGeom, float size, int idmat, float probability) { int i, j, bCreateConstraint = idmat >> 16 & 1; idmat &= 0xFFFF; for (i = 0; i < m_nExpl; i++) { if (m_pExpl[i].pGeom == pGeom && m_pExpl[i].idmat == idmat) { return -1; } } if (m_nExpl == m_nExplAlloc) { ReallocateList(m_pExpl, m_nExpl, m_nExplAlloc += 16); } for (i = 0; i < m_nExpl && m_pExpl[i].idmat <= idmat; i++) { ; } memmove(m_pExpl + i + 1, m_pExpl + i, (m_nExpl - i) * sizeof(m_pExpl[0])); for (int explIdx = i + 1; explIdx <= m_nExpl; ++explIdx) { ++m_pExpl[explIdx].iFirstByMat; } if (i > 0 && m_pExpl[i - 1].idmat == idmat) { m_pExpl[i].iFirstByMat = m_pExpl[i - 1].iFirstByMat; m_pExpl[i].nSameMat = m_pExpl[i - 1].nSameMat + 1; int jc = m_pExpl[i].iFirstByMat + m_pExpl[i - 1].nSameMat; for (j = m_pExpl[i].iFirstByMat; j < jc; j++) { m_pExpl[j].nSameMat++; } } else { m_pExpl[i].iFirstByMat = i; m_pExpl[i].nSameMat = 1; } if (pGeom->GetType() == GEOM_TRIMESH) { mesh_data* pmd = (mesh_data*)pGeom->GetData(); memset(pmd->pMats, 100, pmd->nTris); } m_pExpl[i].id = m_idExpl++; (m_pExpl[i].pGeom = pGeom)->AddRef(); m_pExpl[i].size = size; m_pExpl[i].rsize = 1 / size; m_pExpl[i].idmat = idmat; m_pExpl[i].probability = probability; m_pExpl[i].bCreateConstraint = bCreateConstraint; m_nExpl++; return m_pExpl[i].id; } void CPhysicalWorld::RemoveExplosionShape(int id) { int i, j; for (i = 0; i < m_nExpl && m_pExpl[i].id != id; i++) { ; } if (i == m_nExpl) { return; } m_pExpl[i].pGeom->Release(); for (j = m_pExpl[i].iFirstByMat; j < m_pExpl[i].iFirstByMat + m_pExpl[i].nSameMat; j++) { m_pExpl[j].nSameMat--; } for (; j < m_nExpl; j++) { m_pExpl[j].iFirstByMat--; } memmove(m_pExpl + i, m_pExpl + i + 1, (m_nExpl - 1 - i) * sizeof(m_pExpl[0])); m_nExpl--; } // disable overflow warning #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winteger-overflow" #endif #if defined(__GNUC__) #if __GNUC__ >= 4 && __GNUC__MINOR__ < 7 #pragma GCC diagnostic ignored "-Woverflow" #else #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverflow" #endif #endif IGeometry* CPhysicalWorld::GetExplosionShape(float size, int idmat, float& scale, int& bCreateConstraint) { int i, j, mask, ibound[2] = { -1, m_nExpl }; float sum, probabilitySum, f; if (!m_nExpl || size <= 0) { return 0; } idmat &= 127; do { i = ibound[0] + ibound[1] >> 1; ibound[isneg(idmat - m_pExpl[i].idmat)] = i; } while (ibound[1] > ibound[0] + 1); if (ibound[0] < 0 || m_pExpl[ibound[0]].idmat != idmat) { return 0; } ibound[1] = m_pExpl[ibound[0]].iFirstByMat + m_pExpl[ibound[0]].nSameMat; j = ibound[0] = m_pExpl[ibound[0]].iFirstByMat; for (i = j + 1; i < ibound[1]; i++) { mask = -isneg(fabs_tpl(m_pExpl[i].size - size) - fabs_tpl(m_pExpl[j].size - size)); j = i & mask | j & ~mask; } for (i = ibound[0], probabilitySum = 0.0f; i < ibound[1]; i++) { probabilitySum += m_pExpl[i].probability * iszero(m_pExpl[i].size - m_pExpl[j].size); } f = cry_random(0.0f, probabilitySum); for (i = ibound[0], sum = 0; i < ibound[1] && sum < f; i++) { sum += m_pExpl[i].probability * iszero(m_pExpl[i].size - m_pExpl[j].size); } scale = size * m_pExpl[i - 1].rsize; bCreateConstraint = m_pExpl[i - 1].bCreateConstraint; return m_pExpl[i - 1].pGeom; } #if defined(__clang__) #pragma clang diagnostic pop #endif #if defined(__GNUC__) #if __GNUC__ >= 4 && __GNUC__MINOR__ < 7 #pragma GCC diagnostic error "-Woverflow" #else #pragma GCC diagnostic pop #endif #endif int CPhysicalWorld::SetWaterManagerParams(pe_params* params) { if (params->type == pe_params_waterman::type_id && !is_unused(((pe_params_waterman*)params)->posViewer)) { m_posViewer = ((pe_params_waterman*)params)->posViewer; } WriteLock lock(m_lockWaterMan); if (!m_pWaterMan || m_pWaterMan->m_pArea) { if ((params->type != pe_params_waterman::type_id) || is_unused(((pe_params_waterman*)params)->nExtraTiles)) { return 0; } CWaterMan* pWaterMan = m_pWaterMan; (m_pWaterMan = new CWaterMan(this))->m_next = pWaterMan; if (pWaterMan) { pWaterMan->m_prev = m_pWaterMan; } } return m_pWaterMan->SetParams(params); } int CPhysicalWorld::GetWaterManagerParams(pe_params* params) { ReadLock lock(m_lockWaterMan); return m_pWaterMan && !m_pWaterMan->m_pArea ? m_pWaterMan->GetParams(params) : 0; } int CPhysicalWorld::GetWatermanStatus(pe_status* status) { ReadLock lock(m_lockWaterMan); return m_pWaterMan && !m_pWaterMan->m_pArea ? m_pWaterMan->GetStatus(status) : 0; } void CPhysicalWorld::DestroyWaterManager() { WriteLock lock(m_lockWaterMan); if (CWaterMan* pWaterMan = m_pWaterMan) { m_pWaterMan = pWaterMan->m_next; delete pWaterMan; } } void CPhysicalWorld::GetEntityMassAndCom(IPhysicalEntity* pIEnt, float& mass, Vec3& com) { CPhysicalEntity* pent = (CPhysicalEntity*) pIEnt; // need to check this is CPhysicalEntity int i, j; for (j = i = 0, mass = 0; i < pent->m_nParts; i++) { j += i - j & - isneg(pent->m_parts[i].mass - pent->m_parts[j].mass); mass += pent->m_parts[i].mass; } //if (pent->m_nParts) // com = pent->m_pos+pent->m_qrot*(pent->m_parts[j].pos+pent->m_parts[j].q*pent->m_parts[j].pPhysGeomProxy->origin*pent->m_parts[j].scale); //else com.zero(); com = pent->m_BBox[0] + (pent->m_BBox[1] - pent->m_BBox[0]) / 2.0f; } void CPhysicalWorld::SavePhysicalEntityPtr(TSerialize ser, IPhysicalEntity* pIEnt) { CPhysicalEntity* pent = (CPhysicalEntity*) pIEnt; // need to check this is CPhysicalEntity ser.BeginGroup("entity_ptr"); int i; float mass; Vec3 com; ser.Value("id", i = pent->m_id); ser.Value("simclass", i = pent->m_iSimClass); GetEntityMassAndCom(pent, mass, com); ser.Value("com", com); ser.Value("mass", mass); ser.EndGroup(); } IPhysicalEntity* CPhysicalWorld::LoadPhysicalEntityPtr(TSerialize ser) { int i, iSimClass, iSimClass1; CPhysicalEntity* pent, ** pents; float mass, mass1; Vec3 com, com1; ser.BeginGroup("entity_ptr"); ser.Value("id", i); ser.Value("simclass", iSimClass); ser.Value("com", com); ser.Value("mass", mass); ser.EndGroup(); iSimClass1 = (unsigned int)(iSimClass - 1) < 2u ? (iSimClass ^ 3) : iSimClass; if (pent = (CPhysicalEntity*)GetPhysicalEntityById(i)) { GetEntityMassAndCom(pent, mass1, com1); if ((pent->m_iSimClass == iSimClass || pent->m_iSimClass == iSimClass1 || iSimClass == -1) && fabs_tpl(mass - mass1) <= fabs_tpl(min(mass, mass1)) * 0.01f && (com - com1).len2() < sqr(0.01f)) { return pent; } } for (i = GetEntitiesAround(com - Vec3(0.1f), com + Vec3(0.1f), pents, 1 << iSimClass | 1 << iSimClass1) - 1; i >= 0; i--) { GetEntityMassAndCom(pents[i], mass1, com1); if (fabs_tpl(mass - mass1) <= fabs_tpl(min(mass, mass1)) * 0.01f && (com - com1).len2() < sqr(0.01f)) { return pents[i]; } } return 0; } int CPhysicalEntity::SetStateFromTypedSnapshot(TSerialize ser, int iSnapshotType, int flags) { static CPhysicalEntity g_entStatic(0); static CRigidEntity g_entRigid(0); static CWheeledVehicleEntity g_entWheeled(0); static CLivingEntity g_entLiving(0); static CParticleEntity g_entParticle(0); static CArticulatedEntity g_entArticulated(0); static CRopeEntity g_entRope(0); static CSoftEntity g_entSoft(0); static CPhysicalEntity* g_pTypedEnts[] = { &g_entStatic, &g_entStatic, &g_entRigid, &g_entWheeled, &g_entLiving, &g_entParticle, &g_entArticulated, &g_entRope, &g_entSoft }; if (GetType() == iSnapshotType) { return SetStateFromSnapshot(ser, flags); } return g_pTypedEnts[min((int)(sizeof(g_pTypedEnts) / sizeof(g_pTypedEnts[0])), max(0, iSnapshotType))]-> SetStateFromSnapshot(ser, flags); } void CPhysicalWorld::SerializeGarbageTypedSnapshot(TSerialize ser, int iSnapshotType, int flags) { static CPhysicalEntity g_entStatic(0); static CRigidEntity g_entRigid(0); static CWheeledVehicleEntity g_entWheeled(0); static CLivingEntity g_entLiving(0); static CParticleEntity g_entParticle(0); static CArticulatedEntity g_entArticulated(0); static CRopeEntity g_entRope(0); static CSoftEntity g_entSoft(0); static CPhysicalEntity* g_pTypedEnts[] = { &g_entStatic, &g_entStatic, &g_entRigid, &g_entWheeled, &g_entLiving, &g_entParticle, &g_entArticulated, &g_entRope, &g_entSoft }; g_pTypedEnts[min((int)(sizeof(g_pTypedEnts) / sizeof(g_pTypedEnts[0])), max(0, iSnapshotType))]-> GetStateSnapshot(ser, flags); } IPhysicalEntityIt* CPhysicalWorld::GetEntitiesIterator() { return new CPhysicalEntityIt(this); } phys_job_info& GetJobProfileInst(int ijob) { return g_pPhysWorlds[0]->GetJobProfileInst(ijob); } struct linkedpt { linkedpt* next[2]; Vec2 pt; }; inline linkedpt* unlink(linkedpt* p) { p->next[0]->next[1] = p->next[1]; p->next[1]->next[0] = p->next[0]; return p; } inline linkedpt* link_after(linkedpt* plist, linkedpt* p) { p->next[0] = plist; p->next[1] = plist->next[1]; plist->next[1]->next[0] = p; plist->next[1] = p; return p; } linkedpt* crop_poly2d(linkedpt* p0, const Vec2* bounds, linkedpt* pbuf) { linkedpt* p = p0; do { if (inrange(p->pt.x, bounds[0].x, bounds[1].x) + inrange(p->pt.y, bounds[0].y, bounds[1].y) < 2) { goto crop_needed; } } while ((p = p->next[1]) != p0); return p0; crop_needed: linkedpt pstart, *plast, *pnew, *pnext; for (int i = 0; i < 4; i++) { pstart.next[1] = pstart.next[0] = plast = &pstart; float b = bounds[i >> 1][i & 1], sg = (i & 2) - 1; int inside = (p = p0)->pt[i & 1] * sg < b * sg; do { pnext = p->next[1]; link_after(inside ? pstart.next[0] : pbuf->next[0], p); if ((p->pt[i & 1] - b) * (pnext->pt[i & 1] - b) < 0) { Vec2 dp = pnext->pt - p->pt; (pnew = unlink(pbuf->next[1]))->pt[i & 1] = b; pnew->pt[i & 1 ^ 1] = p->pt[i & 1 ^ 1] + dp[i & 1 ^ 1] * (b - p->pt[i & 1]) / dp[i & 1]; link_after(pstart.next[0], pnew); inside ^= 1; } } while ((p = pnext) != p0); p0 = pstart.next[1]; unlink(&pstart); } return p0; } void CPhysicalWorld::RasterizeEntities(const grid3d& grid, uchar* rbuf, int objtypes, float massThreshold, const Vec3& offsBBox, const Vec3& sizeBBox, int flags) { linkedpt pbuf[10]; Vec3 bounds = grid.size * Diag33(grid.step), wbounds = Matrix33(grid.Basis.T()).Fabs() * bounds, n, pt; CPhysicalEntity** pents; Quat qBasis = Quat(grid.Basis); int iCaller = get_iCaller(), flagsAll = 0, flagsAny = -1, ystride = grid.size.x; (flags & rwi_colltype_any ? flagsAny : flagsAll) = flags >> rwi_colltype_bit; Vec2 center2d = Vec2(grid.Basis * offsBBox), size2d = Vec2(Matrix33(grid.Basis).Fabs() * sizeBBox), bounds2d[2] = { center2d - size2d, center2d + size2d }; Vec2i ibbox[2]; int i, j, ibuf; for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { ibbox[i][j] = max(0, min(grid.size[j] - 1, float2int(bounds2d[i][j] * grid.stepr[j] - 0.5f))); } } for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { bounds2d[i][j] = (ibbox[i][j] + i) * grid.step[j]; } } for (j = ibbox[0].y; j <= ibbox[1].y; j++) { memset(rbuf + j * ystride + ibbox[0].x, 0, (ibbox[1].x - ibbox[0].x + 1)); } for (int ient = GetEntitiesAround(grid.origin + max(Vec3(ZERO), offsBBox - sizeBBox), grid.origin + min(wbounds, offsBBox + sizeBBox), pents, objtypes) - 1; ient >= 0; ient--) { if (pents[ient]->GetMassInv() * massThreshold <= 1.0f) { for (int ipart = pents[ient]->GetUsedPartsCount(iCaller) - 1; ipart >= 0; ipart--) { const geom& part = pents[ient]->m_parts[pents[ient]->GetUsedPart(iCaller, ipart)]; if (!(part.flags & flagsAny) || (part.flags & flagsAll) != flagsAll) { continue; } QuatTS qPart2Grid(qBasis * pents[ient]->m_qrot * part.q, qBasis * (pents[ient]->m_qrot * part.pos + pents[ient]->m_pos - grid.origin), part.scale); int igeom = part.pPhysGeomProxy->pGeom->GetType(); if (igeom == GEOM_TRIMESH || igeom == GEOM_BOX) { const mesh_data* md = (const mesh_data*)part.pPhysGeomProxy->pGeom->GetData(); int nvtx = 3; if (igeom == GEOM_BOX) { static int boxidx[] = { 0, 2, 3, 1, 0, 4, 6, 2, 2, 6, 7, 3, 3, 7, 5, 1, 1, 5, 4, 0, 4, 5, 7, 6 }; static Vec3 boxvtx[8], boxnorm[6] = { -ortz, -ortx, orty, ortx, -orty, ortz }; static mesh_data boxmd; boxmd.nTris = 6; boxmd.pVertices = strided_pointer<Vec3>(boxvtx); boxmd.pIndices = boxidx; boxmd.pNormals = boxnorm; const box* pbox = (const box*)md; for (i = 0; i < 8; i++) { boxmd.pVertices[i].Set(pbox->size.x * ((i * 2 & 2) - 1), pbox->size.y * ((i & 2) - 1), pbox->size.z * ((i >> 1 & 2) - 1)); } qPart2Grid = qPart2Grid * QuatT(!Quat(pbox->Basis), pbox->center); md = &boxmd; nvtx = 4; } for (int itri = 0; itri < md->nTris; itri++) { if ((n = qPart2Grid.q * md->pNormals[itri]).z > 0) { float zmin = bounds.z * 2, zmax = -bounds.z * 2; for (j = 0; j < nvtx; j++) { pt = qPart2Grid * md->pVertices[md->pIndices[itri * nvtx + j]]; pbuf[j].pt = Vec2(pt); zmin = min(zmin, pt.z); zmax = max(zmax, pt.z); } if (min(bounds.z - zmin, zmax) < 0) { continue; } float rnz = 1 / n.z, rx[2], dy[2] = {1e10f, 1e10f}, rdy[2]; for (i = 0; i < (nvtx + 1) * 2; i++) { pbuf[i].next[1] = pbuf + i + 1, pbuf[i].next[0] = pbuf + i - 1; } pbuf[0].next[0] = pbuf + nvtx - 1; pbuf[nvtx - 1].next[1] = pbuf; pbuf[nvtx].next[0] = pbuf + (nvtx + 1) * 2 - 1; pbuf[(nvtx + 1) * 2 - 1].next[1] = pbuf + nvtx; linkedpt* p0 = crop_poly2d(pbuf, bounds2d, pbuf + nvtx), * p[2]; p[0] = p[1] = p0; do { if (p[1]->pt.y < p[0]->pt.y) { p[0] = p[1]; } } while ((p[1] = p[1]->next[1]) != p0); int iy = float2int(p[0]->pt.y * grid.stepr.y); for (p[1] = p[0];; iy++) { for (i = 0; i < 2; i++) { for (rx[i] = grid.size.x * grid.step.x * (1 - i); p[i]->next[i]->pt.y < (iy + 0.5f) * grid.step.y; ) { if ((p[i] = p[i]->next[i]) == p[i ^ 1]) { goto finished; } } Vec2 dp = p[i]->next[i]->pt - p[i]->pt; if (dp.y != dy[i]) { rdy[i] = 1 / (dy[i] = dp.y); } rx[i] = minmax(rx[i], p[i]->pt.x + ((iy + 0.5f) * grid.step.y - p[i]->pt.y) * dp.x * rdy[i], i); } int ix[2] = { float2int(rx[0] * grid.stepr.x), float2int(rx[1] * grid.stepr.x) }; float zh = (pt * n - (ix[0] + 0.5f) * grid.step.x * n.x - (iy + 0.5f) * grid.step.y * n.y) * rnz; for (ibuf = (i = ix[0]) + iy * ystride; i < ix[1]; i++, ibuf++, zh -= grid.step.x * n.x * rnz) { rbuf[ibuf] = max((int)rbuf[ibuf], max(0, min(255, float2int(zh * grid.stepr.z - 0.5f)))); } } finished:; } } } else { box bbox; part.pPhysGeomProxy->pGeom->GetBBox(&bbox); Vec3 center = qPart2Grid * bbox.center, size = (Matrix33(qPart2Grid.q) * bbox.Basis.T()).Fabs() * bbox.size * qPart2Grid.s; Vec2 bbox2d[2] = { max(bounds2d[0], Vec2(center - size)), min(bounds2d[1], Vec2(center + size)) }; const primitive* pprim = (const primitive*)part.pPhysGeomProxy->pGeom->GetData(); QuatTS qGrid2Part = qPart2Grid.GetInverted(); Vec3 org(0, 0, grid.size.z * grid.step.z * 2), dirn = qGrid2Part.q * -ortz * qPart2Grid.s; ray aray; aray.dir = qGrid2Part.q * -org * qGrid2Part.s; prim_inters inters; for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { ibbox[i][j] = float2int(bbox2d[i][j] * grid.stepr[j] - 0.5f); } } for (org.y = ((j = ibbox[0].y) + 0.5f) * grid.step.y; j <= ibbox[1].y; j++, org.y += grid.step.y) { for (ibuf = j * ystride + (i = ibbox[0].x), org.x = (ibbox[0].x + 0.5f) * grid.step.x; i <= ibbox[1].x; i++, ibuf++, org.x += grid.step.x) { if ((aray.origin = qGrid2Part * org, g_Intersector.Check(igeom, ray::type, pprim, &aray, &inters)) && inters.n.z > 0) { rbuf[ibuf] = max((int)rbuf[ibuf], max(0, min(255, float2int((org.z - ((inters.pt[0] - aray.origin) * dirn)) * grid.stepr.z - 0.5f)))); } } } } } } } } int CPhysicalWorld::GetMaxThreads() { return MAX_PHYS_THREADS; } #if MAX_PHYS_THREADS <= 1 TLS_DECLARE(int*, g_pidxPhysThread) #endif #endif // ENABLE_CRY_PHYSICS