You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2029 lines
73 KiB
C++
2029 lines
73 KiB
C++
/*
|
|
* 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 : make physical representation
|
|
|
|
#include "Cry3DEngine_precompiled.h"
|
|
|
|
#include "StatObj.h"
|
|
#include "IndexedMesh.h"
|
|
#include "3dEngine.h"
|
|
#include "CGFContent.h"
|
|
#include "ObjMan.h"
|
|
#define SMALL_MESH_NUM_INDEX 30
|
|
|
|
#include <CryPhysicsDeprecation.h>
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
///////////////////////// Breakable Geometry /////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace
|
|
{
|
|
template<class T>
|
|
struct SplitArray
|
|
{
|
|
T* ptr[2];
|
|
T& operator[](int idx) { return ptr[idx >> 15][idx & ~(1 << 15)]; }
|
|
T* operator+(int idx) { return ptr[idx >> 15] + (idx & ~(1 << 15)); }
|
|
T* operator-(int idx) { return ptr[idx >> 15] - (idx & ~(1 << 15)); }
|
|
};
|
|
}
|
|
|
|
static inline int mapiTri(int itri, int* pIdx2iTri)
|
|
{
|
|
int mask = (itri - BOP_NEWIDX0) >> 31;
|
|
return itri & mask | pIdx2iTri[itri - BOP_NEWIDX0 | mask];
|
|
}
|
|
|
|
static inline void swap(int* pSubsets, vtx_idx* pidx, int* pmap, int i1, int i2)
|
|
{
|
|
int i = pSubsets[i1];
|
|
pSubsets[i1] = pSubsets[i2];
|
|
pSubsets[i2] = i;
|
|
i = pmap[i1];
|
|
pmap[i1] = pmap[i2];
|
|
pmap[i2] = i;
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
vtx_idx u = pidx[i1 * 3 + i];
|
|
pidx[i1 * 3 + i] = pidx[i2 * 3 + i];
|
|
pidx[i2 * 3 + i] = u;
|
|
}
|
|
}
|
|
static void qsort(int* pSubsets, vtx_idx* pidx, int* pmap, int ileft, int iright, int iter = 0)
|
|
{
|
|
if (ileft >= iright)
|
|
{
|
|
return;
|
|
}
|
|
int i, ilast, diff = 0;
|
|
swap(pSubsets, pidx, pmap, ileft, (ileft + iright) >> 1);
|
|
for (ilast = ileft, i = ileft + 1; i <= iright; i++)
|
|
{
|
|
diff |= pSubsets[i] - pSubsets[ileft];
|
|
if (pSubsets[i] < pSubsets[ileft] + iter) // < when iter==0 and <= when iter==1
|
|
{
|
|
swap(pSubsets, pidx, pmap, ++ilast, i);
|
|
}
|
|
}
|
|
swap(pSubsets, pidx, pmap, ileft, ilast);
|
|
|
|
if (diff)
|
|
{
|
|
qsort(pSubsets, pidx, pmap, ileft, ilast - 1, iter ^ 1);
|
|
qsort(pSubsets, pidx, pmap, ilast + 1, iright, iter ^ 1);
|
|
}
|
|
}
|
|
|
|
static inline int check_mask(unsigned int* pMask, int i)
|
|
{
|
|
return pMask[i >> 5] >> (i & 31) & 1;
|
|
}
|
|
static inline void set_mask(unsigned int* pMask, int i)
|
|
{
|
|
pMask[i >> 5] |= 1u << (i & 31);
|
|
}
|
|
static inline void clear_mask(unsigned int* pMask, int i)
|
|
{
|
|
pMask[i >> 5] &= ~(1u << (i & 31));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
///////////////////////// Deformable Geometry ////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int CStatObj::SubobjHasDeformMorph(int iSubObj)
|
|
{
|
|
int i;
|
|
char nameDeformed[256];
|
|
cry_strcpy(nameDeformed, m_subObjects[iSubObj].name);
|
|
cry_strcat(nameDeformed, "_Destroyed");
|
|
|
|
for (i = m_subObjects.size() - 1; i >= 0 && strcmp(m_subObjects[i].name, nameDeformed); i--)
|
|
{
|
|
;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
#define getBidx(islot) _getBidx(islot, pIdxABuf, pFaceToFace0A, pFace0ToFaceB, pIdxB)
|
|
static inline int _getBidx(int islot, int* pIdxABuf, uint16* pFaceToFace0A, int* pFace0ToFaceB, vtx_idx* pIdxB)
|
|
{
|
|
int idx, mask;
|
|
idx = pFace0ToFaceB[pFaceToFace0A[pIdxABuf[islot] >> 2]] * 3 + (pIdxABuf[islot] & 3);
|
|
mask = idx >> 31;
|
|
return (pIdxB[idx & ~mask] & ~mask) + mask;
|
|
}
|
|
|
|
|
|
int CStatObj::SetDeformationMorphTarget(IStatObj* pDeformed)
|
|
{
|
|
int i, j, k, j0, it, ivtx, nVtxA, nVtxAnew, nVtxA0, nIdxA, nFacesA, nFacesB;
|
|
vtx_idx* pIdxA, * pIdxB;
|
|
int* pVtx2IdxA, * pIdxABuf, * pFace0ToFaceB;
|
|
uint16* pFaceToFace0A, * pFaceToFace0B, maxFace0;
|
|
_smart_ptr<IRenderMesh> pMeshA, pMeshB, pMeshBnew;
|
|
strided_pointer<Vec3> pVtxA, pVtxB, pVtxBnew;
|
|
strided_pointer<Vec2> pTexA, pTexB, pTexBnew;
|
|
strided_pointer<SPipTangents> pTangentsA, pTangentsB, pTangentsBnew;
|
|
if (!GetRenderMesh())
|
|
{
|
|
MakeRenderMesh();
|
|
}
|
|
if (!pDeformed->GetRenderMesh())
|
|
{
|
|
((CStatObj*)pDeformed)->MakeRenderMesh();
|
|
}
|
|
if (!(pMeshA = GetRenderMesh()) || !(pMeshB = pDeformed->GetRenderMesh()) ||
|
|
!(pFaceToFace0A = m_pMapFaceToFace0) || !(pFaceToFace0B = ((CStatObj*)pDeformed)->m_pMapFaceToFace0))
|
|
{
|
|
return 0;
|
|
}
|
|
if (pMeshA->GetMorphBuddy())
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (pMeshA->GetVerticesCount() > 0xffff || pMeshB->GetVerticesCount() > 0xffff)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
nVtxA0 = nVtxA = pMeshA->GetVerticesCount();
|
|
pVtxA.data = (Vec3*)pMeshA->GetPosPtr(pVtxA.iStride, FSL_READ);
|
|
pTexA.data = (Vec2*)pMeshA->GetUVPtr(pTexA.iStride, FSL_READ);
|
|
pTangentsA.data = (SPipTangents*)pMeshA->GetTangentPtr(pTangentsA.iStride, FSL_READ);
|
|
|
|
pVtxB.data = (Vec3*)pMeshB->GetPosPtr(pVtxB.iStride, FSL_READ);
|
|
pTexB.data = (Vec2*)pMeshB->GetUVPtr(pTexB.iStride, FSL_READ);
|
|
pTangentsB.data = (SPipTangents*)pMeshB->GetTangentPtr(pTangentsB.iStride, FSL_READ);
|
|
|
|
nFacesB = pMeshB->GetIndicesCount();
|
|
nFacesB /= 3;
|
|
pIdxB = pMeshB->GetIndexPtr(FSL_READ);
|
|
nIdxA = pMeshA->GetIndicesCount();
|
|
nFacesA = nIdxA /= 3;
|
|
pIdxA = pMeshA->GetIndexPtr(FSL_READ);
|
|
|
|
memset(pVtx2IdxA = new int[nVtxA + 1], 0, (nVtxA + 1) * sizeof(int));
|
|
for (i = 0; i < nIdxA; i++)
|
|
{
|
|
pVtx2IdxA[pIdxA[i]]++;
|
|
}
|
|
for (i = maxFace0 = 0; i < nFacesA; i++)
|
|
{
|
|
maxFace0 = max(maxFace0, pFaceToFace0A[i]);
|
|
}
|
|
for (i = 0; i < nVtxA; i++)
|
|
{
|
|
pVtx2IdxA[i + 1] += pVtx2IdxA[i];
|
|
}
|
|
pIdxABuf = new int[nIdxA];
|
|
for (i = nFacesA - 1; i >= 0; i--)
|
|
{
|
|
for (j = 2; j >= 0; j--)
|
|
{
|
|
pIdxABuf[--pVtx2IdxA[pIdxA[i * 3 + j]]] = i * 4 + j;
|
|
}
|
|
}
|
|
|
|
for (i = nFacesB - 1; i >= 0; i--)
|
|
{
|
|
maxFace0 = max(maxFace0, pFaceToFace0B[i]);
|
|
}
|
|
memset(pFace0ToFaceB = new int[maxFace0 + 1], -1, (maxFace0 + 1) * sizeof(int));
|
|
for (i = nFacesB - 1; i >= 0; i--)
|
|
{
|
|
pFace0ToFaceB[pFaceToFace0B[i]] = i;
|
|
}
|
|
|
|
for (i = nVtxAnew = k = 0; i < nVtxA; i++)
|
|
{
|
|
for (j = pVtx2IdxA[i]; j < pVtx2IdxA[i + 1] - 1; j++)
|
|
{
|
|
for (k = pVtx2IdxA[i + 1] - 1; k > j; k--)
|
|
{
|
|
if (getBidx(k - 1) > getBidx(k))
|
|
{
|
|
it = pIdxABuf[k - 1], pIdxABuf[k - 1] = pIdxABuf[k], pIdxABuf[k] = it;
|
|
}
|
|
}
|
|
}
|
|
for (j = pVtx2IdxA[i] + 1; j < pVtx2IdxA[i + 1]; j++)
|
|
{
|
|
nVtxAnew += iszero(getBidx(j) - getBidx(j - 1)) ^ 1;
|
|
#ifdef _DEBUG
|
|
if ((pVtxB[getBidx(j)] - pVtxB[getBidx(j - 1)]).len2() > sqr(0.01f))
|
|
{
|
|
k++;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
pMeshBnew = GetRenderer()->CreateRenderMesh("StatObj_Deformed", GetFilePath());
|
|
pMeshBnew->UpdateVertices(0, nVtxA0 + nVtxAnew, 0, VSF_GENERAL, 0u);
|
|
if (nVtxAnew)
|
|
{
|
|
pMeshA = GetRenderer()->CreateRenderMesh("StatObj_MorphTarget", GetFilePath());
|
|
m_pRenderMesh->CopyTo(pMeshA, nVtxAnew);
|
|
pVtxA.data = (Vec3*)pMeshA->GetPosPtr(pVtxA.iStride, FSL_SYSTEM_UPDATE);
|
|
pTexA.data = (Vec2*)pMeshA->GetUVPtr(pTexA.iStride, FSL_SYSTEM_UPDATE);
|
|
pTangentsA.data = (SPipTangents*)pMeshA->GetTangentPtr(pTangentsA.iStride, FSL_SYSTEM_UPDATE);
|
|
nIdxA = pMeshA->GetIndicesCount();
|
|
pIdxA = pMeshA->GetIndexPtr(FSL_READ);
|
|
m_pRenderMesh = pMeshA;
|
|
}
|
|
|
|
pVtxBnew.data = (Vec3*)pMeshBnew->GetPosPtr(pVtxBnew.iStride, FSL_SYSTEM_UPDATE);
|
|
pTexBnew.data = (Vec2*)pMeshBnew->GetUVPtr(pTexBnew.iStride, FSL_SYSTEM_UPDATE);
|
|
pTangentsBnew.data = (SPipTangents*)pMeshBnew->GetTangentPtr(pTangentsBnew.iStride, FSL_SYSTEM_UPDATE);
|
|
|
|
for (i = 0; i < nVtxA0; i++)
|
|
{
|
|
for (j = j0 = pVtx2IdxA[i]; j < pVtx2IdxA[i + 1]; j++)
|
|
{
|
|
if (j == pVtx2IdxA[i + 1] - 1 || getBidx(j) != getBidx(j + 1))
|
|
{
|
|
if (j0 > pVtx2IdxA[i])
|
|
{
|
|
ivtx = nVtxA++;
|
|
pVtxA[ivtx] = pVtxA[i];
|
|
pTangentsA[ivtx] = pTangentsA[i];
|
|
pTexA[ivtx] = pTexA[i];
|
|
for (k = j0; k <= j; k++)
|
|
{
|
|
pIdxA[(pIdxABuf[k] >> 2) * 3 + (pIdxABuf[k] & 3)] = ivtx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ivtx = i;
|
|
}
|
|
if ((it = getBidx(j)) >= 0)
|
|
{
|
|
#ifdef _DEBUG
|
|
static float maxdist = 0.1f;
|
|
float dist = (pVtxB[it] - pVtxA[i]).len();
|
|
if (dist > maxdist)
|
|
{
|
|
k++;
|
|
}
|
|
#endif
|
|
pVtxBnew[ivtx] = pVtxB[it];
|
|
pTangentsBnew[ivtx] = pTangentsB[it];
|
|
pTexBnew[ivtx] = pTexB[it];
|
|
}
|
|
else
|
|
{
|
|
pVtxBnew[ivtx] = pVtxA[i];
|
|
pTangentsBnew[ivtx] = pTangentsA[i];
|
|
pTexBnew[ivtx] = pTexA[i];
|
|
}
|
|
j0 = j + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
pMeshA->SetMorphBuddy(pMeshBnew);
|
|
pDeformed->SetFlags(pDeformed->GetFlags() | STATIC_OBJECT_HIDDEN);
|
|
|
|
pMeshBnew->UnlockStream(VSF_GENERAL);
|
|
pMeshBnew->UnlockStream(VSF_TANGENTS);
|
|
pMeshA->UnlockStream(VSF_GENERAL);
|
|
pMeshA->UnlockStream(VSF_TANGENTS);
|
|
|
|
delete [] pVtx2IdxA;
|
|
delete [] pIdxABuf;
|
|
delete [] pFace0ToFaceB;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static inline float max_fast(float op1, float op2) { return (op1 + op2 + fabsf(op1 - op2)) * 0.5f; }
|
|
static inline float min_fast(float op1, float op2) { return (op1 + op2 - fabsf(op1 - op2)) * 0.5f; }
|
|
|
|
static void UpdateWeights(const Vec3& pt, float r, float strength, IRenderMesh* pMesh, IRenderMesh* pWeights)
|
|
{
|
|
int i, nVtx = pMesh->GetVerticesCount();
|
|
float r2 = r * r, rr = 1 / r;
|
|
strided_pointer<Vec3> pVtx;
|
|
strided_pointer<Vec2> pWeight;
|
|
pVtx.data = (Vec3*)pMesh->GetPosPtr(pVtx.iStride, FSL_SYSTEM_UPDATE);
|
|
pWeight.data = (Vec2*)pWeights->GetPosPtr(pWeight.iStride, FSL_SYSTEM_UPDATE);
|
|
|
|
if (r > 0)
|
|
{
|
|
for (i = 0; i < nVtx; i++)
|
|
{
|
|
if ((pVtx[i] - pt).len2() < r2)
|
|
{
|
|
pWeight[i].x = max_fast(0.0f, min_fast(1.0f, pWeight[i].x + strength * (1 - (pVtx[i] - pt).len() * rr)));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < nVtx; i++)
|
|
{
|
|
pWeight[i].x = max_fast(0.0f, min_fast(1.0f, pWeight[i].x + strength));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
IStatObj* CStatObj::DeformMorph(const Vec3& pt, float r, float strength, IRenderMesh* pWeights)
|
|
{
|
|
int i, j;
|
|
CStatObj* pObj = this;
|
|
|
|
if (!GetCVars()->e_DeformableObjects)
|
|
{
|
|
return pObj;
|
|
}
|
|
|
|
if (m_bHasDeformationMorphs)
|
|
{
|
|
if (!(GetFlags() & STATIC_OBJECT_CLONE))
|
|
{
|
|
pObj = (CStatObj*)Clone(true, false, false);
|
|
pObj->m_bUnmergable = 1;
|
|
for (i = pObj->GetSubObjectCount() - 1; i >= 0; i--)
|
|
{
|
|
if ((j = pObj->SubobjHasDeformMorph(i)) >= 0)
|
|
{
|
|
pObj->GetSubObject(i)->pWeights = pObj->GetSubObject(i)->pStatObj->GetRenderMesh()->GenerateMorphWeights();
|
|
pObj->GetSubObject(j)->pStatObj->SetFlags(pObj->GetSubObject(j)->pStatObj->GetFlags() | STATIC_OBJECT_HIDDEN);
|
|
}
|
|
}
|
|
return pObj->DeformMorph(pt, r, strength, pWeights);
|
|
}
|
|
for (i = m_subObjects.size() - 1; i >= 0; i--)
|
|
{
|
|
if (m_subObjects[i].pWeights)
|
|
{
|
|
UpdateWeights(m_subObjects[i].tm.GetInverted() * pt,
|
|
r * (fabs_tpl(m_subObjects[i].tm.GetColumn(0).len2() - 1.0f) < 0.01f ? 1.0f : 1.0f / m_subObjects[i].tm.GetColumn(0).len()),
|
|
strength, m_subObjects[i].pStatObj->GetRenderMesh(), m_subObjects[i].pWeights);
|
|
}
|
|
}
|
|
}
|
|
else if (m_nSubObjectMeshCount == 0 && m_pRenderMesh && m_pRenderMesh->GetMorphBuddy())
|
|
{
|
|
if (!pWeights)
|
|
{
|
|
pObj = new CStatObj;
|
|
pObj->m_pMaterial = m_pMaterial;
|
|
pObj->m_fObjectRadius = m_fObjectRadius;
|
|
pObj->m_vBoxMin = m_vBoxMin;
|
|
pObj->m_vBoxMax = m_vBoxMax;
|
|
pObj->m_vVegCenter = m_vVegCenter;
|
|
pObj->m_fRadiusHors = m_fRadiusHors;
|
|
pObj->m_fRadiusVert = m_fRadiusVert;
|
|
pObj->m_nFlags = m_nFlags | STATIC_OBJECT_CLONE;
|
|
pObj->m_bHasDeformationMorphs = true;
|
|
pObj->m_nSubObjectMeshCount = 1;
|
|
pObj->m_bSharesChildren = true;
|
|
pObj->m_subObjects.resize(1);
|
|
pObj->m_subObjects[0].nType = STATIC_SUB_OBJECT_MESH;
|
|
pObj->m_subObjects[0].name = "";
|
|
pObj->m_subObjects[0].properties = "";
|
|
pObj->m_subObjects[0].bIdentityMatrix = true;
|
|
pObj->m_subObjects[0].tm.SetIdentity();
|
|
pObj->m_subObjects[0].localTM.SetIdentity();
|
|
pObj->m_subObjects[0].pStatObj = this;
|
|
pObj->m_subObjects[0].nParent = -1;
|
|
pObj->m_subObjects[0].helperSize = Vec3(0, 0, 0);
|
|
pObj->m_subObjects[0].pWeights = GetRenderMesh()->GenerateMorphWeights();
|
|
return pObj->DeformMorph(pt, r, strength, pWeights);
|
|
}
|
|
UpdateWeights(pt, r, strength, m_pRenderMesh, pWeights);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
IStatObj* CStatObj::HideFoliage()
|
|
{
|
|
int i;//,j,idx0;
|
|
CMesh* pMesh;
|
|
if (!GetIndexedMesh())
|
|
{
|
|
return this;
|
|
}
|
|
|
|
pMesh = GetIndexedMesh()->GetMesh();
|
|
for (i = pMesh->m_subsets.size() - 1; i >= 0; i--)
|
|
{
|
|
if (pMesh->m_subsets[i].nPhysicalizeType == PHYS_GEOM_TYPE_NONE)
|
|
{
|
|
//pMesh->m_subsets[i].nMatFlags |= MTL_FLAG_NODRAW;
|
|
//idx0 = i>0 ? pMesh->m_subsets[i-1].nFirstIndexId+pMesh->m_subsets[i-1].nNumIndices : 0;
|
|
pMesh->m_subsets.erase(pMesh->m_subsets.begin() + i);
|
|
/*for(j=i;j<pMesh->m_subsets.size();j++)
|
|
{
|
|
memmove(pMesh->m_pIndices+idx0, pMesh->m_pIndices+pMesh->m_subsets[j].nFirstIndexId,
|
|
pMesh->m_subsets[j].nNumIndices*sizeof(pMesh->m_pIndices[0]));
|
|
pMesh->m_subsets[j].nFirstIndexId = idx0;
|
|
idx0 += pMesh->m_subsets[j].nNumIndices;
|
|
}
|
|
pMesh->m_nIndexCount = idx0;*/
|
|
}
|
|
}
|
|
Invalidate();
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////// SubObjects /////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
static inline int GetEdgeByBuddy(mesh_data* pmd, int itri, int itri_buddy)
|
|
{
|
|
int iedge = 0, imask;
|
|
imask = pmd->pTopology[itri].ibuddy[1] - itri_buddy;
|
|
imask = (imask - 1) >> 31 ^ imask >> 31;
|
|
iedge = 1 & imask;
|
|
imask = pmd->pTopology[itri].ibuddy[2] - itri_buddy;
|
|
imask = (imask - 1) >> 31 ^ imask >> 31;
|
|
iedge = iedge & ~imask | 2 & imask;
|
|
return iedge;
|
|
}
|
|
static inline float qmin(float op1, float op2) { return (op1 + op2 - fabsf(op1 - op2)) * 0.5f; }
|
|
static inline float qmax(float op1, float op2) { return (op1 + op2 + fabsf(op1 - op2)) * 0.5f; }
|
|
|
|
static int __s_pvtx_map_dummy = 0;
|
|
|
|
static void SyncToRenderMesh(SSyncToRenderMeshContext* ctx, volatile int* updateState)
|
|
{
|
|
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::ThreeDEngine);
|
|
|
|
IGeometry* pPhysGeom = ctx->pObj->GetPhysGeom() ? ctx->pObj->GetPhysGeom()->pGeom : 0;
|
|
if (pPhysGeom)
|
|
{
|
|
pPhysGeom->Lock(0);
|
|
IStatObj* pObjSrc = (IStatObj*)pPhysGeom->GetForeignData(0);
|
|
if (pPhysGeom->GetForeignData(DATA_MESHUPDATE) || !ctx->pObj->m_hasClothTangentsData || pObjSrc != ctx->pObj && pObjSrc != ctx->pObj->GetCloneSourceObject())
|
|
{ // skip all updates if the mesh was altered
|
|
if (updateState)
|
|
{
|
|
CryInterlockedDecrement(updateState);
|
|
}
|
|
pPhysGeom->Unlock(0);
|
|
return;
|
|
}
|
|
}
|
|
Vec3 n, edge, t;
|
|
int i, j;
|
|
Vec3* vmin = ctx->vmin, * vmax = ctx->vmax;
|
|
int iVtx0 = ctx->iVtx0, nVtx = ctx->nVtx, mask = ctx->mask;
|
|
strided_pointer<Vec3> pVtx = ctx->pVtx;
|
|
int* pVtxMap = (mask == ~0) ? &__s_pvtx_map_dummy : ctx->pVtxMap;
|
|
float rscale = ctx->rscale;
|
|
SClothTangentVtx* ctd = ctx->ctd;
|
|
strided_pointer<Vec3> pMeshVtx = ctx->pMeshVtx;
|
|
strided_pointer<SPipTangents> pTangents = ctx->pTangents;
|
|
strided_pointer<Vec3> pNormals = ctx->pNormals;
|
|
|
|
AABB bbox;
|
|
bbox.Reset();
|
|
|
|
if (pMeshVtx)
|
|
{
|
|
for (i = iVtx0; i < nVtx; i++)
|
|
{
|
|
Vec3 v = pVtx[j = pVtxMap[i & ~mask] | i & mask] * rscale;
|
|
bbox.Add(v);
|
|
pMeshVtx[i] = v;
|
|
}
|
|
*vmin = bbox.min;
|
|
*vmax = bbox.max;
|
|
}
|
|
|
|
if (pTangents)
|
|
{
|
|
for (i = iVtx0; i < nVtx; i++)
|
|
{
|
|
SMeshTangents tb(pTangents[i]);
|
|
int16 nsg;
|
|
tb.GetR(nsg);
|
|
|
|
j = pVtxMap[i & ~mask] | i & mask;
|
|
n = pNormals[j] * aznumeric_cast<float>(ctd[i].sgnNorm);
|
|
edge = (pVtx[ctd[i].ivtxT] - pVtx[j]).normalized();
|
|
Matrix33 M;
|
|
crossproduct_matrix(pNormals[j] * ctd[i].edge.y, M) *= nsg;
|
|
M += Matrix33(IDENTITY) * ctd[i].edge.x;
|
|
t = M.GetInverted() * (edge - n * ctd[i].edge.z);
|
|
(t -= n * (n * t)).Normalize();
|
|
t.x = qmin(qmax(t.x, -0.9999f), 0.9999f);
|
|
t.y = qmin(qmax(t.y, -0.9999f), 0.9999f);
|
|
t.z = qmin(qmax(t.z, -0.9999f), 0.9999f);
|
|
Vec3 b = n.Cross(t) * nsg;
|
|
|
|
tb = SMeshTangents(t, b, nsg);
|
|
tb.ExportTo(pTangents[i]);
|
|
}
|
|
}
|
|
|
|
if (updateState)
|
|
{
|
|
CryInterlockedDecrement(updateState);
|
|
}
|
|
if (pPhysGeom)
|
|
{
|
|
pPhysGeom->Unlock(0);
|
|
}
|
|
}
|
|
|
|
IStatObj* CStatObj::UpdateVertices(strided_pointer<Vec3> pVtx, strided_pointer<Vec3> pNormals, int iVtx0, int nVtx, int* pVtxMap, float rscale)
|
|
{
|
|
CStatObj* pObj = this;
|
|
if (m_pRenderMesh)
|
|
{
|
|
strided_pointer<Vec3> pMeshVtx;
|
|
strided_pointer<SPipTangents> pTangents;
|
|
int i, j, mask = 0, dummy = 0, nVtxFull;
|
|
if (!pVtxMap)
|
|
{
|
|
pVtxMap = &dummy, mask = ~0;
|
|
}
|
|
AABB bbox;
|
|
bbox.Reset();
|
|
SClothTangentVtx* ctd;
|
|
|
|
if (!m_hasClothTangentsData && GetPhysGeom() && GetPhysGeom()->pGeom->GetType() == GEOM_TRIMESH && m_pRenderMesh)
|
|
{
|
|
if (GetPhysGeom()->pGeom->GetForeignData(DATA_MESHUPDATE))
|
|
{
|
|
return this;
|
|
}
|
|
ctd = m_pClothTangentsData = new SClothTangentVtx[nVtxFull = m_pRenderMesh->GetVerticesCount()];
|
|
m_hasClothTangentsData = 1;
|
|
memset(m_pClothTangentsData, 0, sizeof(SClothTangentVtx) * nVtxFull);
|
|
mesh_data* pmd = (mesh_data*)GetPhysGeom()->pGeom->GetData();
|
|
m_pRenderMesh->LockForThreadAccess();
|
|
pTangents.data = (SPipTangents*)m_pRenderMesh->GetTangentPtr(pTangents.iStride, FSL_READ);
|
|
for (i = 0; i < pmd->nTris; i++)
|
|
{
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
ctd[pmd->pIndices[i * 3 + j]].ivtxT = i, ctd[pmd->pIndices[i * 3 + j]].sgnNorm = j;
|
|
}
|
|
}
|
|
if (pmd->pVtxMap)
|
|
{
|
|
for (i = 0; i < pmd->nVertices; i++)
|
|
{
|
|
ctd[i].ivtxT = ctd[pmd->pVtxMap[i]].ivtxT, ctd[i].sgnNorm = ctd[pmd->pVtxMap[i]].sgnNorm;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < nVtxFull && pTangents; i++)
|
|
{
|
|
j = pmd->pVtxMap ? pmd->pVtxMap[i] : i;
|
|
|
|
SMeshTangents tb = SMeshTangents(pTangents[i]);
|
|
Vec3 t, b, s;
|
|
tb.GetTBN(t, b, s);
|
|
|
|
float tedge = -1, tedgeDenom = 0;
|
|
int itri = ctd[i].ivtxT, iedge = ctd[i].sgnNorm, itriT = 0, iedgeT = 0, itri1, loop;
|
|
Vec3 n, edge, edge0;
|
|
n.zero();
|
|
|
|
for (int iter = 0; iter < 2; iter++)
|
|
{
|
|
for (edge0.zero(), loop = 20;; ) // iter==0 - trace cw, 1 - ccw
|
|
{
|
|
edge = (pmd->pVertices[pmd->pIndices[itri * 3 + inc_mod3[iedge]]] - pmd->pVertices[pmd->pIndices[itri * 3 + iedge]]) * (1.f - iter * 2.f);
|
|
n += (edge0 ^ edge) * (iter * 2.f - 1.f);
|
|
edge0 = edge;
|
|
if (sqr(t * edge) * tedgeDenom > tedge * edge.len2())
|
|
{
|
|
tedge = sqr(t * edge), tedgeDenom = edge.len2(), itriT = itri, iedgeT = iedge;
|
|
}
|
|
itri1 = pmd->pTopology[itri].ibuddy[iedge];
|
|
if (itri1 == ctd[i].ivtxT || itri1 < 0 || --loop < 0)
|
|
{
|
|
break;
|
|
}
|
|
iedge = GetEdgeByBuddy(pmd, itri1, itri) + 1 + iter;
|
|
itri = itri1;
|
|
iedge -= 3 & (2 - iedge) >> 31;
|
|
}
|
|
if (itri1 >= 0 && iter == 0)
|
|
{
|
|
n.zero();
|
|
}
|
|
itri = ctd[i].ivtxT;
|
|
iedge = dec_mod3[ctd[i].sgnNorm];
|
|
}
|
|
n += pmd->pVertices[pmd->pIndices[ctd[i].ivtxT * 3 + 1]] - pmd->pVertices[pmd->pIndices[ctd[i].ivtxT * 3]] ^
|
|
pmd->pVertices[pmd->pIndices[ctd[i].ivtxT * 3 + 2]] - pmd->pVertices[pmd->pIndices[ctd[i].ivtxT * 3]];
|
|
|
|
if ((ctd[i].ivtxT = pmd->pIndices[itriT * 3 + iedgeT]) == j)
|
|
{
|
|
ctd[i].ivtxT = pmd->pIndices[itriT * 3 + inc_mod3[iedgeT]];
|
|
}
|
|
edge = (pmd->pVertices[ctd[i].ivtxT] - pmd->pVertices[j]).normalized();
|
|
|
|
ctd[i].edge.Set(edge * t, edge * b, edge * s);
|
|
ctd[i].sgnNorm = sgnnz(n * s);
|
|
}
|
|
if (pObj != this)
|
|
{
|
|
memcpy(pObj->m_pClothTangentsData = new SClothTangentVtx[nVtxFull], ctd, nVtxFull * sizeof(SClothTangentVtx));
|
|
pObj->m_hasClothTangentsData = 1;
|
|
}
|
|
pObj->SetFlags(pObj->GetFlags() & ~STATIC_OBJECT_CANT_BREAK);
|
|
m_pRenderMesh->UnLockForThreadAccess();
|
|
}
|
|
|
|
if (GetTetrLattice() || m_hasSkinInfo)
|
|
{
|
|
Vec3 sz = GetAABB().GetSize();
|
|
float szmin = min(min(sz.x, sz.y), sz.z), szmax = max(max(sz.x, sz.y), sz.z), szmed = sz.x + sz.y + sz.y - szmin - szmax;
|
|
if (!m_hasSkinInfo)
|
|
{
|
|
PrepareSkinData(Matrix34(IDENTITY), 0, min(szmin * 0.5f, szmed * 0.15f));
|
|
}
|
|
if (pVtx)
|
|
{
|
|
return SkinVertices(pVtx, Matrix34(IDENTITY));
|
|
}
|
|
return pObj;
|
|
}
|
|
|
|
if (!pVtx)
|
|
{
|
|
return pObj;
|
|
}
|
|
|
|
if (!(GetFlags() & STATIC_OBJECT_CLONE))
|
|
{
|
|
pObj = (CStatObj*)Clone(true, true, false);
|
|
pObj->m_pRenderMesh->KeepSysMesh(true);
|
|
}
|
|
|
|
IRenderMesh* mesh = pObj->m_pRenderMesh;
|
|
mesh->LockForThreadAccess();
|
|
pMeshVtx.data = (Vec3*)((mesh = pObj->m_pRenderMesh)->GetPosPtr(pMeshVtx.iStride, FSL_SYSTEM_UPDATE));
|
|
if (m_hasClothTangentsData && m_pClothTangentsData)
|
|
{
|
|
pTangents.data = (SPipTangents*)mesh->GetTangentPtr(pTangents.iStride, FSL_SYSTEM_UPDATE);
|
|
}
|
|
|
|
if (!m_pAsyncUpdateContext)
|
|
{
|
|
m_pAsyncUpdateContext = new SSyncToRenderMeshContext;
|
|
}
|
|
else
|
|
{
|
|
m_pAsyncUpdateContext->jobExecutor.WaitForCompletion();
|
|
}
|
|
m_pAsyncUpdateContext->Set(&pObj->m_vBoxMin, &pObj->m_vBoxMax, iVtx0, nVtx, pVtx, pVtxMap, mask, rscale
|
|
, m_pClothTangentsData, pMeshVtx, pTangents, pNormals, pObj);
|
|
|
|
if (GetCVars()->e_RenderMeshUpdateAsync)
|
|
{
|
|
SSyncToRenderMeshContext* pAsyncUpdateContext = m_pAsyncUpdateContext;
|
|
volatile int* updateState = mesh->SetAsyncUpdateState();
|
|
m_pAsyncUpdateContext->jobExecutor.StartJob([pAsyncUpdateContext, updateState]()
|
|
{
|
|
SyncToRenderMesh(pAsyncUpdateContext, updateState);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
SyncToRenderMesh(m_pAsyncUpdateContext, NULL);
|
|
if (m_hasClothTangentsData && m_pClothTangentsData)
|
|
{
|
|
mesh->UnlockStream(VSF_TANGENTS);
|
|
}
|
|
mesh->UnlockStream(VSF_GENERAL);
|
|
}
|
|
mesh->UnLockForThreadAccess();
|
|
}
|
|
return pObj;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
namespace
|
|
{
|
|
CryCriticalSection g_cPrepareSkinData;
|
|
}
|
|
|
|
void CStatObj::PrepareSkinData(const Matrix34& mtxSkelToMesh, IGeometry* pPhysSkel, float r)
|
|
{
|
|
if (m_hasSkinInfo || pPhysSkel && pPhysSkel->GetType() != GEOM_TRIMESH)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// protect again possible paralle calls, if streaming is here, but mainthread also reaches this function
|
|
// before streaming thread has finished and wants to prepare data also
|
|
AUTO_LOCK(g_cPrepareSkinData);
|
|
|
|
// recheck again to guard again creating the object two times
|
|
if (m_hasSkinInfo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_nFlags |= STATIC_OBJECT_DYNAMIC;
|
|
if (!m_pRenderMesh)
|
|
{
|
|
if (!m_pDelayedSkinParams)
|
|
{
|
|
m_pDelayedSkinParams = new SDelayedSkinParams;
|
|
m_pDelayedSkinParams->mtxSkelToMesh = mtxSkelToMesh;
|
|
m_pDelayedSkinParams->pPhysSkel = pPhysSkel;
|
|
m_pDelayedSkinParams->r = r;
|
|
}
|
|
return;
|
|
}
|
|
m_pRenderMesh->KeepSysMesh(true);
|
|
|
|
int i, j, nVtx;
|
|
Vec3 vtx[4];
|
|
geom_world_data gwd[2];
|
|
geom_contact* pcontact;
|
|
mesh_data* md;
|
|
// two spheres for checking intersections against skeleton
|
|
CRY_PHYSICS_REPLACEMENT_ASSERT();
|
|
AZ_UNUSED(pcontact);
|
|
AZ_UNUSED(j);
|
|
strided_pointer<Vec3> pVtx;
|
|
Matrix34 mtxMeshToSkel = mtxSkelToMesh.GetInverted();
|
|
ITetrLattice* pLattice = GetTetrLattice();
|
|
if (pLattice)
|
|
{
|
|
pPhysSkel = pLattice->CreateSkinMesh();
|
|
}
|
|
|
|
gwd[1].scale = mtxSkelToMesh.GetColumn0().len();
|
|
gwd[1].offset = mtxSkelToMesh.GetTranslation();
|
|
gwd[1].R = Matrix33(mtxSkelToMesh) * (1.0f / gwd[1].scale);
|
|
m_pRenderMesh->GetBBox(vtx[0], vtx[1]);
|
|
vtx[1] -= vtx[0];
|
|
primitives::sphere sph;
|
|
sph.center.zero();
|
|
sph.r = r > 0.0f ? r : min(min(vtx[1].x, vtx[1].y), vtx[1].z);
|
|
sph.r *= 3.0f;
|
|
assert(pPhysSkel);
|
|
PREFAST_ASSUME(pPhysSkel);
|
|
md = (mesh_data*)pPhysSkel->GetData();
|
|
IRenderMesh::ThreadAccessLock lockrm(m_pRenderMesh);
|
|
pVtx.data = (Vec3*)m_pRenderMesh->GetPosPtr(pVtx.iStride, FSL_READ);
|
|
SSkinVtx* pSkinInfo = m_pSkinInfo = new SSkinVtx[nVtx = m_pRenderMesh->GetVerticesCount()];
|
|
m_hasSkinInfo = 1;
|
|
|
|
for (i = 0; i < nVtx; i++)
|
|
{
|
|
Vec3 v = pVtx[i];
|
|
if (pSkinInfo[i].bVolumetric = (pLattice && pLattice->CheckPoint(mtxMeshToSkel * v, pSkinInfo[i].idx, pSkinInfo[i].w)))
|
|
{
|
|
pSkinInfo[i].M = Matrix33(md->pVertices[pSkinInfo[i].idx[1]] - md->pVertices[pSkinInfo[i].idx[0]],
|
|
md->pVertices[pSkinInfo[i].idx[2]] - md->pVertices[pSkinInfo[i].idx[0]],
|
|
md->pVertices[pSkinInfo[i].idx[3]] - md->pVertices[pSkinInfo[i].idx[0]]).GetInverted();
|
|
}
|
|
else
|
|
{
|
|
gwd[0].offset = v;
|
|
}
|
|
}
|
|
if (pLattice)
|
|
{
|
|
pPhysSkel->Release();
|
|
}
|
|
}
|
|
|
|
IStatObj* CStatObj::SkinVertices(strided_pointer<Vec3> pSkelVtx, const Matrix34& mtxSkelToMesh)
|
|
{
|
|
if (!m_hasSkinInfo && m_pDelayedSkinParams)
|
|
{
|
|
PrepareSkinData(m_pDelayedSkinParams->mtxSkelToMesh, m_pDelayedSkinParams->pPhysSkel, m_pDelayedSkinParams->r);
|
|
if (m_hasSkinInfo)
|
|
{
|
|
delete m_pDelayedSkinParams, m_pDelayedSkinParams = 0;
|
|
}
|
|
}
|
|
if (!m_pRenderMesh || !m_hasSkinInfo)
|
|
{
|
|
return this;
|
|
}
|
|
CStatObj* pObj = this;
|
|
if (!(GetFlags() & STATIC_OBJECT_CLONE))
|
|
{
|
|
pObj = (CStatObj*)Clone(true, true, false);
|
|
}
|
|
if (!pObj->m_pClonedSourceObject || !pObj->m_pClonedSourceObject->m_pRenderMesh)
|
|
{
|
|
return pObj;
|
|
}
|
|
|
|
strided_pointer<Vec3> pVtx;
|
|
strided_pointer<SPipTangents> pTangents, pTangents0;
|
|
Vec3 vtx[4], t, b;
|
|
Matrix33 M;
|
|
AABB bbox;
|
|
bbox.Reset();
|
|
int i, j, nVtx;
|
|
SSkinVtx* pSkinInfo = m_pSkinInfo;
|
|
pObj->m_pRenderMesh->LockForThreadAccess();
|
|
pObj->m_pClonedSourceObject->m_pRenderMesh->LockForThreadAccess();
|
|
|
|
pVtx.data = (Vec3*)pObj->m_pRenderMesh->GetPosPtr(pVtx.iStride, FSL_SYSTEM_UPDATE);
|
|
pTangents.data = (SPipTangents*)pObj->m_pRenderMesh->GetTangentPtr(pTangents.iStride, FSL_SYSTEM_UPDATE);
|
|
pTangents0.data = (SPipTangents*)pObj->m_pClonedSourceObject->m_pRenderMesh->GetTangentPtr(pTangents0.iStride, FSL_READ);
|
|
nVtx = pObj->m_pRenderMesh->GetVerticesCount();
|
|
if (!pVtx.data)
|
|
{
|
|
nVtx = 0;
|
|
}
|
|
const bool canUseTangents = pTangents.data && pTangents0.data;
|
|
for (i = 0; i < nVtx; i++)
|
|
{
|
|
Vec3 v3 = pVtx[i];
|
|
if (pSkinInfo[i].idx[0] >= 0)
|
|
{
|
|
for (j = 0, v3.zero(); j < 3 + pSkinInfo[i].bVolumetric; j++)
|
|
{
|
|
v3 += pSkinInfo[i].w[j] * (vtx[j] = mtxSkelToMesh * pSkelVtx[pSkinInfo[i].idx[j]]);
|
|
}
|
|
if (!pSkinInfo[i].bVolumetric)
|
|
{
|
|
Vec3 n = (vtx[1] - vtx[0] ^ vtx[2] - vtx[0]).normalized();
|
|
v3 += n * pSkinInfo[i].w[3];
|
|
Vec3 edge = (vtx[1] + vtx[2] - vtx[0] * 2).normalized();
|
|
M = Matrix33(edge, n ^ edge, n);
|
|
}
|
|
else
|
|
{
|
|
M = Matrix33(vtx[1] - vtx[0], vtx[2] - vtx[0], vtx[3] - vtx[0]);
|
|
}
|
|
M *= pSkinInfo[i].M;
|
|
if (canUseTangents)
|
|
{
|
|
SMeshTangents tb(pTangents0[i]);
|
|
|
|
tb.RotateBy(M);
|
|
tb.ExportTo(pTangents0[i]);
|
|
}
|
|
pVtx[i] = v3;
|
|
}
|
|
bbox.Add(v3);
|
|
}
|
|
pObj->m_pRenderMesh->UnlockStream(VSF_GENERAL);
|
|
pObj->m_pRenderMesh->UnlockStream(VSF_TANGENTS);
|
|
pObj->m_pClonedSourceObject->m_pRenderMesh->UnlockStream(VSF_TANGENTS);
|
|
pObj->m_pClonedSourceObject->m_pRenderMesh->UnLockForThreadAccess();
|
|
pObj->m_pRenderMesh->UnLockForThreadAccess();
|
|
pObj->m_vBoxMin = bbox.min;
|
|
pObj->m_vBoxMax = bbox.max;
|
|
return pObj;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
int CStatObj::Physicalize(IPhysicalEntity* pent, pe_geomparams* pgp, int id, const char* szPropsOverride)
|
|
{
|
|
int res = -1;
|
|
if (GetFlags() & STATIC_OBJECT_COMPOUND)
|
|
{
|
|
Matrix34 mtxId(IDENTITY);
|
|
res = PhysicalizeSubobjects(pent, pgp->pMtx3x4 ? pgp->pMtx3x4 : &mtxId, pgp->mass, pgp->density, id, 0, szPropsOverride);
|
|
}
|
|
|
|
{
|
|
int i, nNoColl, iNoColl = 0;
|
|
float V;
|
|
if (pgp->mass < 0 && pgp->density < 0)
|
|
{
|
|
GetPhysicalProperties(pgp->mass, pgp->density);
|
|
}
|
|
for (i = m_arrPhysGeomInfo.GetGeomCount() - 1, nNoColl = 0, V = 0.0f; i >= 0; i--)
|
|
{
|
|
if (m_arrPhysGeomInfo.GetGeomType(i) == PHYS_GEOM_TYPE_DEFAULT)
|
|
{
|
|
V += m_arrPhysGeomInfo[i]->V;
|
|
}
|
|
else
|
|
{
|
|
iNoColl = i, ++nNoColl;
|
|
}
|
|
}
|
|
int flags0 = pgp->flags;
|
|
ISurfaceTypeManager* pSurfaceMan = Get3DEngine()->GetMaterialManager()->GetSurfaceTypeManager();
|
|
if (phys_geometry* pSolid = m_arrPhysGeomInfo[PHYS_GEOM_TYPE_DEFAULT])
|
|
{
|
|
if (pSolid->surface_idx < pSolid->nMats)
|
|
{
|
|
if (ISurfaceType* pMat = pSurfaceMan->GetSurfaceType(pSolid->pMatMapping[pSolid->surface_idx]))
|
|
{
|
|
if (pMat->GetPhyscalParams().collType >= 0)
|
|
{
|
|
(pgp->flags &= ~(geom_collides | geom_floats)) |= pMat->GetPhyscalParams().collType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (pgp->mass > pgp->density && V > 0.0f) // mass is set instead of density and V is valid
|
|
{
|
|
pgp->density = pgp->mass / V, pgp->mass = -1.0f;
|
|
}
|
|
(pgp->flags &= ~geom_colltype_explosion) |= geom_colltype_explosion & ~-(int)m_bDontOccludeExplosions;
|
|
(pgp->flags &= ~geom_manually_breakable) |= geom_manually_breakable & - (int)m_bBreakableByGame;
|
|
if (m_nFlags & STATIC_OBJECT_NO_PLAYER_COLLIDE)
|
|
{
|
|
pgp->flags &= ~geom_colltype_player;
|
|
}
|
|
if (m_nSpines && m_arrPhysGeomInfo.GetGeomCount() - nNoColl <= 1 &&
|
|
(nNoColl == 1 || nNoColl == 2 && m_arrPhysGeomInfo[PHYS_GEOM_TYPE_NO_COLLIDE] && m_arrPhysGeomInfo[PHYS_GEOM_TYPE_OBSTRUCT]))
|
|
{
|
|
pe_params_structural_joint psj;
|
|
bool bHasJoints = false;
|
|
if (m_arrPhysGeomInfo.GetGeomCount() > nNoColl)
|
|
{
|
|
if (nNoColl && m_pParentObject && m_pParentObject != this)
|
|
{
|
|
CStatObj* pParent;
|
|
for (pParent = m_pParentObject; pParent->m_pParentObject; pParent = pParent->m_pParentObject)
|
|
{
|
|
;
|
|
}
|
|
bHasJoints = pParent->FindSubObject_StrStr("$joint") != 0;
|
|
psj.partid[0] = id;
|
|
psj.pt = m_arrPhysGeomInfo[iNoColl]->origin;
|
|
psj.bBreakable = 0;
|
|
}
|
|
res = pent->AddGeometry(m_arrPhysGeomInfo[PHYS_GEOM_TYPE_DEFAULT], pgp, id);
|
|
id += 1024;
|
|
}
|
|
pgp->minContactDist = 1.0f;
|
|
pgp->density = 5.0f;
|
|
if (nNoColl == 1)
|
|
{
|
|
pgp->flags = geom_log_interactions | geom_squashy;
|
|
pgp->flags |= geom_colltype_foliage_proxy;
|
|
res = pent->AddGeometry(m_arrPhysGeomInfo[iNoColl], pgp, psj.partid[1] = id);
|
|
if (bHasJoints)
|
|
{
|
|
pent->SetParams(&psj);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pgp->flags = geom_squashy | geom_colltype_obstruct;
|
|
pent->AddGeometry(m_arrPhysGeomInfo[PHYS_GEOM_TYPE_OBSTRUCT], pgp, psj.partid[1] = id);
|
|
id += 1024;
|
|
if (bHasJoints)
|
|
{
|
|
pent->SetParams(&psj);
|
|
}
|
|
pgp->flags = geom_log_interactions | geom_colltype_foliage_proxy;
|
|
int flagsCollider = pgp->flagsCollider;
|
|
pgp->flagsCollider = 0;
|
|
pent->AddGeometry(m_arrPhysGeomInfo[PHYS_GEOM_TYPE_NO_COLLIDE], pgp, psj.partid[1] = id);
|
|
pgp->flagsCollider = flagsCollider;
|
|
if (bHasJoints)
|
|
{
|
|
pent->SetParams(&psj);
|
|
}
|
|
}
|
|
}
|
|
else if (nNoColl == 1 && m_arrPhysGeomInfo.GetGeomCount() == 2)
|
|
{ // one solid, one obstruct or nocoll proxy -> use single part with ray proxy
|
|
res = pent->AddGeometry(m_arrPhysGeomInfo[iNoColl], pgp, id);
|
|
pgp->flags |= geom_proxy;
|
|
pent->AddGeometry(m_arrPhysGeomInfo[iNoColl ^ 1], pgp, res);
|
|
pgp->flags &= ~geom_proxy;
|
|
}
|
|
else
|
|
{ // add all solid and non-colliding geoms as individual parts
|
|
for (i = 0; i < m_arrPhysGeomInfo.GetGeomCount(); i++)
|
|
{
|
|
if (m_arrPhysGeomInfo.GetGeomType(i) == PHYS_GEOM_TYPE_DEFAULT)
|
|
{
|
|
res = pent->AddGeometry(m_arrPhysGeomInfo[i], pgp, id), id += 1024;
|
|
}
|
|
}
|
|
pgp->idmatBreakable = -1;
|
|
for (i = 0; i < m_arrPhysGeomInfo.GetGeomCount(); i++)
|
|
{
|
|
if (m_arrPhysGeomInfo.GetGeomType(i) == PHYS_GEOM_TYPE_NO_COLLIDE)
|
|
{
|
|
pgp->flags = geom_colltype_ray, res = pent->AddGeometry(m_arrPhysGeomInfo[i], pgp, id), id += 1024;
|
|
}
|
|
else if (m_arrPhysGeomInfo.GetGeomType(i) == PHYS_GEOM_TYPE_OBSTRUCT)
|
|
{
|
|
pgp->flags = geom_colltype_obstruct, res = pent->AddGeometry(m_arrPhysGeomInfo[i], pgp, id), id += 1024;
|
|
}
|
|
}
|
|
}
|
|
pgp->flags = flags0;
|
|
|
|
if (m_arrPhysGeomInfo.GetGeomCount() >= 10 && pent->GetType() == PE_STATIC)
|
|
{
|
|
pe_params_flags pf;
|
|
pf.flagsOR = pef_parts_traceable;
|
|
pf.flagsAND = ~pef_traceable;
|
|
pent->SetParams(&pf);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#define isalpha(c) isalpha((unsigned char)c)
|
|
#define isdigit(c) isdigit((unsigned char)c)
|
|
|
|
int CStatObj::PhysicalizeSubobjects(IPhysicalEntity* pent, const Matrix34* pMtx, float mass, float density, int id0, strided_pointer<int> pJointsIdMap, const char* szPropsOverride)
|
|
{
|
|
int i, j, i0, i1, len = 0, len1, nObj = GetSubObjectCount(), ipart[2], nparts, bHasPlayerOnlyGeoms = 0, nGeoms = 0, id, idNext = 0;
|
|
float V[2], M, scale, jointsz;
|
|
const char* pval, * properties;
|
|
bool bHasSkeletons = false, bAutoJoints = false;
|
|
Matrix34 mtxLoc;
|
|
Vec3 dist;
|
|
primitives::box joint_bbox;
|
|
IGeometry* pJointBox = 0;
|
|
|
|
primitives::box bbox;
|
|
IStatObj::SSubObject* pSubObj, * pSubObj1;
|
|
pe_articgeomparams partpos;
|
|
pe_params_structural_joint psj;
|
|
pe_params_flags pf;
|
|
geom_world_data gwd;
|
|
intersection_params ip;
|
|
geom_contact* pcontacts;
|
|
scale = pMtx ? pMtx->GetColumn(0).len() : 1.0f;
|
|
ip.bStopAtFirstTri = ip.bNoBorder = true;
|
|
|
|
bbox.Basis.SetIdentity();
|
|
bbox.size.Set(0.5f, 0.5f, 0.5f);
|
|
bbox.center.zero();
|
|
joint_bbox = bbox;
|
|
pf.flagsOR = 0;
|
|
|
|
for (i = 0; i < nObj; i++)
|
|
{
|
|
if ((pSubObj = GetSubObject(i))->nType == STATIC_SUB_OBJECT_MESH && pSubObj->pStatObj && pSubObj->pStatObj->GetPhysGeom() &&
|
|
pSubObj->bHidden &&
|
|
(!strncmp(pSubObj->name, "childof_", 8) && (pSubObj1 = FindSubObject((const char*)pSubObj->name + 8)) ||
|
|
(pSubObj1 = GetSubObject(pSubObj->nParent)) && strstr(pSubObj1->properties, "group")))
|
|
{
|
|
pSubObj->bHidden = pSubObj1->bHidden;
|
|
}
|
|
}
|
|
|
|
for (i = 0, V[0] = V[1] = M = 0; i < nObj; i++)
|
|
{
|
|
if ((pSubObj = GetSubObject(i))->nType == STATIC_SUB_OBJECT_MESH && pSubObj->pStatObj && pSubObj->pStatObj->GetPhysGeom() && !pSubObj->bHidden && strncmp(pSubObj->name, "skeleton_", 9))
|
|
{
|
|
float mi = -1.0f, dens = -1.0f, Vsubobj;
|
|
for (j = 0, Vsubobj = 0.0f; pSubObj->pStatObj->GetPhysGeom(j); j++)
|
|
{
|
|
if (((CStatObj*)pSubObj->pStatObj)->m_arrPhysGeomInfo.GetGeomType(j) == PHYS_GEOM_TYPE_DEFAULT)
|
|
{
|
|
Vsubobj += pSubObj->pStatObj->GetPhysGeom(j)->V;
|
|
}
|
|
}
|
|
pSubObj->pStatObj->GetPhysicalProperties(mi, dens);
|
|
if (dens > 0)
|
|
{
|
|
mi = Vsubobj * cube(scale) * dens; // Calc mass.
|
|
}
|
|
if (mi != 0.0f)
|
|
{
|
|
V[isneg(mi)] += Vsubobj * cube(scale);
|
|
}
|
|
M += max(0.0f, mi);
|
|
|
|
if (((CStatObj*)pSubObj->pStatObj)->m_nRenderTrisCount <= 0 &&
|
|
pSubObj->pStatObj->GetPhysGeom()->pGeom->GetForeignData() == pSubObj->pStatObj &&
|
|
strstr(pSubObj->properties, "other_rendermesh"))
|
|
{
|
|
Vec3 center = pSubObj->localTM * (((CStatObj*)pSubObj->pStatObj)->m_vBoxMin + ((CStatObj*)pSubObj->pStatObj)->m_vBoxMax) * 0.5f;
|
|
float mindist = 1e10f, curdist;
|
|
for (j = 0, i0 = i; j < nObj; j++)
|
|
{
|
|
if (j != i && (pSubObj1 = GetSubObject(j)) && pSubObj1->pStatObj && ((CStatObj*)pSubObj1->pStatObj)->m_nRenderTrisCount > 0 &&
|
|
(curdist = (pSubObj1->localTM * (((CStatObj*)pSubObj1->pStatObj)->m_vBoxMin +
|
|
((CStatObj*)pSubObj1->pStatObj)->m_vBoxMax) * 0.5f - center).len2()) < mindist)
|
|
{
|
|
mindist = curdist;
|
|
i0 = j;
|
|
}
|
|
}
|
|
pSubObj->pStatObj->GetPhysGeom()->pGeom->SetForeignData(GetSubObject(i0)->pStatObj, 0);
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < nObj; i++)
|
|
{
|
|
GetSubObject(i)->nBreakerJoints = 0;
|
|
}
|
|
if (mass <= 0)
|
|
{
|
|
mass = M * density;
|
|
}
|
|
if (density <= 0)
|
|
{
|
|
if ((V[0] + V[1]) != 0)
|
|
{
|
|
density = mass / (V[0] + V[1]);
|
|
}
|
|
else
|
|
{
|
|
density = 1000.0f; // Some default.
|
|
}
|
|
}
|
|
partpos.flags = geom_collides | geom_floats;
|
|
|
|
for (i = 0; i < nObj; i++)
|
|
{
|
|
if ((pSubObj = GetSubObject(i))->nType == STATIC_SUB_OBJECT_MESH && pSubObj->pStatObj && pSubObj->pStatObj->GetPhysGeom() &&
|
|
!pSubObj->bHidden && strcmp(pSubObj->name, "colltype_player") == 0)
|
|
{
|
|
bHasPlayerOnlyGeoms = 1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < nObj; i++)
|
|
{
|
|
if ((pSubObj = GetSubObject(i))->nType == STATIC_SUB_OBJECT_MESH && pSubObj->pStatObj && ((CStatObj*)pSubObj->pStatObj)->m_arrPhysGeomInfo.GetGeomCount() && !pSubObj->bHidden)
|
|
{
|
|
if (pJointsIdMap)
|
|
{
|
|
continue;
|
|
}
|
|
partpos.idbody = i + id0;
|
|
partpos.pMtx3x4 = pMtx ? &(mtxLoc = *pMtx * pSubObj->tm) : &pSubObj->tm;
|
|
|
|
float mi = 0, di = 0;
|
|
if (pSubObj->pStatObj->GetPhysicalProperties(mi, di))
|
|
{
|
|
if (mi >= 0)
|
|
{
|
|
partpos.mass = mi, partpos.density = 0;
|
|
}
|
|
else
|
|
{
|
|
partpos.mass = 0, partpos.density = di;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
partpos.density = density;
|
|
}
|
|
if (GetCVars()->e_ObjQuality != CONFIG_LOW_SPEC)
|
|
{
|
|
partpos.idmatBreakable = ((CStatObj*)pSubObj->pStatObj)->m_idmatBreakable;
|
|
if (((CStatObj*)pSubObj->pStatObj)->m_bVehicleOnlyPhysics)
|
|
{
|
|
partpos.flags = geom_colltype6;
|
|
}
|
|
else
|
|
{
|
|
partpos.flags = (geom_colltype_solid & ~(geom_colltype_player & - bHasPlayerOnlyGeoms)) | geom_colltype_ray | geom_floats | geom_colltype_explosion;
|
|
if (bHasPlayerOnlyGeoms && strcmp(pSubObj->name, "colltype_player") == 0)
|
|
{
|
|
partpos.flags = geom_colltype_player;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
partpos.idmatBreakable = -1;
|
|
if (((CStatObj*)pSubObj->pStatObj)->m_bVehicleOnlyPhysics)
|
|
{
|
|
partpos.flags = geom_colltype6;
|
|
}
|
|
}
|
|
if (!strncmp(pSubObj->name, "skeleton_", 9))
|
|
{
|
|
if (!GetCVars()->e_DeformableObjects)
|
|
{
|
|
continue;
|
|
}
|
|
bHasSkeletons = true;
|
|
if (mi <= 0.0f)
|
|
{
|
|
partpos.mass = 1.0f, partpos.density = 0.0f;
|
|
}
|
|
}
|
|
partpos.flagsCollider &= ~geom_destroyed_on_break;
|
|
if (strstr(pSubObj->properties, "pieces"))
|
|
{
|
|
partpos.flagsCollider |= geom_destroyed_on_break;
|
|
}
|
|
if (strstr(pSubObj->properties, "noselfcoll"))
|
|
{
|
|
partpos.flags &= ~(partpos.flagsCollider = geom_colltype_debris);
|
|
}
|
|
if ((id = pSubObj->pStatObj->Physicalize(pent, &partpos, i + id0)) >= 0)
|
|
{
|
|
nGeoms++, idNext = id + 1;
|
|
}
|
|
if (strstr(pSubObj->properties, "force_joint"))
|
|
{
|
|
pe_params_structural_joint psj1;
|
|
psj1.id = 1024 + i;
|
|
psj1.partid[0] = i + id0;
|
|
psj1.partid[1] = pSubObj->nParent + id0;
|
|
psj1.bBreakable = 0;
|
|
psj1.pt = *partpos.pMtx3x4 * (pSubObj->pStatObj->GetBoxMin() + pSubObj->pStatObj->GetBoxMax()) * 0.5f;
|
|
pent->SetParams(&psj1);
|
|
}
|
|
}
|
|
else
|
|
if (pSubObj->nType == STATIC_SUB_OBJECT_DUMMY && !strncmp(pSubObj->name, "$joint", 6))
|
|
{
|
|
properties = szPropsOverride ? szPropsOverride : (const char*)pSubObj->properties;
|
|
psj.pt = pSubObj->tm.GetTranslation();
|
|
psj.n = pSubObj->tm.GetColumn(2).normalized();
|
|
psj.axisx = pSubObj->tm.GetColumn(0).normalized();
|
|
float maxdim = max(pSubObj->helperSize.x, pSubObj->helperSize.y);
|
|
maxdim = max(maxdim, pSubObj->helperSize.z);
|
|
psj.szSensor = jointsz = maxdim * pSubObj->tm.GetColumn(0).len();
|
|
psj.partidEpicenter = -1;
|
|
psj.bBroken = 0;
|
|
psj.id = i;
|
|
psj.bReplaceExisting = 1;
|
|
if (pSubObj->name[6] != ' ')
|
|
{
|
|
gwd.offset = psj.pt;
|
|
ipart[1] = nObj;
|
|
for (i0 = nparts = 0; i0 < nObj && nparts < 2; i0++)
|
|
{
|
|
if ((pSubObj1 = GetSubObject(i0))->nType == STATIC_SUB_OBJECT_MESH && pSubObj1->pStatObj && pSubObj1->pStatObj->GetPhysGeom() &&
|
|
strncmp(pSubObj1->name, "skeleton_", 9) && !strstr(pSubObj1->properties, "group"))
|
|
{
|
|
gwd.offset = pSubObj1->tm.GetInverted() * psj.pt;
|
|
pSubObj1->pStatObj->GetPhysGeom()->pGeom->GetBBox(&bbox);
|
|
dist = bbox.Basis * (gwd.offset - bbox.center);
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
dist[j] = max(0.0f, fabs_tpl(dist[j]) - bbox.size[j]);
|
|
}
|
|
gwd.scale = jointsz;
|
|
if (fabs_tpl(pSubObj1->tm.GetColumn(0).len2() - 1.0f) > 0.01f)
|
|
{
|
|
gwd.scale /= pSubObj1->tm.GetColumn(0).len();
|
|
}
|
|
|
|
// Make a geometry box for intersection.
|
|
if (!pJointBox)
|
|
{
|
|
// Create box for joint
|
|
CRY_PHYSICS_REPLACEMENT_ASSERT();
|
|
}
|
|
{
|
|
WriteLockCond lockColl;
|
|
if (dist.len2() < sqr(gwd.scale * 0.5f) && pSubObj1->pStatObj->GetPhysGeom()->pGeom->IntersectLocked(pJointBox, 0, &gwd, &ip, pcontacts, lockColl))
|
|
{
|
|
ipart[nparts++] = i0;
|
|
}
|
|
} // lock
|
|
}
|
|
}
|
|
if (nparts == 0)
|
|
{
|
|
continue;
|
|
}
|
|
GetSubObject(ipart[0])->pStatObj->GetPhysGeom()->pGeom->GetBBox(&bbox);
|
|
gwd.offset = GetSubObject(ipart[0])->tm * bbox.center;
|
|
j = isneg((gwd.offset - psj.pt) * psj.n);
|
|
i0 = ipart[j];
|
|
i1 = ipart[1 ^ j];
|
|
|
|
if (strlen((const char*)pSubObj->name) >= 7 && !strncmp((const char*)pSubObj->name + 7, "sample", 6))
|
|
{
|
|
psj.bBroken = 2;
|
|
}
|
|
else if (strlen((const char*)pSubObj->name) >= 7 && !strncmp((const char*)pSubObj->name + 7, "impulse", 7))
|
|
{
|
|
psj.bBroken = 2, psj.id = joint_impulse, psj.bReplaceExisting = 0, bAutoJoints = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i0 = 0; i0 < nObj && ((pSubObj1 = GetSubObject(i0))->nType != STATIC_SUB_OBJECT_MESH ||
|
|
(strlen((const char*)pSubObj->name) >= 7 && (strncmp((const char*)pSubObj->name + 7, pSubObj1->name, len = strlen(pSubObj1->name)) || isalpha(pSubObj->name[7 + len])))); i0++)
|
|
{
|
|
;
|
|
}
|
|
for (i1 = 0; i1 < nObj && ((pSubObj1 = GetSubObject(i1))->nType != STATIC_SUB_OBJECT_MESH ||
|
|
(strlen((const char*)pSubObj->name) >= 8 && (strncmp((const char*)pSubObj->name + 8 + len, pSubObj1->name, len1 = strlen(pSubObj1->name)) || isalpha(pSubObj->name[8 + len + len1])))); i1++)
|
|
{
|
|
;
|
|
}
|
|
if (i0 >= nObj && i1 >= nObj)
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, "Error: cannot resolve part names in %s (%s)", (const char*)pSubObj->name, (const char*)m_szFileName);
|
|
}
|
|
}
|
|
if (pJointsIdMap)
|
|
{
|
|
i0 = pJointsIdMap[i0], i1 = pJointsIdMap[i1];
|
|
}
|
|
psj.partid[0] = i0 + id0;
|
|
psj.partid[1] = i1 + id0;
|
|
psj.maxForcePush = psj.maxForcePull = psj.maxForceShift = psj.maxTorqueBend = psj.maxTorqueTwist = 1E20f;
|
|
if (pMtx)
|
|
{
|
|
psj.pt = *pMtx * psj.pt;
|
|
psj.n = pMtx->TransformVector(psj.n).normalized();
|
|
psj.axisx = pMtx->TransformVector(psj.axisx).normalized();
|
|
}
|
|
|
|
if ((pval = strstr(properties, "limit")) && (pval - 11 < properties - 11 || strncmp(pval - 11, "constraint_", 11)))
|
|
{
|
|
for (pval += 5; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.maxTorqueBend = aznumeric_caster(atof(pval));
|
|
}
|
|
//psj.maxTorqueTwist = psj.maxTorqueBend*5;
|
|
psj.maxForcePull = psj.maxTorqueBend;
|
|
psj.maxForceShift = psj.maxTorqueBend;
|
|
// psj.maxForcePush = psj.maxForcePull*2.5f;
|
|
psj.bBreakable = 1;
|
|
}
|
|
if (pval = strstr(properties, "twist"))
|
|
{
|
|
for (pval += 5; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.maxTorqueTwist = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "bend"))
|
|
{
|
|
for (pval += 4; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.maxTorqueBend = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "push"))
|
|
{
|
|
for (pval += 4; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.maxForcePush = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "pull"))
|
|
{
|
|
for (pval += 4; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.maxForcePull = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "shift"))
|
|
{
|
|
for (pval += 5; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.maxForceShift = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "damage_accum"))
|
|
{
|
|
for (pval += 5; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.damageAccum = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "damage_accum_threshold"))
|
|
{
|
|
for (pval += 5; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.damageAccumThresh = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (psj.maxForcePush + psj.maxForcePull + psj.maxForceShift + psj.maxTorqueBend + psj.maxTorqueTwist > 4.9E20f)
|
|
{
|
|
if (azsscanf(properties, "%f %f %f %f %f",
|
|
&psj.maxForcePush, &psj.maxForcePull, &psj.maxForceShift, &psj.maxTorqueBend, &psj.maxTorqueTwist) == 5)
|
|
{
|
|
psj.maxForcePush *= density;
|
|
psj.maxForcePull *= density;
|
|
psj.maxForceShift *= density;
|
|
psj.maxTorqueBend *= density;
|
|
psj.maxTorqueTwist *= density;
|
|
psj.bBreakable = 1;
|
|
}
|
|
else
|
|
{
|
|
psj.bBreakable = 0;
|
|
}
|
|
}
|
|
psj.bDirectBreaksOnly = strstr(properties, "hits_only") != 0;
|
|
psj.limitConstraint.zero();
|
|
psj.bConstraintWillIgnoreCollisions = 1;
|
|
if ((len = 16, pval = strstr(properties, "constraint_limit")) || (len = 5, pval = strstr(properties, "C_lmt")))
|
|
{
|
|
for (pval += len; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.limitConstraint.z = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if ((len = 17, pval = strstr(properties, "constraint_minang")) || (len = 5, pval = strstr(properties, "C_min")))
|
|
{
|
|
for (pval += len; *pval && !isdigit(*pval) && *pval != '-'; pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.limitConstraint.x = aznumeric_caster(DEG2RAD(atof(pval)));
|
|
}
|
|
}
|
|
if ((len = 17, pval = strstr(properties, "constraint_maxang")) || (len = 5, pval = strstr(properties, "C_max")))
|
|
{
|
|
for (pval += len; *pval && !isdigit(*pval) && *pval != '-'; pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.limitConstraint.y = aznumeric_caster(DEG2RAD(atof(pval)));
|
|
}
|
|
}
|
|
if ((len = 18, pval = strstr(properties, "constraint_damping")) || (len = 5, pval = strstr(properties, "C_dmp")))
|
|
{
|
|
for (pval += len; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
psj.dampingConstraint = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (strstr(properties, "constraint_collides") || strstr(properties, "C_coll"))
|
|
{
|
|
psj.bConstraintWillIgnoreCollisions = 0;
|
|
}
|
|
scale = GetFloatCVar(e_JointStrengthScale);
|
|
psj.maxForcePush *= scale;
|
|
psj.maxForcePull *= scale;
|
|
psj.maxForceShift *= scale;
|
|
psj.maxTorqueBend *= scale;
|
|
psj.maxTorqueTwist *= scale;
|
|
psj.limitConstraint.z *= scale;
|
|
pent->SetParams(&psj);
|
|
if (!gEnv->bMultiplayer && strstr(properties, "gameplay_critical"))
|
|
{
|
|
pf.flagsOR |= pef_override_impulse_scale;
|
|
}
|
|
if (gEnv->bMultiplayer && strstr(properties, "mp_break_always"))
|
|
{
|
|
pf.flagsOR |= pef_override_impulse_scale;
|
|
}
|
|
if (strstr(properties, "player_can_break"))
|
|
{
|
|
pf.flagsOR |= pef_players_can_break;
|
|
}
|
|
|
|
if (strstr(pSubObj->properties, "breaker"))
|
|
{
|
|
if (GetSubObject(i0))
|
|
{
|
|
GetSubObject(i0)->nBreakerJoints++;
|
|
}
|
|
if (GetSubObject(i1))
|
|
{
|
|
GetSubObject(i1)->nBreakerJoints++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bAutoJoints)
|
|
{
|
|
psj.idx = -2; // tels the physics to try and generate joints
|
|
pent->SetParams(&psj);
|
|
}
|
|
|
|
pe_params_part pp;
|
|
if (bHasSkeletons)
|
|
{
|
|
for (i = 0; i < nObj; i++)
|
|
{
|
|
if ((pSubObj = GetSubObject(i))->nType == STATIC_SUB_OBJECT_MESH && pSubObj->pStatObj && pSubObj->pStatObj->GetPhysGeom() && !pSubObj->bHidden &&
|
|
!strncmp(pSubObj->name, "skeleton_", 9) && (pSubObj1 = FindSubObject((const char*)pSubObj->name + 9)))
|
|
{
|
|
pe_params_skeleton ps;
|
|
properties = szPropsOverride ? szPropsOverride : (const char*)pSubObj->properties;
|
|
if (pval = strstr(properties, "stiffness"))
|
|
{
|
|
for (pval += 9; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
ps.stiffness = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "thickness"))
|
|
{
|
|
for (pval += 9; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
ps.thickness = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "max_stretch"))
|
|
{
|
|
for (pval += 11; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
ps.maxStretch = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "max_impulse"))
|
|
{
|
|
for (pval += 11; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
ps.maxImpulse = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "skin_dist"))
|
|
{
|
|
for (pval += 9; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
pp.minContactDist = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "hardness"))
|
|
{
|
|
for (pval += 8; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
ps.hardness = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
if (pval = strstr(properties, "explosion_scale"))
|
|
{
|
|
for (pval += 15; *pval && !isdigit(*pval); pval++)
|
|
{
|
|
;
|
|
}
|
|
if (pval)
|
|
{
|
|
ps.explosionScale = aznumeric_caster(atof(pval));
|
|
}
|
|
}
|
|
|
|
pp.partid = ps.partid = aznumeric_cast<int>((pSubObj1 - &m_subObjects[0])) + id0;
|
|
pp.idSkeleton = i + id0;
|
|
pent->SetParams(&pp);
|
|
pent->SetParams(&ps);
|
|
((CStatObj*)pSubObj1->pStatObj)->PrepareSkinData(pSubObj1->localTM.GetInverted() * pSubObj->localTM, pSubObj->pStatObj->GetPhysGeom()->pGeom,
|
|
is_unused(pp.minContactDist) ? 0.0f : pp.minContactDist);
|
|
}
|
|
}
|
|
}
|
|
|
|
new(&pp)pe_params_part;
|
|
for (i = 0; i < nObj; i++)
|
|
{
|
|
if ((pSubObj = GetSubObject(i))->nType == STATIC_SUB_OBJECT_MESH && pSubObj->pStatObj && pSubObj->pStatObj->GetPhysGeom() && !pSubObj->bHidden &&
|
|
(!strncmp(pSubObj->name, "childof_", 8) && (pSubObj1 = FindSubObject((const char*)pSubObj->name + 8)) ||
|
|
(pSubObj1 = GetSubObject(pSubObj->nParent)) && strstr(pSubObj1->properties, "group")))
|
|
{
|
|
pp.partid = i + id0;
|
|
pp.idParent = aznumeric_caster(pSubObj1 - &m_subObjects[0]);
|
|
pent->SetParams(&pp);
|
|
pSubObj->bHidden = true;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Iterate through sub objects and update hide mask.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
m_nInitialSubObjHideMask = 0;
|
|
for (size_t a = 0, numsub = m_subObjects.size(); a < numsub; a++)
|
|
{
|
|
SSubObject& subObj = m_subObjects[a];
|
|
if (subObj.pStatObj && subObj.nType == STATIC_SUB_OBJECT_MESH && subObj.bHidden)
|
|
{
|
|
m_nInitialSubObjHideMask |= ((uint64)1 << a);
|
|
}
|
|
}
|
|
|
|
if (nGeoms >= 10 && pent->GetType() == PE_STATIC)
|
|
{
|
|
pf.flagsOR |= pef_parts_traceable, pf.flagsAND = ~pef_traceable;
|
|
}
|
|
|
|
if (pf.flagsOR)
|
|
{
|
|
pent->SetParams(&pf);
|
|
}
|
|
if (pJointBox)
|
|
{
|
|
pJointBox->Release();
|
|
}
|
|
return idNext - 1;
|
|
}
|
|
|
|
//static const int g_nRanges = 5;
|
|
static int g_rngb2a[] = { 0, 'A', 26, 'a', 52, '0', 62, '+', 63, '/' };
|
|
static int g_rnga2b[] = { '+', 62, '/', 63, '0', 52, 'A', 0, 'a', 26 };
|
|
static inline int mapsymb(int symb, int* pmap, int n)
|
|
{
|
|
int i, j;
|
|
for (i = j = 0; j < n; j++)
|
|
{
|
|
i += isneg(symb - pmap[j * 2]);
|
|
}
|
|
i = n - 1 - i;
|
|
return symb - pmap[i * 2] + pmap[i * 2 + 1];
|
|
}
|
|
|
|
static int Bin2ascii(const unsigned char* pin, int sz, unsigned char* pout)
|
|
{
|
|
int a0, a1, a2, i, j, nout, chr[4];
|
|
for (i = nout = 0; i < sz; i += 3, nout += 4)
|
|
{
|
|
a0 = pin[i];
|
|
j = isneg(i + 1 - sz);
|
|
a1 = pin[i + j] & - j;
|
|
j = isneg(i + 2 - sz);
|
|
a2 = pin[i + j * 2] & - j;
|
|
chr[0] = a0 >> 2;
|
|
chr[1] = a0 << 4 & 0x30 | (a1 >> 4) & 0x0F;
|
|
chr[2] = a1 << 2 & 0x3C | a2 >> 6 & 0x03;
|
|
chr[3] = a2 & 0x03F;
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
*pout++ = mapsymb(chr[j], g_rngb2a, 5);
|
|
}
|
|
}
|
|
return nout;
|
|
}
|
|
static int Ascii2bin(const unsigned char* pin, int sz, unsigned char* pout, int szout)
|
|
{
|
|
int a0, a1, a2, a3, i, nout;
|
|
for (i = nout = 0; i < sz - 4; i += 4, nout += 3)
|
|
{
|
|
a0 = mapsymb(pin[i + 0], g_rnga2b, 5);
|
|
a1 = mapsymb(pin[i + 1], g_rnga2b, 5);
|
|
a2 = mapsymb(pin[i + 2], g_rnga2b, 5);
|
|
a3 = mapsymb(pin[i + 3], g_rnga2b, 5);
|
|
*pout++ = a0 << 2 | a1 >> 4;
|
|
*pout++ = a1 << 4 & 0xF0 | a2 >> 2 & 0x0F;
|
|
*pout++ = a2 << 6 & 0xC0 | a3;
|
|
}
|
|
a0 = mapsymb(pin[i + 0], g_rnga2b, 5);
|
|
a1 = mapsymb(pin[i + 1], g_rnga2b, 5);
|
|
a2 = mapsymb(pin[i + 2], g_rnga2b, 5);
|
|
a3 = mapsymb(pin[i + 3], g_rnga2b, 5);
|
|
if (nout < szout)
|
|
{
|
|
*pout++ = a0 << 2 | a1 >> 4, nout++;
|
|
}
|
|
if (nout < szout)
|
|
{
|
|
*pout++ = a1 << 4 & 0xF0 | a2 >> 2 & 0x0F, nout++;
|
|
}
|
|
if (nout < szout)
|
|
{
|
|
*pout++ = a2 << 6 & 0xC0 | a3, nout++;
|
|
}
|
|
return nout;
|
|
}
|
|
|
|
|
|
static void SerializeData(TSerialize ser, const char* name, void* pdata, int size)
|
|
{
|
|
/*static std::vector<int> arr;
|
|
if (ser.IsReading())
|
|
{
|
|
ser.Value(name, arr);
|
|
memcpy(pdata, &arr[0], size);
|
|
} else
|
|
{
|
|
arr.resize(((size-1)>>2)+1);
|
|
memcpy(&arr[0], pdata, size);
|
|
ser.Value(name, arr);
|
|
}*/
|
|
static string str;
|
|
if (!size)
|
|
{
|
|
return;
|
|
}
|
|
if (ser.IsReading())
|
|
{
|
|
ser.Value(name, str);
|
|
int n = Ascii2bin((const unsigned char*)(const char*)str, str.length(), (unsigned char*)pdata, size);
|
|
assert(n == size);
|
|
(void)n;
|
|
}
|
|
else
|
|
{
|
|
str.resize(((size - 1) / 3 + 1) * 4);
|
|
int n = Bin2ascii((const unsigned char*)pdata, size, (unsigned char*)(const char*)str);
|
|
assert(n == str.length());
|
|
(void)n;
|
|
ser.Value(name, str);
|
|
}
|
|
}
|
|
|
|
|
|
int CStatObj::Serialize(TSerialize ser)
|
|
{
|
|
ser.BeginGroup("StatObj");
|
|
ser.Value("Flags", m_nFlags);
|
|
if (GetFlags() & STATIC_OBJECT_COMPOUND)
|
|
{
|
|
int i, nParts = m_subObjects.size();
|
|
bool bVal;
|
|
string srcObjName;
|
|
ser.Value("nParts", nParts);
|
|
if (m_pClonedSourceObject)
|
|
{
|
|
SetSubObjectCount(nParts);
|
|
for (i = 0; i < nParts; i++)
|
|
{
|
|
ser.BeginGroup("part");
|
|
ser.Value("bGenerated", bVal = !ser.IsReading() && m_subObjects[i].pStatObj &&
|
|
m_subObjects[i].pStatObj->GetFlags() & STATIC_OBJECT_GENERATED && !m_subObjects[i].bHidden);
|
|
if (bVal)
|
|
{
|
|
if (ser.IsReading())
|
|
{
|
|
(m_subObjects[i].pStatObj = gEnv->p3DEngine->CreateStatObj())->AddRef();
|
|
}
|
|
m_subObjects[i].pStatObj->Serialize(ser);
|
|
}
|
|
else
|
|
{
|
|
ser.Value("subobj", m_subObjects[i].name);
|
|
if (ser.IsReading())
|
|
{
|
|
IStatObj::SSubObject* pSrcSubObj = m_pClonedSourceObject->FindSubObject(m_subObjects[i].name);
|
|
if (pSrcSubObj)
|
|
{
|
|
m_subObjects[i] = *pSrcSubObj;
|
|
if (pSrcSubObj->pStatObj)
|
|
{
|
|
pSrcSubObj->pStatObj->AddRef();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
bVal = m_subObjects[i].bHidden;
|
|
ser.Value("hidden", bVal);
|
|
m_subObjects[i].bHidden = bVal;
|
|
ser.EndGroup();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined(CONSOLE)
|
|
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, "Error: full geometry serialization should never happen on consoles. file: '%s' Geom: '%s'", m_szFileName.c_str(), m_szGeomName.c_str());
|
|
#else
|
|
CMesh* pMesh;
|
|
int i, nVtx, nTris, nSubsets;
|
|
string matName;
|
|
_smart_ptr<IMaterial> pMat;
|
|
|
|
if (ser.IsReading())
|
|
{
|
|
ser.Value("nvtx", nVtx = 0);
|
|
if (nVtx)
|
|
{
|
|
m_pIndexedMesh = new CIndexedMesh();
|
|
pMesh = m_pIndexedMesh->GetMesh();
|
|
assert(pMesh->m_pPositionsF16 == 0);
|
|
ser.Value("ntris", nTris);
|
|
ser.Value("nsubsets", nSubsets);
|
|
pMesh->SetVertexCount(nVtx);
|
|
pMesh->ReallocStream(CMesh::TEXCOORDS, 0, nVtx);
|
|
pMesh->ReallocStream(CMesh::TANGENTS, 0, nVtx);
|
|
pMesh->SetIndexCount(nTris * 3);
|
|
|
|
for (i = 0; i < nSubsets; i++)
|
|
{
|
|
SMeshSubset mss;
|
|
ser.BeginGroup("subset");
|
|
ser.Value("matid", mss.nMatID);
|
|
ser.Value("matflg", mss.nMatFlags);
|
|
ser.Value("vtx0", mss.nFirstVertId);
|
|
ser.Value("nvtx", mss.nNumVerts);
|
|
ser.Value("idx0", mss.nFirstIndexId);
|
|
ser.Value("nidx", mss.nNumIndices);
|
|
ser.Value("center", mss.vCenter);
|
|
ser.Value("radius", mss.fRadius);
|
|
pMesh->m_subsets.push_back(mss);
|
|
ser.EndGroup();
|
|
}
|
|
|
|
SerializeData(ser, "Positions", pMesh->m_pPositions, nVtx * sizeof(pMesh->m_pPositions[0]));
|
|
SerializeData(ser, "Normals", pMesh->m_pNorms, nVtx * sizeof(pMesh->m_pNorms[0]));
|
|
SerializeData(ser, "TexCoord", pMesh->m_pTexCoord, nVtx * sizeof(pMesh->m_pTexCoord[0]));
|
|
SerializeData(ser, "Tangents", pMesh->m_pTangents, nVtx * sizeof(pMesh->m_pTangents[0]));
|
|
SerializeData(ser, "Indices", pMesh->m_pIndices, nTris * 3 * sizeof(pMesh->m_pIndices[0]));
|
|
|
|
ser.Value("Material", matName);
|
|
SetMaterial(gEnv->p3DEngine->GetMaterialManager()->FindMaterial(matName));
|
|
ser.Value("MaterialAux", matName);
|
|
if (m_pMaterial && (pMat = gEnv->p3DEngine->GetMaterialManager()->FindMaterial(matName)))
|
|
{
|
|
if (pMat->GetSubMtlCount() > 0)
|
|
{
|
|
pMat = pMat->GetSubMtl(0);
|
|
}
|
|
for (i = m_pMaterial->GetSubMtlCount() - 1; i >= 0 && strcmp(m_pMaterial->GetSubMtl(i)->GetName(), matName); i--)
|
|
{
|
|
;
|
|
}
|
|
if (i < 0)
|
|
{
|
|
i = m_pMaterial->GetSubMtlCount();
|
|
m_pMaterial->SetSubMtlCount(i + 1);
|
|
m_pMaterial->SetSubMtl(i, pMat);
|
|
}
|
|
}
|
|
|
|
int surfaceTypesId[MAX_SUB_MATERIALS];
|
|
memset(surfaceTypesId, 0, sizeof(surfaceTypesId));
|
|
int numIds = 0;
|
|
if (m_pMaterial)
|
|
{
|
|
numIds = m_pMaterial->FillSurfaceTypeIds(surfaceTypesId);
|
|
}
|
|
|
|
|
|
char* pIds = new char[nTris];
|
|
memset(pIds, 0, nTris);
|
|
int j, itri;
|
|
for (i = 0; i < pMesh->m_subsets.size(); i++)
|
|
{
|
|
for (itri = (j = pMesh->m_subsets[i].nFirstIndexId) / 3; j < pMesh->m_subsets[i].nFirstIndexId + pMesh->m_subsets[i].nNumIndices; j += 3, itri++)
|
|
{
|
|
pIds[itri] = pMesh->m_subsets[i].nMatID;
|
|
}
|
|
}
|
|
|
|
ser.Value("PhysSz", i);
|
|
if (i)
|
|
{
|
|
char* pbuf = new char[i];
|
|
CMemStream stm(pbuf, i, false);
|
|
SerializeData(ser, "PhysMeshData", pbuf, i);
|
|
CRY_PHYSICS_REPLACEMENT_ASSERT();
|
|
delete[] pbuf;
|
|
}
|
|
delete[] pIds;
|
|
|
|
Invalidate();
|
|
SetFlags(STATIC_OBJECT_GENERATED);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GetIndexedMesh(true))
|
|
{
|
|
pMesh = GetIndexedMesh(true)->GetMesh();
|
|
assert(pMesh->m_pPositionsF16 == 0);
|
|
ser.Value("nvtx", nVtx = pMesh->GetVertexCount());
|
|
ser.Value("ntris", nTris = pMesh->GetIndexCount() / 3);
|
|
ser.Value("nsubsets", nSubsets = pMesh->m_subsets.size());
|
|
|
|
for (i = 0; i < nSubsets; i++)
|
|
{
|
|
ser.BeginGroup("subset");
|
|
ser.Value("matid", pMesh->m_subsets[i].nMatID);
|
|
ser.Value("matflg", pMesh->m_subsets[i].nMatFlags);
|
|
ser.Value("vtx0", pMesh->m_subsets[i].nFirstVertId);
|
|
ser.Value("nvtx", pMesh->m_subsets[i].nNumVerts);
|
|
ser.Value("idx0", pMesh->m_subsets[i].nFirstIndexId);
|
|
ser.Value("nidx", pMesh->m_subsets[i].nNumIndices);
|
|
ser.Value("center", pMesh->m_subsets[i].vCenter);
|
|
ser.Value("radius", pMesh->m_subsets[i].fRadius);
|
|
ser.EndGroup();
|
|
}
|
|
|
|
if (m_pMaterial && m_pMaterial->GetSubMtlCount() > 0)
|
|
{
|
|
ser.Value("auxmatname", m_pMaterial->GetSubMtl(m_pMaterial->GetSubMtlCount() - 1)->GetName());
|
|
}
|
|
|
|
SerializeData(ser, "Positions", pMesh->m_pPositions, nVtx * sizeof(pMesh->m_pPositions[0]));
|
|
SerializeData(ser, "Normals", pMesh->m_pNorms, nVtx * sizeof(pMesh->m_pNorms[0]));
|
|
SerializeData(ser, "TexCoord", pMesh->m_pTexCoord, nVtx * sizeof(pMesh->m_pTexCoord[0]));
|
|
SerializeData(ser, "Tangents", pMesh->m_pTangents, nVtx * sizeof(pMesh->m_pTangents[0]));
|
|
SerializeData(ser, "Indices", pMesh->m_pIndices, nTris * 3 * sizeof(pMesh->m_pIndices[0]));
|
|
|
|
ser.Value("Material", GetMaterial()->GetName());
|
|
if (m_pMaterial && m_pMaterial->GetSubMtlCount() > 0)
|
|
{
|
|
ser.Value("MaterialAux", m_pMaterial->GetSubMtl(m_pMaterial->GetSubMtlCount() - 1)->GetName());
|
|
}
|
|
else
|
|
{
|
|
ser.Value("MaterialAux", matName = "");
|
|
}
|
|
|
|
if (GetPhysGeom())
|
|
{
|
|
CMemStream stm(false);
|
|
ser.Value("PhysSz", i = stm.GetUsedSize());
|
|
SerializeData(ser, "PhysMeshData", stm.GetBuf(), i);
|
|
}
|
|
else
|
|
{
|
|
ser.Value("PhysSz", i = 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ser.Value("nvtx", nVtx = 0);
|
|
}
|
|
}
|
|
#endif // !defined(CONSOLE)
|
|
}
|
|
|
|
ser.EndGroup(); //StatObj
|
|
|
|
return 1;
|
|
}
|
|
|
|
|