/* * 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. #include "StdAfx.h" #if ENABLE_CRY_PHYSICS #include "RopeRenderNode.h" #include "VisAreas.h" #include "ObjMan.h" #include "MatMan.h" class TubeSurface : public _i_reference_target_t { public: Matrix34 m_worldTM; // If rkUpVector is not the zero vector, it will be used as 'up' in the frame calculations. If it is the zero // vector, the Frenet frame will be used. If bWantColors is 'true', // the vertex colors are allocated and set to black. The application // needs to assign colors as needed. If either of pkTextureMin or // pkTextureMax is not null, both must be not null. In this case, // texture coordinates are generated for the surface. void GenerateSurface(spline::CatmullRomSpline<Vec3>* pSpline, float fRadius, bool bClosed, const Vec3& rkUpVector, int iMedialSamples, int iSliceSamples, bool bWantNormals, bool bWantColors, bool bSampleByArcLength, bool bInsideView, const Vec2* pkTextureMin, const Vec2* pkTextureMax); TubeSurface(); ~TubeSurface (); // member access Vec3& UpVector (); const Vec3& GetUpVector () const; int GetSliceSamples () const; // Generate vertices for the end slices. These are useful when you build // an open tube and want to attach meshes at the ends to close the tube. // The input array must have size (at least) S+1 where S is the number // returned by GetSliceSamples. Function GetTMinSlice is used to access // the slice corresponding to the medial curve evaluated at its domain // minimum, tmin. Function GetTMaxSlice accesses the slice for the // domain maximum, tmax. If the curve is closed, the slices are the same. void GetTMinSlice (Vec3* akSlice); void GetTMaxSlice (Vec3* akSlice); // If the medial curve is modified, for example if it is control point // based and the control points are modified, then you should call this // update function to recompute the tube surface geometry. void UpdateSurface (); ////////////////////////////////////////////////////////////////////////// // tessellation support int Index (int iS, int iM) { return iS + (m_iSliceSamples + 1) * iM; }; void ComputeSinCos (); void ComputeVertices (Vec3* akVertex); void ComputeNormals(); void ComputeTextures (const Vec2& rkTextureMin, const Vec2& rkTextureMax, Vec2* akTexture); void ComputeConnectivity (int& riTQuantity, vtx_idx*& raiConnect, bool bInsideView); float m_fRadius; int m_iMedialSamples, m_iSliceSamples; Vec3 m_kUpVector; float* m_afSin; float* m_afCos; bool m_bClosed, m_bSampleByArcLength; bool m_bCap; int nAllocVerts; int nAllocIndices; int nAllocSinCos; int iVQuantity; int iNumIndices; int iTQuantity; // Num triangles (use iNumIndices instead). Vec3* m_akTangents; Vec3* m_akBitangents; Vec3* m_akVertex; Vec3* m_akNormals; Vec2* m_akTexture; vtx_idx* m_pIndices; spline::CatmullRomSpline<Vec3>* m_pSpline; }; ////////////////////////////////////////////////////////////////////////// TubeSurface::TubeSurface() { m_afSin = NULL; m_afCos = NULL; m_bCap = false; m_bClosed = false; m_bSampleByArcLength = false; m_fRadius = 0; m_iMedialSamples = 0; m_iSliceSamples = 0; nAllocVerts = 0; nAllocIndices = 0; nAllocSinCos = 0; iVQuantity = 0; iTQuantity = 0; iNumIndices = 0; m_pSpline = NULL; m_akTangents = NULL; m_akBitangents = NULL; m_akVertex = NULL; m_akNormals = NULL; m_akTexture = NULL; m_pIndices = NULL; } //---------------------------------------------------------------------------- void TubeSurface::GenerateSurface(spline::CatmullRomSpline<Vec3>* pSpline, float fRadius, bool bClosed, const Vec3& rkUpVector, int iMedialSamples, int iSliceSamples, bool bWantNormals, bool bWantColors, bool bSampleByArcLength, bool bInsideView, const Vec2* pkTextureMin, const Vec2* pkTextureMax) { assert((pkTextureMin && pkTextureMax) || (!pkTextureMin && !pkTextureMax)); m_pSpline = pSpline; m_fRadius = fRadius; m_kUpVector = rkUpVector; m_iMedialSamples = iMedialSamples; m_iSliceSamples = iSliceSamples; m_bClosed = bClosed; m_bSampleByArcLength = bSampleByArcLength; m_bCap = !m_bClosed; // compute the surface vertices if (m_bClosed) { iVQuantity = (m_iSliceSamples + 1) * (m_iMedialSamples + 1); } else { iVQuantity = (m_iSliceSamples + 1) * m_iMedialSamples; } bool bAllocVerts = false; if (iVQuantity > nAllocVerts || !m_akVertex) { bAllocVerts = true; nAllocVerts = iVQuantity; delete [] m_akVertex; m_akVertex = new Vec3[nAllocVerts]; } ComputeSinCos(); ComputeVertices(m_akVertex); // compute the surface normals if (bWantNormals) { if (bAllocVerts) { delete [] m_akNormals; delete [] m_akTangents; delete [] m_akBitangents; m_akNormals = new Vec3[iVQuantity]; m_akTangents = new Vec3[iVQuantity]; m_akBitangents = new Vec3[iVQuantity]; } ComputeNormals(); } // compute the surface textures coordinates if (pkTextureMin && pkTextureMax) { if (bAllocVerts) { delete [] m_akTexture; m_akTexture = new Vec2[iVQuantity]; } ComputeTextures(*pkTextureMin, *pkTextureMax, m_akTexture); } // compute the surface triangle connectivity ComputeConnectivity(iTQuantity, m_pIndices, bInsideView); // create the triangle mesh for the tube surface //Reconstruct(iVQuantity,akVertex,akNormal,akColor,akTexture,iTQuantity,aiConnect); } //---------------------------------------------------------------------------- TubeSurface::~TubeSurface () { delete[] m_pIndices; delete[] m_akTexture; delete[] m_akBitangents; delete[] m_akTangents; delete[] m_akNormals; delete[] m_akVertex; delete[] m_afSin; delete[] m_afCos; } //---------------------------------------------------------------------------- void TubeSurface::ComputeSinCos () { // Compute slice vertex coefficients. The first and last coefficients // are duplicated to allow a closed cross section that has two different // pairs of texture coordinates at the shared vertex. if (m_iSliceSamples + 1 != nAllocSinCos) { nAllocSinCos = m_iSliceSamples + 1; delete []m_afSin; delete []m_afCos; m_afSin = new float[nAllocSinCos]; m_afCos = new float[nAllocSinCos]; } PREFAST_ASSUME(m_iSliceSamples > 0 && m_iSliceSamples == nAllocSinCos - 1); float fInvSliceSamples = 1.0f / (float)m_iSliceSamples; for (int i = 0; i < m_iSliceSamples; i++) { float fAngle = gf_PI2 * fInvSliceSamples * i; m_afCos[i] = cosf(fAngle); m_afSin[i] = sinf(fAngle); } m_afSin[m_iSliceSamples] = m_afSin[0]; m_afCos[m_iSliceSamples] = m_afCos[0]; } //---------------------------------------------------------------------------- void TubeSurface::ComputeVertices (Vec3* akVertex) { float fTMin = m_pSpline->GetRangeStart(); float fTRange = m_pSpline->GetRangeEnd() - fTMin; // sampling by arc length requires the total length of the curve /* float fTotalLength; if ( m_bSampleByArcLength ) //fTotalLength = m_pkMedial->GetTotalLength(); fTotalLength = 0.0f; else fTotalLength = 0.0f; */ if (Cry3DEngineBase::GetCVars()->e_Ropes == 2) { for (int i = 0, npoints = m_pSpline->num_keys() - 1; i < npoints; i++) { gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(m_worldTM * m_pSpline->value(i), ColorB(0, 255, 0, 255), m_worldTM * m_pSpline->value(i + 1), ColorB(0, 255, 0, 255), 6); } } // vertex construction requires a normalized time (uses a division) float fDenom; if (m_bClosed) { fDenom = 1.0f / (float)m_iMedialSamples; } else { fDenom = 1.0f / (float)(m_iMedialSamples - 1); } Vec3 kT_Prev = Vec3(1.0f, 0, 0), kB = m_kUpVector; for (int iM = 0, iV = 0; iM < m_iMedialSamples; iM++) { float fT = fTMin + iM * fTRange * fDenom; float fRadius = m_fRadius; // compute frame (position P, tangent T, normal N, bitangent B) Vec3 kP, kP1, kT, kN; { // Always use 'up' vector N rather than curve normal. You must // constrain the curve so that T and N are never parallel. To // build the frame from this, let // B = Cross(T,N)/Length(Cross(T,N)) // and replace // N = Cross(B,T)/Length(Cross(B,T)). //kP = m_pkMedial->GetPosition(fT); //kT = m_pkMedial->GetTangent(fT); if (iM < m_iMedialSamples - 1) { m_pSpline->interpolate(fT, kP); m_pSpline->interpolate(fT + 0.01f, kP1); } else { m_pSpline->interpolate(fT - 0.01f, kP); m_pSpline->interpolate(fT, kP1); } kT = (kP1 - kP); if (!kT.IsZero()) { kT.NormalizeFast(); } else { // Take direction of last points. kT = kT_Prev; } kT_Prev = kT; //kT = m_pkMedial->GetTangent(fT); kB -= kT * (kB * kT); if (kB.len2() < sqr(0.001f)) { kB = kT.GetOrthogonal(); } //kB = kT.Cross(m_kUpVector); //if (kB.IsZero()) // kB = Vec3(1.0f,0,0); kB.NormalizeFast(); kN = kB.Cross(kT); kN.NormalizeFast(); } // compute slice vertices, duplication at end point as noted earlier int iSave = iV; for (int i = 0; i < m_iSliceSamples; i++) { akVertex[iV] = kP + fRadius * (m_afCos[i] * kN + m_afSin[i] * kB); if (Cry3DEngineBase::GetCVars()->e_Ropes == 2) { ColorB col = ColorB(255, 0, 0, 255); if (i == 0) { col = ColorB(255, 0, 0, 255); } else if (i == 1) { col = ColorB(0, 255, 0, 255); } else { col = ColorB(0, 0, 255, 255); } gEnv->pRenderer->GetIRenderAuxGeom()->DrawPoint(m_worldTM * akVertex[iV], col, 8); } iV++; } akVertex[iV] = akVertex[iSave]; iV++; } if (m_bClosed) { for (int i = 0; i <= m_iSliceSamples; i++) { int i1 = Index(i, m_iMedialSamples); int i0 = Index(i, 0); akVertex[i1] = akVertex[i0]; } } } //---------------------------------------------------------------------------- void TubeSurface::ComputeNormals () { int iS, iSm, iSp, iM, iMm, iMp; Vec3 kDir0, kDir1; // interior normals (central differences) for (iM = 1; iM <= m_iMedialSamples - 2; iM++) { for (iS = 0; iS < m_iSliceSamples; iS++) { iSm = (iS > 0 ? iS - 1 : m_iSliceSamples - 1); iSp = iS + 1; iMm = iM - 1; iMp = iM + 1; kDir0 = m_akVertex[Index(iSm, iM)] - m_akVertex[Index(iSp, iM)]; kDir1 = m_akVertex[Index(iS, iMm)] - m_akVertex[Index(iS, iMp)]; if (kDir0.IsZero(1e-10f)) { kDir0 = Vec3(1, 0, 0); } if (kDir1.IsZero(1e-10f)) { kDir1 = Vec3(0, 1, 0); } Vec3 kN = kDir0.Cross(kDir1); // Normal Vec3 kT = kDir0; // Tangent Vec3 kB = kDir1; // Bitangent if (kN.IsZero(1e-10f)) { kN = Vec3(0, 0, 1); } kN.NormalizeFast(); kT.NormalizeFast(); kB.NormalizeFast(); int nIndex = Index(iS, iM); m_akNormals[nIndex] = kN; m_akTangents[nIndex] = kT; m_akBitangents[nIndex] = kB; } m_akNormals[Index(m_iSliceSamples, iM)] = m_akNormals[Index(0, iM)]; m_akTangents[Index(m_iSliceSamples, iM)] = m_akTangents[Index(0, iM)]; m_akBitangents[Index(m_iSliceSamples, iM)] = m_akBitangents[Index(0, iM)]; } // boundary normals if (m_bClosed) { // central differences for (iS = 0; iS < m_iSliceSamples; iS++) { iSm = (iS > 0 ? iS - 1 : m_iSliceSamples - 1); iSp = iS + 1; // m = 0 kDir0 = m_akVertex[Index(iSm, 0)] - m_akVertex[Index(iSp, 0)]; kDir1 = m_akVertex[Index(iS, m_iMedialSamples - 1)] - m_akVertex[Index(iS, 1)]; m_akNormals[iS] = kDir0.Cross(kDir1).GetNormalized(); // m = max m_akNormals[Index(iS, m_iMedialSamples)] = m_akNormals[Index(iS, 0)]; } m_akNormals[Index(m_iSliceSamples, 0)] = m_akNormals[Index(0, 0)]; m_akNormals[Index(m_iSliceSamples, m_iMedialSamples)] = m_akNormals[Index(0, m_iMedialSamples)]; } else { // one-sided finite differences // m = 0 for (iS = 0; iS < m_iSliceSamples; iS++) { iSm = (iS > 0 ? iS - 1 : m_iSliceSamples - 1); iSp = iS + 1; kDir0 = m_akVertex[Index(iSm, 0)] - m_akVertex[Index(iSp, 0)]; kDir1 = m_akVertex[Index(iS, 0)] - m_akVertex[Index(iS, 1)]; //m_akNormals[Index(iS,0)] = kDir0.Cross(kDir1).GetNormalized(); if (kDir0.IsZero()) { kDir0 = Vec3(1, 0, 0); } if (kDir1.IsZero()) { kDir1 = Vec3(0, 1, 0); } Vec3 kN = kDir0.Cross(kDir1); // Normal Vec3 kT = kDir0; // Tangent Vec3 kB = kDir1; // Bitangent if (kN.IsZero(1e-10f)) { kN = Vec3(0, 0, 1); } kN.NormalizeFast(); kT.NormalizeFast(); kB.NormalizeFast(); int nIndex = iS; m_akNormals[nIndex] = kN; m_akTangents[nIndex] = kT; m_akBitangents[nIndex] = kB; } m_akNormals[Index(m_iSliceSamples, 0)] = m_akNormals[Index(0, 0)]; m_akTangents[Index(m_iSliceSamples, 0)] = m_akTangents[Index(0, 0)]; m_akBitangents[Index(m_iSliceSamples, 0)] = m_akBitangents[Index(0, 0)]; // m = max-1 for (iS = 0; iS < m_iSliceSamples; iS++) { iSm = (iS > 0 ? iS - 1 : m_iSliceSamples - 1); iSp = iS + 1; kDir0 = m_akVertex[Index(iSm, m_iMedialSamples - 1)] - m_akVertex[Index(iSp, m_iMedialSamples - 1)]; kDir1 = m_akVertex[Index(iS, m_iMedialSamples - 2)] - m_akVertex[Index(iS, m_iMedialSamples - 1)]; //m_akNormals[iS] = kDir0.Cross(kDir1).GetNormalized(); if (kDir0.IsZero()) { kDir0 = Vec3(1, 0, 0); } if (kDir1.IsZero()) { kDir1 = Vec3(0, 1, 0); } Vec3 kN = kDir0.Cross(kDir1); // Normal Vec3 kT = kDir0; // Tangent Vec3 kB = kDir1; // Bitangent if (kN.IsZero(1e-10f)) { kN = Vec3(0, 0, 1); } kN.NormalizeFast(); kT.NormalizeFast(); kB.NormalizeFast(); int nIndex = Index(iS, m_iMedialSamples - 1); m_akNormals[nIndex] = kN; m_akTangents[nIndex] = kT; m_akBitangents[nIndex] = kB; } m_akNormals[Index(m_iSliceSamples, m_iMedialSamples - 1)] = m_akNormals[Index(0, m_iMedialSamples - 1)]; m_akTangents[Index(m_iSliceSamples, m_iMedialSamples - 1)] = m_akTangents[Index(0, m_iMedialSamples - 1)]; m_akBitangents[Index(m_iSliceSamples, m_iMedialSamples - 1)] = m_akBitangents[Index(0, m_iMedialSamples - 1)]; } } //---------------------------------------------------------------------------- void TubeSurface::ComputeTextures (const Vec2& rkTextureMin, const Vec2& rkTextureMax, Vec2* akTexture) { Vec2 kTextureRange = rkTextureMax - rkTextureMin; int iMMax = (m_bClosed ? m_iMedialSamples : m_iMedialSamples - 1); for (int iM = 0, iV = 0; iM <= iMMax; iM++) { float fMRatio = ((float)iM) / ((float)iMMax); float fMValue = rkTextureMin.y + fMRatio * kTextureRange.y; for (int iS = 0; iS <= m_iSliceSamples; iS++) { float fSRatio = ((float)iS) / ((float)m_iSliceSamples); float fSValue = rkTextureMin.x + fSRatio * kTextureRange.x; akTexture[iV].x = fSValue; akTexture[iV].y = fMValue; iV++; } } } //---------------------------------------------------------------------------- void TubeSurface::ComputeConnectivity (int& riTQuantity, vtx_idx*& raiConnect, bool bInsideView) { if (m_bClosed) { riTQuantity = 2 * m_iSliceSamples * m_iMedialSamples; } else { riTQuantity = 2 * m_iSliceSamples * (m_iMedialSamples - 1); } iNumIndices = riTQuantity * 3; if (m_bCap) { riTQuantity += ((m_iSliceSamples - 2) * 3) * 2; iNumIndices += ((m_iSliceSamples - 2) * 3) * 2; } if (nAllocIndices != iNumIndices) { if (raiConnect) { delete []raiConnect; } nAllocIndices = iNumIndices; raiConnect = new vtx_idx[nAllocIndices]; } int iM, iMStart, i0, i1, i2, i3, i; vtx_idx* aiConnect = raiConnect; for (iM = 0, iMStart = 0; iM < m_iMedialSamples - 1; iM++) { i0 = iMStart; i1 = i0 + 1; iMStart += m_iSliceSamples + 1; i2 = iMStart; i3 = i2 + 1; for (i = 0; i < m_iSliceSamples; i++, aiConnect += 6) { if (bInsideView) { aiConnect[0] = i0++; aiConnect[1] = i2; aiConnect[2] = i1; aiConnect[3] = i1++; aiConnect[4] = i2++; aiConnect[5] = i3++; } else // outside view { aiConnect[0] = i0++; aiConnect[1] = i1; aiConnect[2] = i2; aiConnect[3] = i1++; aiConnect[4] = i3++; aiConnect[5] = i2++; if (Cry3DEngineBase::GetCVars()->e_Ropes == 2) { ColorB col = ColorB(100, 100, 0, 50); gEnv->pRenderer->GetIRenderAuxGeom()->DrawTriangle(m_worldTM * m_akVertex[aiConnect[0]], col, m_worldTM * m_akVertex[aiConnect[1]], col, m_worldTM * m_akVertex[aiConnect[2]], col); col = ColorB(100, 0, 100, 50); gEnv->pRenderer->GetIRenderAuxGeom()->DrawTriangle(m_worldTM * m_akVertex[aiConnect[3]], col, m_worldTM * m_akVertex[aiConnect[4]], col, m_worldTM * m_akVertex[aiConnect[5]], col); } } } } if (m_bClosed) { i0 = iMStart; i1 = i0 + 1; i2 = 0; i3 = i2 + 1; for (i = 0; i < m_iSliceSamples; i++, aiConnect += 6) { if (bInsideView) { aiConnect[0] = i0++; aiConnect[1] = i2; aiConnect[2] = i1; aiConnect[3] = i1++; aiConnect[4] = i2++; aiConnect[5] = i3++; } else // outside view { aiConnect[0] = i0++; aiConnect[1] = i1; aiConnect[2] = i2; aiConnect[3] = i1++; aiConnect[4] = i3++; aiConnect[5] = i2++; } } } else if (m_bCap) { i0 = 0; i1 = 1; i2 = 2; // Begining Cap. for (i = 0; i < m_iSliceSamples - 2; i++, aiConnect += 3) { aiConnect[0] = i0; aiConnect[1] = i2++; aiConnect[2] = i1++; } i0 = iMStart; i1 = i0 + 1; i2 = i0 + 2; // Ending Cap. for (i = 0; i < m_iSliceSamples - 2; i++, aiConnect += 3) { aiConnect[0] = i0; aiConnect[1] = i1++; aiConnect[2] = i2++; } } } /* //---------------------------------------------------------------------------- void TubeSurface::GetTMinSlice (Vec3* akSlice) { for (int i = 0; i <= m_iSliceSamples; i++) akSlice[i] = m_akVertex[i]; } //---------------------------------------------------------------------------- void TubeSurface::GetTMaxSlice (Vec3* akSlice) { int j = m_iVertexQuantity - m_iSliceSamples - 1; for (int i = 0; i <= m_iSliceSamples; i++, j++) akSlice[i] = m_akVertex[j]; } //---------------------------------------------------------------------------- void TubeSurface::UpdateSurface () { ComputeVertices(m_akVertex); if ( m_akNormal ) ComputeNormals(m_akVertex,m_akNormal); } */ //---------------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // Every Rope will keep a pointer to this and increase reference count. ////////////////////////////////////////////////////////////////////////// class CRopeSurfaceCache : public _i_reference_target_t { public: static CRopeSurfaceCache* GetSurfaceCache(); static void ReleaseSurfaceCache(CRopeSurfaceCache* cache); static void CleanupCaches(); public: struct GetLocalCache { GetLocalCache() : pCache(CRopeSurfaceCache::GetSurfaceCache()) {} ~GetLocalCache() { CRopeSurfaceCache::ReleaseSurfaceCache(pCache); } CRopeSurfaceCache* pCache; private: GetLocalCache(const GetLocalCache&); GetLocalCache& operator = (const GetLocalCache&); }; public: TubeSurface tubeSurface; CRopeSurfaceCache() {}; ~CRopeSurfaceCache() {} private: static CryCriticalSectionNonRecursive g_CacheLock; static StaticInstance<std::vector<CRopeSurfaceCache*>> g_CachePool; }; CryCriticalSectionNonRecursive CRopeSurfaceCache::g_CacheLock; StaticInstance<std::vector<CRopeSurfaceCache*>> CRopeSurfaceCache::g_CachePool; CRopeSurfaceCache* CRopeSurfaceCache::GetSurfaceCache() { CryAutoLock<CryCriticalSectionNonRecursive> lock(g_CacheLock); CRopeSurfaceCache* ret = NULL; if (g_CachePool.empty()) { ret = new CRopeSurfaceCache; } else { ret = g_CachePool.back(); g_CachePool.pop_back(); } return ret; } void CRopeSurfaceCache::ReleaseSurfaceCache(CRopeSurfaceCache* cache) { CryAutoLock<CryCriticalSectionNonRecursive> lock(g_CacheLock); g_CachePool.push_back(cache); } void CRopeSurfaceCache::CleanupCaches() { CryAutoLock<CryCriticalSectionNonRecursive> lock(g_CacheLock); for (std::vector<CRopeSurfaceCache*>::iterator it = g_CachePool.begin(), itEnd = g_CachePool.end(); it != itEnd; ++it) { delete *it; } stl::free_container(g_CachePool); } ////////////////////////////////////////////////////////////////////////// CRopeRenderNode::CRopeRenderNode() : m_pos(0, 0, 0) , m_localBounds(Vec3(-1, -1, -1), Vec3(1, 1, 1)) , m_pRenderMesh(0) , m_pMaterial(0) , m_pPhysicalEntity(0) , m_nLinkedEndsMask(0) { m_pMaterial = GetMatMan()->GetDefaultMaterial(); m_sName.Format("Rope_%X", this); memset(&m_params, 0, sizeof(m_params)); m_params.nMaxIters = 650; m_params.maxTimeStep = 0.02f; m_params.stiffness = 10.0f; m_params.damping = 0.2f; m_params.sleepSpeed = 0.04f; m_bModified = true; m_bRopeCreatedInsideVisArea = false; m_worldTM.SetIdentity(); m_InvWorldTM.SetIdentity(); m_WSBBox.min = Vec3(0, 0, 0); m_WSBBox.max = Vec3(1, 1, 1); m_bNeedToReRegister = true; m_bStaticPhysics = false; m_nEntityOwnerId = 0; } ////////////////////////////////////////////////////////////////////////// CRopeRenderNode::~CRopeRenderNode() { StopRopeSound(); Dephysicalize(); Get3DEngine()->FreeRenderNodeState(this); m_pRenderMesh = NULL; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::StaticReset() { CRopeSurfaceCache::CleanupCaches(); } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::GetLocalBounds(AABB& bbox) { bbox = m_localBounds; }; ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::SetMatrix(const Matrix34& mat) { m_worldTM = mat; m_InvWorldTM = m_worldTM.GetInverted(); m_pos = mat.GetTranslation(); m_WSBBox.SetTransformedAABB(mat, m_localBounds); Get3DEngine()->RegisterEntity(this); m_bNeedToReRegister = false; if (!m_pPhysicalEntity) { Physicalize(); } else { // Just move physics. pe_params_pos par_pos; par_pos.pMtx3x4 = &m_worldTM; m_pPhysicalEntity->SetParams(&par_pos); IVisArea* pVisArea = GetEntityVisArea(); if ((pVisArea != 0) != m_bRopeCreatedInsideVisArea) { // Rope moved between vis area and outside. m_bRopeCreatedInsideVisArea = pVisArea != 0; pe_params_flags par_flags; if (m_params.nFlags & eRope_CheckCollisinos) { // If we are inside vis/area disable terrain collisions. if (!m_bRopeCreatedInsideVisArea) { par_flags.flagsOR = rope_collides_with_terrain; } else { par_flags.flagsAND = ~rope_collides_with_terrain; } } m_pPhysicalEntity->SetParams(&par_flags); } } } ////////////////////////////////////////////////////////////////////////// const char* CRopeRenderNode::GetEntityClassName() const { return "Rope"; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::SetName(const char* sName) { m_sName = sName; } ////////////////////////////////////////////////////////////////////////// const char* CRopeRenderNode::GetName() const { return m_sName; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::Render(const SRendParams& rParams, const SRenderingPassInfo& passInfo) { FUNCTION_PROFILER_3DENGINE; if (GetCVars()->e_Ropes == 0 || (m_dwRndFlags & ERF_HIDDEN) || m_pMaterial == NULL) { return; // false; } if (GetCVars()->e_Ropes == 2) { UpdateRenderMesh(); return; // true; } if (m_bModified) { UpdateRenderMesh(); } if (!m_pRenderMesh || m_pRenderMesh->GetVerticesCount() <= 3) { return; // false; } IRenderer* pRend = GetRenderer(); CRenderObject* pObj = pRend->EF_GetObject_Temp(passInfo.ThreadID()); if (!pObj) { return; // false; } pObj->m_pRenderNode = this; pObj->m_DissolveRef = rParams.nDissolveRef; pObj->m_fSort = rParams.fCustomSortOffset; pObj->m_ObjFlags |= rParams.dwFObjFlags; pObj->m_fAlpha = rParams.fAlpha; pObj->m_II.m_AmbColor = rParams.AmbientColor; pObj->m_II.m_Matrix = m_worldTM; pObj->m_ObjFlags |= FOB_PARTICLE_SHADOWS; //////////////////////////////////////////////////////////////////////////////////////////////////// // Set render quality //////////////////////////////////////////////////////////////////////////////////////////////////// pObj->m_nRenderQuality = (uint16)(rParams.fRenderQuality * 65535.0f); pObj->m_fDistance = rParams.fDistance; //////////////////////////////////////////////////////////////////////////////////////////////////// // Add render elements //////////////////////////////////////////////////////////////////////////////////////////////////// _smart_ptr<IMaterial> pMaterial = m_pMaterial; if (rParams.pMaterial) { pMaterial = rParams.pMaterial; } pObj->m_nMaterialLayers = m_nMaterialLayers; ////////////////////////////////////////////////////////////////////////// if (GetCVars()->e_DebugDraw) { RenderDebugInfo(rParams, passInfo); } ////////////////////////////////////////////////////////////////////////// m_pRenderMesh->Render(rParams, pObj, pMaterial, passInfo); } ////////////////////////////////////////////////////////////////////////// bool CRopeRenderNode::RenderDebugInfo(const SRendParams& rParams, const SRenderingPassInfo& passInfo) { if (passInfo.IsShadowPass()) { return false; } IRenderer* pRend = GetRenderer(); // bool bVerbose = GetCVars()->e_DebugDraw > 1; bool bOnlyBoxes = GetCVars()->e_DebugDraw < 0; if (bOnlyBoxes) { return true; } Vec3 pos = m_worldTM.GetTranslation(); int nTris = m_pRenderMesh->GetIndicesCount() / 3; // text float color[4] = {0, 1, 1, 1}; if (GetCVars()->e_DebugDraw == 2) // color coded polygon count { pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%d", nTris); } else if (GetCVars()->e_DebugDraw == 5) // number of render materials (color coded) { pRend->DrawLabelEx(pos, 1.3f, color, true, true, "1"); } return true; } ////////////////////////////////////////////////////////////////////////// IPhysicalEntity* CRopeRenderNode::GetPhysics() const { return m_pPhysicalEntity; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::SetPhysics(IPhysicalEntity* pPhysicalEntity) { m_pPhysicalEntity = pPhysicalEntity; m_bStaticPhysics = pPhysicalEntity->GetType() != PE_ROPE; pe_params_foreign_data pfd; pfd.pForeignData = (IRenderNode*)this; pfd.iForeignData = PHYS_FOREIGN_ID_ROPE; pPhysicalEntity->SetParams(&pfd); pe_params_rope pr; pPhysicalEntity->GetParams(&pr); m_points.resize(2); m_points[0] = pr.pPoints[0]; m_points[1] = pr.pPoints[pr.nSegments]; SyncWithPhysicalRope(true); } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::Physicalize(bool bInstant) { if (m_params.mass <= 0) { m_bStaticPhysics = true; } else { m_bStaticPhysics = false; } if (!m_pPhysicalEntity || (m_pPhysicalEntity->GetType() == PE_ROPE) != (m_params.mass > 0)) { if (m_pPhysicalEntity) { gEnv->pPhysicalWorld->DestroyPhysicalEntity(m_pPhysicalEntity); } m_pPhysicalEntity = gEnv->pPhysicalWorld->CreatePhysicalEntity((m_bStaticPhysics) ? PE_STATIC : PE_ROPE, NULL, (IRenderNode*)this, PHYS_FOREIGN_ID_ROPE, m_nEntityOwnerId ? (m_nEntityOwnerId & 0xFFFF) : -1); if (!m_pPhysicalEntity) { return; } } if (m_points.size() < 2 || m_params.nPhysSegments < 1) { return; } pe_params_pos par_pos; par_pos.pMtx3x4 = &m_worldTM; m_pPhysicalEntity->SetParams(&par_pos); int surfaceTypesId[MAX_SUB_MATERIALS]; memset(surfaceTypesId, 0, sizeof(surfaceTypesId)); surfaceTypesId[0] = 0; if (m_pMaterial) { m_pMaterial->FillSurfaceTypeIds(surfaceTypesId); } Vec3* pSrcPoints = &m_points[0]; int nSrcPoints = (int)m_points.size(); int nSrcSegments = nSrcPoints - 1; m_physicsPoints.resize(m_params.nPhysSegments + 1); int nPoints = (int)m_physicsPoints.size(); int nTargetSegments = nPoints - 1; int i; ////////////////////////////////////////////////////////////////////////// // Set physical ropes params. pe_params_rope pr; pr.nSegments = nTargetSegments; pr.pPoints = strided_pointer<Vec3>(&m_physicsPoints[0]); pr.length = 0; pr.pEntTiedTo[0] = 0; pr.pEntTiedTo[1] = 0; // Calc original length. for (i = 0; i < nSrcPoints - 1; i++) { pr.length += (pSrcPoints[i + 1] - pSrcPoints[i]).GetLength(); } // Make rope segments of equal length. if (nSrcPoints == 2 && m_params.tension < 0) { int idir, ibound[2] = { 0, 128 }; float h, s, L, a, k, ra, seglen, rh, abound[2], x; Vec3 origin, axisx; static double g_atab[128]; static int g_bTabFilled = 0; h = fabs_tpl(pSrcPoints[0].z - pSrcPoints[1].z); s = (Vec2(pSrcPoints[0]) - Vec2(pSrcPoints[1])).GetLength(); L = (pSrcPoints[0] - pSrcPoints[1]).GetLength() * (1 - m_params.tension); if (h < s * 0.0001) { // will solve for 2*a*sinh(s/(2*a)) == L rh = L; k = 0; } else { // will solve for 2*a*sinh(L/(2*a)) == h/sinh(k) k = h / L; k = log_tpl((1 + k) / (1 - k)) * 0.5f; // k = atanh(k) rh = h / sinh(k); } if (!g_bTabFilled) { for (i = 0, a = 0.125, g_bTabFilled = 1; i < 128; i++, a += (8.0 / 128)) { g_atab[i] = 2 * a * sinh(1 / (2 * a)); } } ibound[0] = 0; ibound[1] = 127; do { i = (ibound[0] + ibound[1]) >> 1; ibound[isneg(g_atab[i] * s - L)] = i; } while (ibound[1] > ibound[0] + 1); abound[1] = (abound[0] = (ibound[0] * (8.0f / 128) + 0.125f) * s) * 2; i = 0; do { a = (abound[0] + abound[1]) * 0.5f; x = 2 * a * sinh(s / (2 * a)) - rh; abound[isneg(x)] = a; } while (++i < 16); ra = 1 / (a = (abound[0] + abound[1]) * 0.5f); idir = isneg(pSrcPoints[0].z - pSrcPoints[1].z); pr.pPoints[0] = pSrcPoints[0]; pr.pPoints[nTargetSegments] = pSrcPoints[1]; (axisx = (Vec2(pSrcPoints[idir ^ 1]) - Vec2(pSrcPoints[idir])) / s).z = 0; origin = (pSrcPoints[0] + pSrcPoints[1]) * 0.5; origin.z = pSrcPoints[idir].z; origin += axisx * (a * k); x = -s * 0.5f - a * k; origin.z -= a * cosh(x * ra); seglen = L / nTargetSegments; for (i = 1; i < nTargetSegments; i++) { x = seglen * ra + sinh(x * ra); x = log(x + sqrt(x * x + 1)) * a; // x = asinh(x)*a; pr.pPoints[(nTargetSegments & - idir) + i - (i & - idir) * 2] = origin + axisx * x + Vec3(0, 0, 1) * a * cosh(x * ra); } } else { int iter, ivtx; float ka, kb, kc, kd; float rnSegs = 1.0f / nTargetSegments; float len = pr.length; Vec3 dir, v0; iter = 3; do { pr.pPoints[0] = pSrcPoints[0]; for (i = ivtx = 0; i < nTargetSegments && ivtx < nSrcSegments; ) { dir = pSrcPoints[ivtx + 1] - pSrcPoints[ivtx]; v0 = pSrcPoints[ivtx] - pr.pPoints[i]; ka = dir.GetLengthSquared(); kb = v0 * dir; kc = v0.GetLengthSquared() - sqr(len * rnSegs); kd = sqrt_tpl(max(0.0f, kb * kb - ka * kc)); if (kd - kb < ka) { pr.pPoints[++i] = pSrcPoints[ivtx] + dir * ((kd - kb) / ka); } else { ++ivtx; } } len *= (1.0f - (nTargetSegments - i) * rnSegs); len += (pSrcPoints[nSrcSegments] - pr.pPoints[i]).GetLength(); } while (--iter); pr.pPoints[nTargetSegments] = pSrcPoints[nSrcSegments]; // copy last point dir = pr.pPoints[nTargetSegments] - pr.pPoints[i]; if (i + 1 < nTargetSegments) { for (ivtx = i + 1, rnSegs = 1.0f / (nTargetSegments - i); ivtx < nTargetSegments; ivtx++) { pr.pPoints[ivtx] = pr.pPoints[i] + dir * ((ivtx - i) * rnSegs); } } // Update length. pr.length = 0; for (i = 0; i < nPoints - 1; i++) { pr.length += (pr.pPoints[i + 1] - pr.pPoints[i]).GetLength(); } } // Transform to world space. for (i = 0; i < nPoints; i++) { pr.pPoints[i] = m_worldTM.TransformPoint(pr.pPoints[i]); } if (!m_bStaticPhysics) { ////////////////////////////////////////////////////////////////////////// pe_params_flags par_flags; par_flags.flags = pef_never_affect_triggers | pef_log_state_changes | pef_log_poststep; if (m_params.nFlags & eRope_Subdivide) { par_flags.flags |= rope_subdivide_segs; } if (m_params.nFlags & eRope_CheckCollisinos) { par_flags.flags |= rope_collides; if (m_params.nFlags & eRope_NoAttachmentCollisions) { par_flags.flags |= rope_ignore_attachments; } // If we are inside vis/area disable terrain collisions. if (!m_bRopeCreatedInsideVisArea) { par_flags.flags |= rope_collides_with_terrain; } } par_flags.flags |= rope_traceable; if (m_params.mass <= 0) { par_flags.flags |= pef_disabled; } par_flags.flags |= rope_no_tears & - isneg(m_params.maxForce); m_pPhysicalEntity->SetParams(&par_flags); pr.mass = m_params.mass; pr.airResistance = m_params.airResistance; pr.collDist = m_params.fThickness; pr.sensorRadius = m_params.fAnchorRadius; pr.maxForce = fabsf(m_params.maxForce); pr.jointLimit = m_params.jointLimit; pr.friction = m_params.friction; pr.frictionPull = m_params.frictionPull; pr.wind = m_params.wind; pr.windVariance = m_params.windVariance; pr.nMaxSubVtx = m_params.nMaxSubVtx; pr.surface_idx = surfaceTypesId[0]; pr.maxIters = m_params.nMaxIters; pr.stiffness = m_params.stiffness; pr.penaltyScale = m_params.hardness; AnchorEndPoints(pr); if (m_params.tension != 0) { pr.length *= 1 - m_params.tension; } else if (nSrcPoints == 2) { pr.length *= 0.999f; } m_pPhysicalEntity->SetParams(&pr); ////////////////////////////////////////////////////////////////////////// // After creation rope is put to sleep, will be awaked on first render after modify. pe_action_awake pa; pa.bAwake = m_params.nFlags & eRope_Awake ? 1 : 0; m_pPhysicalEntity->Action(&pa); pe_simulation_params simparams; simparams.maxTimeStep = m_params.maxTimeStep; simparams.damping = m_params.damping; simparams.minEnergy = sqr(m_params.sleepSpeed); m_pPhysicalEntity->SetParams(&simparams); m_bRopeCreatedInsideVisArea = GetEntityVisArea() != 0; if (m_params.nFlags & eRope_NoPlayerCollisions) { pe_params_collision_class pcs; pcs.collisionClassOR.ignore = collision_class_living; m_pPhysicalEntity->SetParams(&pcs); } } else { primitives::capsule caps; pe_params_pos pp; IGeomManager* pGeoman = gEnv->pPhysicalWorld->GetGeomManager(); phys_geometry* pgeom; pe_geomparams gp; pp.pos = pr.pPoints[0]; m_pPhysicalEntity->SetParams(&pp); caps.r = m_params.fThickness; /* m_pPhysicalEntity->RemoveGeometry() for(i=0; i<nPoints-1; i++) { caps.axis = pr.pPoints[i+1]-pr.pPoints[i]; caps.axis /= (caps.hh = caps.axis.len()); caps.hh *= 0.5f; caps.center = (pr.pPoints[i+1]+pr.pPoints[i])*0.5f-pr.pPoints[0]; pgeom = pGeoman->RegisterGeometry(pGeoman->CreatePrimitive(primitives::capsule::type, &caps), surfaceTypesId[0]); pgeom->nRefCount = 0; // we want it to be deleted together with the physical entity m_pPhysicalEntity->AddGeometry(pgeom, &gp); } */ pe_action_remove_all_parts removeall; m_pPhysicalEntity->Action(&removeall); int numSplinePoints = m_points.size(); m_spline.resize(numSplinePoints); for (int pnt = 0; pnt < numSplinePoints; pnt++) { m_spline.key(pnt).flags = 0; m_spline.time(pnt) = (float)pnt / (numSplinePoints - 1); m_spline.value(pnt) = m_points[pnt]; } int nLengthSamples = m_params.nNumSegments + 1; if (!(m_params.nFlags & IRopeRenderNode::eRope_Smooth)) { nLengthSamples = m_params.nPhysSegments + 1; } Vec3 p0 = m_points[0]; Vec3 p1; for (i = 1; i < nLengthSamples; i++) { m_spline.interpolate((float)i / (nLengthSamples - 1), p1); caps.axis = p1 - p0; caps.axis /= (caps.hh = caps.axis.len()); caps.hh *= 0.5f; caps.center = (p1 + p0) * 0.5f - m_points[0]; IGeometry* pGeom = pGeoman->CreatePrimitive(primitives::capsule::type, &caps); pgeom = pGeoman->RegisterGeometry(pGeom, surfaceTypesId[0]); pGeom->Release(); pgeom->nRefCount = 0; // we want it to be deleted together with the physical entity m_pPhysicalEntity->AddGeometry(pgeom, &gp); p0 = p1; } } m_WSBBox.Reset(); for (auto& point : m_points) { m_WSBBox.Add(m_worldTM.TransformPoint(point)); } m_WSBBox.Expand(Vec3(m_params.fThickness)); m_bModified = true; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::LinkEndPoints() { if (m_pPhysicalEntity) { if (m_params.mass > 0) { pe_params_flags par_flags; par_flags.flagsOR = pef_disabled; m_pPhysicalEntity->SetParams(&par_flags); // To prevent rope from awakining objects on linking. } pe_params_rope pr; pr.pEntTiedTo[0] = 0; pr.pEntTiedTo[1] = 0; AnchorEndPoints(pr); m_pPhysicalEntity->SetParams(&pr); if (m_params.mass > 0 && !(m_params.nFlags & eRope_Disabled)) { pe_params_flags par_flags; par_flags.flagsAND = ~pef_disabled; m_pPhysicalEntity->SetParams(&par_flags); } } } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::LinkEndEntities(IPhysicalEntity* pStartEntity, IPhysicalEntity* pEndEntity) { m_nLinkedEndsMask = 0; if (!m_pPhysicalEntity || m_params.mass <= 0) { return; } if (m_params.mass > 0) { pe_params_flags par_flags; par_flags.flagsOR = pef_disabled; m_pPhysicalEntity->SetParams(&par_flags); // To prevent rope from awakining objects on linking. } pe_params_rope pr; if (pStartEntity) { pr.pEntTiedTo[0] = pStartEntity; pr.idPartTiedTo[0] = 0; m_nLinkedEndsMask |= 0x01; } if (pEndEntity) { pr.pEntTiedTo[1] = pEndEntity; pr.idPartTiedTo[1] = 0; m_nLinkedEndsMask |= 0x02; } m_pPhysicalEntity->SetParams(&pr); if (m_params.mass > 0 && !(m_params.nFlags & eRope_Disabled)) { pe_params_flags par_flags; par_flags.flagsAND = ~pef_disabled; m_pPhysicalEntity->SetParams(&par_flags); } } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::GetEndPointLinks(SEndPointLink* links) { links[0].pPhysicalEntity = 0; links[0].offset.Set(0, 0, 0); links[0].nPartId = 0; links[1].pPhysicalEntity = 0; links[1].offset.Set(0, 0, 0); links[1].nPartId = 0; pe_params_rope pr; if (m_pPhysicalEntity) { m_pPhysicalEntity->GetParams(&pr); for (int i = 0; i < 2; i++) { if (!is_unused(pr.pEntTiedTo[i]) && pr.pEntTiedTo[i]) { if (pr.pEntTiedTo[i] == WORLD_ENTITY) { pr.pEntTiedTo[i] = gEnv->pPhysicalWorld->GetPhysicalEntityById(gEnv->pPhysicalWorld->GetPhysicalEntityId(WORLD_ENTITY)); } links[i].pPhysicalEntity = pr.pEntTiedTo[i]; links[i].nPartId = pr.idPartTiedTo[i]; Matrix34 tm; pe_status_pos ppos; ppos.pMtx3x4 = &tm; pr.pEntTiedTo[i]->GetStatus(&ppos); tm.Invert(); links[i].offset = tm.TransformPoint(pr.ptTiedTo[i]); // To local space. } else { links[i].offset = Vec3(0, 0, 0); } } } } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::AnchorEndPoints(pe_params_rope& pr) { m_nLinkedEndsMask = 0; //m_params.fAnchorRadius if (!m_pPhysicalEntity || m_params.mass <= 0) { return; } if (m_points.size() < 2) { return; } //pr.pEntTiedTo[0] = WORLD_ENTITY; //pr.pEntTiedTo[1] = WORLD_ENTITY; bool bEndsAdjusted = false, bAdjustEnds = m_params.fAnchorRadius > 0.05001f; Vec3 ptend[2] = { m_worldTM* m_points[0], m_worldTM * m_points[m_points.size() - 1] }; primitives::sphere sphPrim; geom_contact* pContacts = 0; sphPrim.center = m_worldTM.TransformPoint(m_points[0]); sphPrim.r = m_params.fAnchorRadius; int collisionEntityTypes = ent_static | ent_sleeping_rigid | ent_rigid | ent_ignore_noncolliding; float d = 0; if (m_params.nFlags & eRope_StaticAttachStart) { pr.pEntTiedTo[0] = WORLD_ENTITY; } else { d = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphPrim.type, &sphPrim, Vec3(0, 0, 0), collisionEntityTypes, &pContacts, 0, geom_colltype0, 0); if (d > 0 && pContacts) { IPhysicalEntity* pBindToEntity = gEnv->pPhysicalWorld->GetPhysicalEntityById(pContacts[0].iPrim[0]); if (pBindToEntity) { pr.pEntTiedTo[0] = pBindToEntity; pr.idPartTiedTo[0] = pContacts[0].iPrim[1]; m_nLinkedEndsMask |= 0x01; if (bAdjustEnds && inrange((pContacts->center - sphPrim.center).len2(), sqr(0.01f), sqr(m_params.fAnchorRadius))) { ptend[0] = pr.ptTiedTo[0] = pContacts->t > 0 ? pContacts->pt : pContacts->center, bEndsAdjusted = true; } } } } if (m_params.nFlags & eRope_StaticAttachEnd) { pr.pEntTiedTo[1] = WORLD_ENTITY; } else { sphPrim.center = m_worldTM.TransformPoint(m_points[m_points.size() - 1]); d = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphPrim.type, &sphPrim, Vec3(0, 0, 0), collisionEntityTypes, &pContacts, 0, geom_colltype0, 0); if (d > 0 && pContacts) { IPhysicalEntity* pBindToEntity = gEnv->pPhysicalWorld->GetPhysicalEntityById(pContacts[0].iPrim[0]); if (pBindToEntity) { pr.pEntTiedTo[1] = pBindToEntity; pr.idPartTiedTo[1] = pContacts[0].iPrim[1]; m_nLinkedEndsMask |= 0x02; if (bAdjustEnds && inrange((pContacts->center - sphPrim.center).len2(), sqr(0.01f), sqr(m_params.fAnchorRadius))) { ptend[1] = pr.ptTiedTo[1] = pContacts->t > 0 ? pContacts->pt : pContacts->center, bEndsAdjusted = true; } } } } if (bEndsAdjusted && !is_unused(pr.length) && inrange((pr.pPoints[0] - pr.pPoints[pr.nSegments]).len2(), sqr(pr.length * 0.999f), sqr(pr.length * 1.001f))) { pr.length = (ptend[0] - ptend[1]).len(); } } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::Dephysicalize(bool bKeepIfReferenced) { // delete old physics if (m_pPhysicalEntity) { gEnv->pPhysicalWorld->DestroyPhysicalEntity(m_pPhysicalEntity); } m_pPhysicalEntity = 0; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::SetMaterial(_smart_ptr<IMaterial> pMat) { if (!pMat) { pMat = m_pMaterial = GetMatMan()->GetDefaultMaterial(); } m_pMaterial = pMat; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::Precache() { } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::GetMemoryUsage(ICrySizer* pSizer) const { SIZER_COMPONENT_NAME(pSizer, "RopeRenderNode"); pSizer->AddObject(this, sizeof(*this)); } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::SyncWithPhysicalRope(bool bForce) { if (m_bStaticPhysics) { m_physicsPoints = m_points; m_WSBBox.Reset(); int numPoints = m_points.size(); for (int i = 0; i < numPoints; i++) { Vec3 point = m_points[i]; m_localBounds.Add(point); point = m_worldTM.TransformPoint(point); m_WSBBox.Add(point); } float r = m_params.fThickness; m_localBounds.min -= Vec3(r, r, r); m_localBounds.max += Vec3(r, r, r); m_WSBBox.min -= Vec3(r, r, r); m_WSBBox.max += Vec3(r, r, r); m_bNeedToReRegister = true; // Bounding box was recalculated, rope needs to be registered again 3d engine. m_bModified = false; return; } ////////////////////////////////////////////////////////////////////////// if (m_pPhysicalEntity) { pe_status_rope sr; sr.lock = 1; if (!m_pPhysicalEntity->GetStatus(&sr)) { return; } sr.lock = -1; int numPoints = sr.nSegments + 1; if (m_params.nFlags & eRope_Subdivide && sr.nVtx != 0) { numPoints = sr.nVtx; } if (numPoints < 2) { m_pPhysicalEntity->GetStatus(&sr); // just to unlock if it was locked return; } m_physicsPoints.resize(numPoints); m_WSBBox.Reset(); m_localBounds.Reset(); if (m_params.nFlags & eRope_Subdivide && sr.nVtx != 0) { sr.pVtx = &m_physicsPoints[0]; } else { sr.pPoints = &m_physicsPoints[0]; } m_pPhysicalEntity->GetStatus(&sr); for (int i = 0; i < numPoints; i++) { Vec3 point = m_physicsPoints[i]; m_WSBBox.Add(point); point = m_InvWorldTM.TransformPoint(point); m_physicsPoints[i] = point; m_localBounds.Add(point); } float r = m_params.fThickness; m_localBounds.min -= Vec3(r, r, r); m_localBounds.max += Vec3(r, r, r); m_WSBBox.min -= Vec3(r, r, r); m_WSBBox.max += Vec3(r, r, r); m_bNeedToReRegister = true; // Bounding box was recalculated, rope needs to be registered again 3d engine. } ////////////////////////////////////////////////////////////////////////// m_bModified = false; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::CreateRenderMesh() { // make new RenderMesh ////////////////////////////////////////////////////////////////////////// m_pRenderMesh = GetRenderer()->CreateRenderMeshInitialized( NULL, 3, eVF_P3F_C4B_T2F, NULL, 3, prtTriangleList, "Rope", GetName(), eRMT_Dynamic, 1, 0, NULL, NULL, false, false); CRenderChunk chunk; if (m_pMaterial) { chunk.m_nMatFlags = m_pMaterial->GetFlags(); } chunk.nFirstIndexId = 0; chunk.nFirstVertId = 0; chunk.nNumIndices = 3; chunk.nNumVerts = 3; chunk.m_texelAreaDensity = 1.0f; chunk.m_vertexFormat = eVF_P3F_C4B_T2F; m_pRenderMesh->SetChunk(0, chunk); } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::UpdateRenderMesh() { FUNCTION_PROFILER_3DENGINE; if (!m_pRenderMesh) { CreateRenderMesh(); if (!m_pRenderMesh) { return; } } if (m_physicsPoints.size() < 2 && !m_bStaticPhysics) { return; } if (m_points.size() < 2) { return; } SyncWithPhysicalRope(false); ////////////////////////////////////////////////////////////////////////// // Tesselate. ////////////////////////////////////////////////////////////////////////// int numSplinePoints = m_physicsPoints.size(); m_spline.resize(numSplinePoints); for (int pnt = 0; pnt < numSplinePoints; pnt++) { m_spline.key(pnt).flags = 0; m_spline.time(pnt) = aznumeric_caster(pnt); m_spline.value(pnt) = m_physicsPoints[pnt]; } m_spline.SetRange(0, aznumeric_caster(m_physicsPoints.size() - 1)); int nLengthSamples = m_params.nNumSegments + 1; if (!(m_params.nFlags & IRopeRenderNode::eRope_Smooth)) { nLengthSamples = m_params.nPhysSegments + 1; } Vec2 vTexMin(0, 0); Vec2 vTexMax(m_params.fTextureTileU, m_params.fTextureTileV); CRopeSurfaceCache::GetLocalCache getCache; TubeSurface& tubeSurf = getCache.pCache->tubeSurface; tubeSurf.m_worldTM = m_worldTM; //tubeSurf.m_worldTM.SetIdentity(); tubeSurf.GenerateSurface(&m_spline, m_params.fThickness, false, Vec3(0, 0, 1), nLengthSamples, m_params.nNumSides, true, false, false, false, &vTexMin, &vTexMax); int nNewVertexCount = tubeSurf.iVQuantity; // Resize vertex buffer. m_pRenderMesh->LockForThreadAccess(); m_pRenderMesh->UpdateVertices(NULL, nNewVertexCount, 0, VSF_GENERAL, 0u); int nPosStride = 0; int nUVStride = 0; int nTangsStride = 0; byte* pVertPos = m_pRenderMesh->GetPosPtr(nPosStride, FSL_VIDEO_CREATE); if (!pVertPos) { return; } byte* pVertTexUV = m_pRenderMesh->GetUVPtr(nUVStride, FSL_VIDEO_CREATE); byte* pTangents = m_pRenderMesh->GetTangentPtr(nTangsStride, FSL_VIDEO_CREATE); for (int i = 0; i < nNewVertexCount; i++) { Vec3 vP = Vec3(tubeSurf.m_akVertex[i].x, tubeSurf.m_akVertex[i].y, tubeSurf.m_akVertex[i].z); Vec2 vT = Vec2(tubeSurf.m_akTexture[i].x, tubeSurf.m_akTexture[i].y); *((Vec3*)pVertPos) = vP; *((Vec2*)pVertTexUV) = vT; *(SPipTangents*)pTangents = SPipTangents( tubeSurf.m_akTangents [i], tubeSurf.m_akBitangents[i], 1); pVertPos += nPosStride; pVertTexUV += nUVStride; pTangents += nTangsStride; } m_pRenderMesh->UnlockStream(VSF_GENERAL); m_pRenderMesh->UnlockStream(VSF_TANGENTS); m_pRenderMesh->UpdateIndices(tubeSurf.m_pIndices, tubeSurf.iNumIndices, 0, 0u); // Update chunk params. CRenderChunk* pChunk = &m_pRenderMesh->GetChunks()[0]; if (m_pMaterial) { pChunk->m_nMatFlags = m_pMaterial->GetFlags(); } pChunk->nNumIndices = tubeSurf.iNumIndices; pChunk->nNumVerts = tubeSurf.iVQuantity; m_pRenderMesh->SetChunk(0, *pChunk); m_pRenderMesh->UnLockForThreadAccess(); } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::SetParams(const SRopeParams& params) { m_params = params; (m_dwRndFlags &= ~(ERF_CASTSHADOWMAPS | ERF_HAS_CASTSHADOWMAPS)) |= -(params.nFlags & eRope_CastShadows) >> 31 & (ERF_CASTSHADOWMAPS | ERF_HAS_CASTSHADOWMAPS); } ////////////////////////////////////////////////////////////////////////// const CRopeRenderNode::SRopeParams& CRopeRenderNode::GetParams() const { return m_params; } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::SetPoints(const Vec3* pPoints, int nCount) { m_points.resize(nCount); m_physicsPoints.resize(nCount); if (nCount > 0) { m_localBounds.Reset(); for (int i = 0; i < nCount; i++) { m_points[i] = pPoints[i]; m_physicsPoints[i] = pPoints[i]; m_localBounds.Add(pPoints[i]); } } float r = m_params.fThickness; m_localBounds.min -= Vec3(r, r, r); m_localBounds.max += Vec3(r, r, r); m_WSBBox.SetTransformedAABB(m_worldTM, m_localBounds); Get3DEngine()->RegisterEntity(this); m_bNeedToReRegister = false; Physicalize(); m_bModified = true; if (m_pPhysicalEntity) { // Awake on first render after modification. //pe_action_awake pa; //pa.bAwake = 1; //m_pPhysicalEntity->Action( &pa ); } } ////////////////////////////////////////////////////////////////////////// int CRopeRenderNode::GetPointsCount() const { return (int)m_points.size(); } ////////////////////////////////////////////////////////////////////////// const Vec3* CRopeRenderNode::GetPoints() const { if (!m_points.empty()) { return &m_points[0]; } else { return 0; } } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::ResetPoints() { m_physicsPoints = m_points; Physicalize(); m_bModified = true; } void CRopeRenderNode::OnPhysicsPostStep() { // Re-register entity. if (m_bNeedToReRegister) { pe_params_bbox pbb; m_pPhysicalEntity->GetParams(&pbb); m_WSBBox = AABB(pbb.BBox[0], pbb.BBox[1]); Get3DEngine()->RegisterEntity(this); } m_bNeedToReRegister = false; m_bModified = true; // Update the sound data UpdateSound(); } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::UpdateSound() { } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::SetRopeSound(char const* const pcSoundName, int unsigned const nSegmentToAttachTo, float const fOffset) { } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::StopRopeSound() { } ////////////////////////////////////////////////////////////////////////// void CRopeRenderNode::ResetRopeSound() { } void CRopeRenderNode::OffsetPosition(const Vec3& delta) { if (m_pRNTmpData) { m_pRNTmpData->OffsetPosition(delta); } m_pos += delta; m_worldTM.SetTranslation(m_pos); m_InvWorldTM = m_worldTM.GetInverted(); m_WSBBox.Move(delta); if (m_pPhysicalEntity) { pe_status_pos status_pos; m_pPhysicalEntity->GetStatus(&status_pos); pe_params_pos par_pos; par_pos.pos = status_pos.pos + delta; par_pos.bRecalcBounds = 3; m_pPhysicalEntity->SetParams(&par_pos); } } #endif // ENABLE_CRY_PHYSICS