Refactor Physics to separate PhysicsBackend

#673
This commit is contained in:
Wojtek Figat
2022-01-20 23:28:43 +01:00
parent cf1af53ab8
commit 427846f73b
66 changed files with 4803 additions and 4327 deletions

View File

@@ -7,13 +7,12 @@
#include "Engine/Level/Scene/SceneRendering.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include <ThirdParty/PhysX/PxFiltering.h>
Terrain::Terrain(const SpawnParams& params)
: PhysicsColliderActor(params)
@@ -69,13 +68,11 @@ void Terrain::UpdateLayerBits()
if (_patches.IsEmpty())
return;
PxFilterData filterData;
// Own layer ID
filterData.word0 = GetLayerMask();
const uint32 mask0 = GetLayerMask();
// Own layer mask
filterData.word1 = Physics::LayerMasks[GetLayer()];
const uint32 mask1 = Physics::LayerMasks[GetLayer()];
// Update the shapes layer bits
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
@@ -83,8 +80,7 @@ void Terrain::UpdateLayerBits()
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->_physicsShape->setSimulationFilterData(filterData);
patch->_physicsShape->setQueryFilterData(filterData);
PhysicsBackend::SetShapeFilterMask(patch->_physicsShape, mask0, mask1);
}
}
}
@@ -170,7 +166,6 @@ bool Terrain::RayCast(const Vector3& origin, const Vector3& direction, RayCastHi
bool result = false;
RayCastHit tmpHit;
const Ray ray(origin, direction);
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
@@ -184,7 +179,6 @@ bool Terrain::RayCast(const Vector3& origin, const Vector3& direction, RayCastHi
result = true;
}
}
return result;
}
@@ -192,13 +186,12 @@ void Terrain::ClosestPoint(const Vector3& position, Vector3& result) const
{
float minDistance = MAX_float;
Vector3 tmp;
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->ClosestPoint(position, &tmp);
patch->ClosestPoint(position, tmp);
const auto distance = Vector3::DistanceSquared(position, tmp);
if (distance < minDistance)
{
@@ -237,22 +230,13 @@ void Terrain::OnPhysicalMaterialChanged()
if (_patches.IsEmpty())
return;
PxMaterial* material = Physics::GetDefaultMaterial();
if (PhysicalMaterial)
{
if (!PhysicalMaterial->WaitForLoaded())
{
material = ((::PhysicalMaterial*)PhysicalMaterial->Instance)->GetPhysXMaterial();
}
}
// Update the shapes material
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->_physicsShape->setMaterials(&material, 1);
PhysicsBackend::SetShapeMaterial(patch->_physicsShape, PhysicalMaterial.Get());
}
}
}
@@ -819,13 +803,12 @@ void Terrain::OnActiveInTreeChanged()
Actor::OnActiveInTreeChanged();
// Update physics
const PxShapeFlags shapeFlags = GetShapeFlags(false, IsActiveInHierarchy());
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->_physicsShape->setFlags(shapeFlags);
PhysicsBackend::SetShapeState(patch->_physicsShape, IsActiveInHierarchy(), false);
}
}
}

View File

@@ -5,11 +5,12 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/CollisionCooking.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Level.h"
#include "Engine/Graphics/Async/GPUTask.h"
#include "Engine/Threading/Threading.h"
#if TERRAIN_EDITING
@@ -19,6 +20,7 @@
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Engine/ContentImporters/AssetsImportingManager.h"
@@ -27,21 +29,10 @@
#if USE_EDITOR
#include "Engine/Debug/DebugDraw.h"
#endif
#include "Engine/Physics/CollisionCooking.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/RawDataAsset.h"
#include "Engine/Level/Level.h"
#include <extensions/PxDefaultStreams.h>
#include <extensions/PxShapeExt.h>
#include <foundation/PxTransform.h>
#include <geometry/PxHeightField.h>
#include <geometry/PxHeightFieldDesc.h>
#include <geometry/PxHeightFieldSample.h>
#include <cooking/PxCooking.h>
#include <PxRigidStatic.h>
#include <PxPhysics.h>
#define TERRAIN_PATCH_COLLISION_QUANTIZATION ((PxReal)0x7fff)
#define TERRAIN_PATCH_COLLISION_QUANTIZATION ((float)0x7fff)
struct TerrainCollisionDataHeader
{
@@ -128,8 +119,7 @@ void TerrainPatch::UpdateTransform()
if (_physicsActor)
{
const Transform& terrainTransform = _terrain->_transform;
const PxTransform trans(C2P(terrainTransform.LocalToWorld(_offset)), C2P(terrainTransform.Orientation));
_physicsActor->setGlobalPose(trans);
PhysicsBackend::SetRigidActorPose(_physicsActor, terrainTransform.LocalToWorld(_offset), terrainTransform.Orientation);
}
// Update chunks cache
@@ -565,10 +555,10 @@ bool CookCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* ini
const int32 heightFieldChunkSize = ((info.ChunkSize + 1) >> collisionLOD) - 1;
const int32 heightFieldSize = heightFieldChunkSize * TerrainPatch::CHUNKS_COUNT_EDGE + 1;
const int32 heightFieldLength = heightFieldSize * heightFieldSize;
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, heightFieldLength, PxHeightFieldSample);
PxHeightFieldSample sample;
Platform::MemoryClear(&sample, sizeof(PxHeightFieldSample));
Platform::MemoryClear(heightFieldData, sizeof(PxHeightFieldSample) * heightFieldLength);
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, heightFieldLength, PhysicsBackend::HeightFieldSample);
PhysicsBackend::HeightFieldSample sample;
Platform::MemoryClear(&sample, sizeof(PhysicsBackend::HeightFieldSample));
Platform::MemoryClear(heightFieldData, sizeof(PhysicsBackend::HeightFieldSample) * heightFieldLength);
// Setup terrain collision information
auto& mip = initData->Mips[collisionLOD];
@@ -598,47 +588,38 @@ bool CookCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* ini
const int32 heightmapZ = chunkStartZ + z;
const int32 dstIndex = (heightmapX * heightFieldSize) + heightmapZ;
sample.height = PxI16(TERRAIN_PATCH_COLLISION_QUANTIZATION * normalizedHeight);
sample.materialIndex0 = sample.materialIndex1 = isHole ? PxHeightFieldMaterial::eHOLE : 0;
sample.Height = int16(TERRAIN_PATCH_COLLISION_QUANTIZATION * normalizedHeight);
sample.MaterialIndex0 = sample.MaterialIndex1 = isHole ? (uint8)PhysicsBackend::HeightFieldMaterial::Hole : 0;
heightFieldData[dstIndex] = sample;
}
}
}
}
PxHeightFieldDesc heightFieldDesc;
heightFieldDesc.format = PxHeightFieldFormat::eS16_TM;
heightFieldDesc.flags = PxHeightFieldFlag::eNO_BOUNDARY_EDGES;
heightFieldDesc.nbColumns = heightFieldSize;
heightFieldDesc.nbRows = heightFieldSize;
heightFieldDesc.samples.data = heightFieldData;
heightFieldDesc.samples.stride = sizeof(PxHeightFieldSample);
// Cook height field
PxDefaultMemoryOutputStream outputStream;
if (CollisionCooking::CookHeightField(heightFieldDesc, outputStream))
MemoryWriteStream outputStream;
if (CollisionCooking::CookHeightField(heightFieldSize, heightFieldSize, heightFieldData, outputStream))
{
return true;
}
// Write results
collisionData->Resize(sizeof(TerrainCollisionDataHeader) + outputStream.getSize(), false);
collisionData->Resize(sizeof(TerrainCollisionDataHeader) + outputStream.GetPosition(), false);
const auto header = (TerrainCollisionDataHeader*)collisionData->Get();
header->LOD = collisionLOD;
header->ScaleXZ = (float)info.HeightmapSize / heightFieldSize;
Platform::MemoryCopy(collisionData->Get() + sizeof(TerrainCollisionDataHeader), outputStream.getData(), outputStream.getSize());
Platform::MemoryCopy(collisionData->Get() + sizeof(TerrainCollisionDataHeader), outputStream.GetHandle(), outputStream.GetPosition());
return false;
#else
LOG(Warning, "Collision cooking is disabled.");
return true;
LOG(Warning, "Collision cooking is disabled.");
return true;
#endif
}
bool ModifyCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* initData, int32 collisionLod, const Int2& modifiedOffset, const Int2& modifiedSize, PxHeightField* heightField)
bool ModifyCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* initData, int32 collisionLod, const Int2& modifiedOffset, const Int2& modifiedSize, void* heightField)
{
PROFILE_CPU_NAMED("Terrain.ModifyCollision");
@@ -658,10 +639,10 @@ bool ModifyCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* i
// Allocate data
const int32 heightFieldDataLength = samplesSize.X * samplesSize.Y;
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, info.HeightmapLength, PxHeightFieldSample);
PxHeightFieldSample sample;
Platform::MemoryClear(&sample, sizeof(PxHeightFieldSample));
Platform::MemoryClear(heightFieldData, sizeof(PxHeightFieldSample) * heightFieldDataLength);
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, info.HeightmapLength, PhysicsBackend::HeightFieldSample);
PhysicsBackend::HeightFieldSample sample;
Platform::MemoryClear(&sample, sizeof(PhysicsBackend::HeightFieldSample));
Platform::MemoryClear(heightFieldData, sizeof(PhysicsBackend::HeightFieldSample) * heightFieldDataLength);
// Setup terrain collision information
auto& mip = initData->Mips[collisionLOD];
@@ -711,24 +692,17 @@ bool ModifyCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* i
const int32 dstIndex = (heightmapLocalX * samplesSize.Y) + heightmapLocalZ;
sample.height = PxI16(TERRAIN_PATCH_COLLISION_QUANTIZATION * normalizedHeight);
sample.materialIndex0 = sample.materialIndex1 = isHole ? PxHeightFieldMaterial::eHOLE : 0;
sample.Height = int16(TERRAIN_PATCH_COLLISION_QUANTIZATION * normalizedHeight);
sample.MaterialIndex0 = sample.MaterialIndex1 = isHole ? (uint8)PhysicsBackend::HeightFieldMaterial::Hole : 0;
heightFieldData[dstIndex] = sample;
}
}
}
}
PxHeightFieldDesc heightFieldDesc;
heightFieldDesc.format = PxHeightFieldFormat::eS16_TM;
heightFieldDesc.flags = PxHeightFieldFlag::eNO_BOUNDARY_EDGES;
heightFieldDesc.nbColumns = samplesSize.Y;
heightFieldDesc.nbRows = samplesSize.X;
heightFieldDesc.samples.data = heightFieldData;
heightFieldDesc.samples.stride = sizeof(PxHeightFieldSample);
// Update height field range
if (!heightField->modifySamples(samplesOffset.Y, samplesOffset.X, heightFieldDesc, true))
if (PhysicsBackend::ModifyHeightField(heightField, samplesOffset.Y, samplesOffset.X, samplesSize.Y, samplesSize.X, heightFieldData))
{
LOG(Warning, "Height Field collision modification failed.");
return true;
@@ -1958,7 +1932,7 @@ bool TerrainPatch::UpdateCollision()
_collisionVertices.Resize(0);
// Recreate height field
_terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
PhysicsBackend::DestroyObject(_physicsHeightField);
_physicsHeightField = nullptr;
if (CreateHeightField())
{
@@ -1981,42 +1955,26 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
{
if (_physicsShape == nullptr)
return false;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
const PxHitFlags hitFlags = (PxHitFlags)0;
// Perform raycast test
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _physicsShape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) != 0)
{
resultHitDistance = hit.distance;
return true;
}
return false;
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
return PhysicsBackend::RayCastShape(_physicsShape, shapePos, shapeRot, origin, direction, resultHitDistance, maxDistance);
}
bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, Vector3& resultHitNormal, float maxDistance) const
{
if (_physicsShape == nullptr)
return false;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
const PxHitFlags hitFlags = PxHitFlag::eNORMAL;
// Perform raycast test
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _physicsShape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) != 0)
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
RayCastHit hit;
if (PhysicsBackend::RayCastShape(_physicsShape, shapePos, shapeRot, origin, direction, hit, maxDistance))
{
resultHitDistance = hit.distance;
resultHitNormal = P2C(hit.normal);
resultHitDistance = hit.Distance;
resultHitNormal = hit.Normal;
return true;
}
return false;
}
@@ -2024,19 +1982,17 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
{
if (_physicsShape == nullptr)
return false;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
const PxHitFlags hitFlags = (PxHitFlags)0;
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
// Perform raycast test
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _physicsShape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) != 0)
float hitDistance;
if (PhysicsBackend::RayCastShape(_physicsShape, shapePos, shapeRot, origin, direction, hitDistance, maxDistance))
{
// Find hit chunk
resultChunk = nullptr;
const auto hitPoint = origin + direction * hit.distance;
const auto hitPoint = origin + direction * hitDistance;
for (int32 chunkIndex = 0; chunkIndex < CHUNKS_COUNT; chunkIndex++)
{
const auto box = Chunks[chunkIndex]._bounds;
@@ -2052,7 +2008,7 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
if (resultChunk == nullptr)
return false;
resultHitDistance = hit.distance;
resultHitDistance = hitDistance;
return true;
}
@@ -2063,44 +2019,28 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, RayC
{
if (_physicsShape == nullptr)
return false;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
const PxHitFlags hitFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eUV;
PxRaycastHit hit;
// Perform raycast test
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _physicsShape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) == 0)
return false;
// Gather results
hitInfo.Gather(hit);
return true;
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
return PhysicsBackend::RayCastShape(_physicsShape, shapePos, shapeRot, origin, direction, hitInfo, maxDistance);
}
void TerrainPatch::ClosestPoint(const Vector3& position, Vector3* result) const
void TerrainPatch::ClosestPoint(const Vector3& position, Vector3& result) const
{
if (_physicsShape == nullptr)
{
result = Vector3::Maximum;
return;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
PxVec3 closestPoint;
// Compute distance between a point and a geometry object
const float distanceSqr = PxGeometryQuery::pointDistance(C2P(position), _physicsShape->getGeometry().any(), trans, &closestPoint);
}
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
Vector3 closestPoint;
const float distanceSqr = PhysicsBackend::ComputeShapeSqrDistanceToPoint(_physicsShape, shapePos, shapeRot, position, &closestPoint);
if (distanceSqr > 0.0f)
{
// Use calculated point
*result = P2C(closestPoint);
}
result = closestPoint;
else
{
// Fallback to the input location
*result = position;
}
result = position;
}
#if USE_EDITOR
@@ -2130,7 +2070,7 @@ void TerrainPatch::UpdatePostManualDeserialization()
_collisionVertices.Resize(0);
// Recreate height field
_terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
PhysicsBackend::DestroyObject(_physicsHeightField);
_physicsHeightField = nullptr;
if (CreateHeightField())
{
@@ -2152,45 +2092,27 @@ void TerrainPatch::UpdatePostManualDeserialization()
void TerrainPatch::CreateCollision()
{
ASSERT(!HasCollision());
if (CreateHeightField())
return;
ASSERT(_physicsHeightField);
// Create geometry
const Transform terrainTransform = _terrain->_transform;
PxHeightFieldGeometry geometry;
geometry.heightField = _physicsHeightField;
geometry.rowScale = Math::Max(Math::Abs(terrainTransform.Scale.X) * _collisionScaleXZ, PX_MIN_HEIGHTFIELD_XZ_SCALE);
geometry.heightScale = Math::Max(Math::Abs(terrainTransform.Scale.Y) * _yHeight / TERRAIN_PATCH_COLLISION_QUANTIZATION, PX_MIN_HEIGHTFIELD_Y_SCALE);
geometry.columnScale = Math::Max(Math::Abs(terrainTransform.Scale.Z) * _collisionScaleXZ, PX_MIN_HEIGHTFIELD_XZ_SCALE);
// Prepare
const PxShapeFlags shapeFlags = GetShapeFlags(false, _terrain->IsActiveInHierarchy());
PxMaterial* material = Physics::GetDefaultMaterial();
if (_terrain->PhysicalMaterial)
{
if (!_terrain->PhysicalMaterial->WaitForLoaded())
{
material = ((::PhysicalMaterial*)_terrain->PhysicalMaterial->Instance)->GetPhysXMaterial();
}
}
CollisionShape shape;
const float rowScale = Math::Abs(terrainTransform.Scale.X) * _collisionScaleXZ;
const float heightScale = Math::Abs(terrainTransform.Scale.Y) * _yHeight / TERRAIN_PATCH_COLLISION_QUANTIZATION;
const float columnScale = Math::Abs(terrainTransform.Scale.Z) * _collisionScaleXZ;
shape.SetHeightField(_physicsHeightField, heightScale, rowScale, columnScale);
// Create shape
_physicsShape = CPhysX->createShape(geometry, *material, true, shapeFlags);
ASSERT(_physicsShape);
_physicsShape->userData = _terrain;
_physicsShape->setLocalPose(PxTransform(0, _yOffset * terrainTransform.Scale.Y, 0));
_physicsShape = PhysicsBackend::CreateShape(_terrain, shape, _terrain->PhysicalMaterial.Get(), _terrain->IsActiveInHierarchy(), false);
PhysicsBackend::SetShapeLocalPose(_physicsShape, Vector3(0, _yOffset * terrainTransform.Scale.Y, 0), Quaternion::Identity);
// Create static actor
const PxTransform trans(C2P(terrainTransform.LocalToWorld(_offset)), C2P(terrainTransform.Orientation));
_physicsActor = CPhysX->createRigidStatic(trans);
ASSERT(_physicsActor);
_physicsActor->userData = _terrain;
#if WITH_PVD
_physicsActor->setActorFlag(PxActorFlag::eVISUALIZATION, true);
#endif
_physicsActor->attachShape(*_physicsShape);
_physicsActor = PhysicsBackend::CreateRigidStaticActor(nullptr, terrainTransform.LocalToWorld(_offset), terrainTransform.Orientation);
PhysicsBackend::AttachShape(_physicsShape, _physicsActor);
void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::AddSceneActor(scene, _physicsActor);
}
bool TerrainPatch::CreateHeightField()
@@ -2208,9 +2130,7 @@ bool TerrainPatch::CreateHeightField()
const auto collisionHeader = (TerrainCollisionDataHeader*)_heightfield->Data.Get();
_collisionScaleXZ = collisionHeader->ScaleXZ * TERRAIN_UNITS_PER_VERTEX;
PxDefaultMemoryInputData heightFieldData(_heightfield->Data.Get() + sizeof(TerrainCollisionDataHeader), _heightfield->Data.Count() - sizeof(TerrainCollisionDataHeader));
_physicsHeightField = CPhysX->createHeightField(heightFieldData);
_physicsHeightField = PhysicsBackend::CreateHeightField(_heightfield->Data.Get() + sizeof(TerrainCollisionDataHeader), _heightfield->Data.Count() - sizeof(TerrainCollisionDataHeader));
if (_physicsHeightField == nullptr)
{
LOG(Error, "Failed to create terrain collision height field.");
@@ -2226,15 +2146,15 @@ void TerrainPatch::UpdateCollisionScale() const
// Create geometry
const Transform terrainTransform = _terrain->_transform;
PxHeightFieldGeometry geometry;
geometry.heightField = _physicsHeightField;
geometry.rowScale = Math::Max(Math::Abs(terrainTransform.Scale.X) * _collisionScaleXZ, PX_MIN_HEIGHTFIELD_XZ_SCALE);
geometry.heightScale = Math::Max(Math::Abs(terrainTransform.Scale.Y) * _yHeight / TERRAIN_PATCH_COLLISION_QUANTIZATION, PX_MIN_HEIGHTFIELD_Y_SCALE);
geometry.columnScale = Math::Max(Math::Abs(terrainTransform.Scale.Z) * _collisionScaleXZ, PX_MIN_HEIGHTFIELD_XZ_SCALE);
CollisionShape geometry;
const float rowScale = Math::Abs(terrainTransform.Scale.X) * _collisionScaleXZ;
const float heightScale = Math::Abs(terrainTransform.Scale.Y) * _yHeight / TERRAIN_PATCH_COLLISION_QUANTIZATION;
const float columnScale = Math::Abs(terrainTransform.Scale.Z) * _collisionScaleXZ;
geometry.SetHeightField(_physicsHeightField, heightScale, rowScale, columnScale);
// Update shape
_physicsShape->setGeometry(geometry);
_physicsShape->setLocalPose(PxTransform(0, _yOffset * terrainTransform.Scale.Y, 0));
PhysicsBackend::SetShapeGeometry(_physicsShape, geometry);
PhysicsBackend::SetShapeLocalPose(_physicsShape, Vector3(0, _yOffset * terrainTransform.Scale.Y, 0), Quaternion::Identity);
}
void TerrainPatch::DestroyCollision()
@@ -2242,10 +2162,12 @@ void TerrainPatch::DestroyCollision()
ScopeLock lock(_collisionLocker);
ASSERT(HasCollision());
_terrain->GetPhysicsScene()->RemoveCollider(_terrain);
_terrain->GetPhysicsScene()->RemoveActor(_physicsActor);
_terrain->GetPhysicsScene()->RemoveObject(_physicsShape);
_terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::RemoveCollider(_terrain);
PhysicsBackend::RemoveSceneActor(scene, _physicsActor);
PhysicsBackend::DestroyActor(_physicsActor);
PhysicsBackend::DestroyShape(_physicsShape);
PhysicsBackend::DestroyObject(_physicsHeightField);
_physicsActor = nullptr;
_physicsShape = nullptr;
@@ -2265,17 +2187,17 @@ void TerrainPatch::CacheDebugLines()
{
ASSERT(_debugLines.IsEmpty() && _physicsHeightField);
const uint32 rows = _physicsHeightField->getNbRows();
const uint32 cols = _physicsHeightField->getNbColumns();
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
_debugLines.Resize((rows - 1) * (cols - 1) * 6 + (cols + rows - 2) * 2);
Vector3* data = _debugLines.Get();
#define GET_VERTEX(x, y) const Vector3 v##x##y((float)(row + (x)), _physicsHeightField->getHeight((PxReal)(row + (x)), (PxReal)(col + (y))) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y)))
#define GET_VERTEX(x, y) const Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, (float)(row + (x)), (float)(col + (y))) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y)))
for (uint32 row = 0; row < rows - 1; row++)
for (int32 row = 0; row < rows - 1; row++)
{
for (uint32 col = 0; col < cols - 1; col++)
for (int32 col = 0; col < cols - 1; col++)
{
GET_VERTEX(0, 0);
GET_VERTEX(0, 1);
@@ -2293,9 +2215,9 @@ void TerrainPatch::CacheDebugLines()
}
}
for (uint32 row = 0; row < rows - 1; row++)
for (int32 row = 0; row < rows - 1; row++)
{
const uint32 col = cols - 1;
const int32 col = cols - 1;
GET_VERTEX(0, 0);
GET_VERTEX(1, 0);
@@ -2303,9 +2225,9 @@ void TerrainPatch::CacheDebugLines()
*data++ = v10;
}
for (uint32 col = 0; col < cols - 1; col++)
for (int32 col = 0; col < cols - 1; col++)
{
const uint32 row = rows - 1;
const int32 row = rows - 1;
GET_VERTEX(0, 0);
GET_VERTEX(0, 1);
@@ -2352,22 +2274,22 @@ const Array<Vector3>& TerrainPatch::GetCollisionTriangles()
if (!_physicsShape || _collisionTriangles.HasItems())
return _collisionTriangles;
const uint32 rows = _physicsHeightField->getNbRows();
const uint32 cols = _physicsHeightField->getNbColumns();
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
_collisionTriangles.Resize((rows - 1) * (cols - 1) * 6);
Vector3* data = _collisionTriangles.Get();
#define GET_VERTEX(x, y) Vector3 v##x##y((float)(row + (x)), _physicsHeightField->getHeight((PxReal)(row + (x)), (PxReal)(col + (y))) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))); Vector3::Transform(v##x##y, world, v##x##y)
#define GET_VERTEX(x, y) Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, (float)(row + (x)), (float)(col + (y))) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))); Vector3::Transform(v##x##y, world, v##x##y)
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * CHUNKS_COUNT_EDGE;
const Transform terrainTransform = _terrain->_transform;
Transform localTransform(Vector3(_x * size, _yOffset, _z * size), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
for (uint32 row = 0; row < rows - 1; row++)
for (int32 row = 0; row < rows - 1; row++)
{
for (uint32 col = 0; col < cols - 1; col++)
for (int32 col = 0; col < cols - 1; col++)
{
GET_VERTEX(0, 0);
GET_VERTEX(0, 1);
@@ -2394,9 +2316,8 @@ void TerrainPatch::GetCollisionTriangles(const BoundingSphere& bounds, Array<Vec
result.Clear();
// Skip if no intersection with patch
if (!CollisionsHelper::BoxIntersectsSphere(GetBounds(), bounds))
if (!CollisionsHelper::BoxIntersectsSphere(GetBounds(), bounds) || !_physicsHeightField)
return;
CHECK(_physicsHeightField);
// Prepare
const auto& triangles = GetCollisionTriangles();
@@ -2424,8 +2345,8 @@ void TerrainPatch::GetCollisionTriangles(const BoundingSphere& bounds, Array<Vec
}
// Normalize bounds and map to actual triangles buffer
const int32 rows = _physicsHeightField->getNbRows();
const int32 cols = _physicsHeightField->getNbColumns();
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
int32 startRow = Math::FloorToInt(min.X / size * rows);
int32 startCol = Math::FloorToInt(min.Z / size * cols);
int32 endRow = Math::CeilToInt(max.X / size * rows);
@@ -2495,8 +2416,8 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Vector3>& vertexBuffer, Array<
if (!_physicsShape)
return;
const uint32 rows = _physicsHeightField->getNbRows();
const uint32 cols = _physicsHeightField->getNbColumns();
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
// Cache pre-transformed collision heightfield vertices locations
if (_collisionVertices.IsEmpty())
@@ -2513,11 +2434,11 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Vector3>& vertexBuffer, Array<
const int32 vertexCount = rows * cols;
_collisionVertices.Resize(vertexCount);
Vector3* vb = _collisionVertices.Get();
for (uint32 row = 0; row < rows; row++)
for (int32 row = 0; row < rows; row++)
{
for (uint32 col = 0; col < cols; col++)
for (int32 col = 0; col < cols; col++)
{
Vector3 v((float)row, _physicsHeightField->getHeight((PxReal)row, (PxReal)col) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)col);
Vector3 v((float)row, PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, (float)row, (float)col) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)col);
Vector3::Transform(v, world, v);
*vb++ = v;
}
@@ -2532,9 +2453,9 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Vector3>& vertexBuffer, Array<
const int32 indexCount = (rows - 1) * (cols - 1) * 6;
indexBuffer.Resize(indexCount);
int32* ib = indexBuffer.Get();
for (uint32 row = 0; row < rows - 1; row++)
for (int32 row = 0; row < rows - 1; row++)
{
for (uint32 col = 0; col < cols - 1; col++)
for (int32 col = 0; col < cols - 1; col++)
{
#define GET_INDEX(x, y) *ib++ = (col + (y)) + (row + (x)) * cols
@@ -2612,6 +2533,7 @@ void TerrainPatch::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
void TerrainPatch::OnPhysicsSceneChanged(PhysicsScene* previous)
{
previous->UnlinkActor(_physicsActor);
_terrain->GetPhysicsScene()->AddActor(_physicsActor);
PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _physicsActor);
void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::AddSceneActor(scene, _physicsActor);
}

View File

@@ -6,7 +6,6 @@
#include "TerrainChunk.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Int2.h"
#include "Engine/Physics/Types.h"
#include "Engine/Level/Scene/Lightmap.h"
#include "Engine/Content/Assets/RawDataAsset.h"
@@ -38,9 +37,9 @@ private:
BoundingBox _bounds;
Vector3 _offset;
AssetReference<RawDataAsset> _heightfield;
PxShape* _physicsShape;
PxRigidStatic* _physicsActor;
PxHeightField* _physicsHeightField;
void* _physicsShape;
void* _physicsActor;
void* _physicsHeightField;
CriticalSection _collisionLocker;
float _collisionScaleXZ;
#if TERRAIN_UPDATING
@@ -331,7 +330,7 @@ public:
/// </summary>
/// <param name="position">The position to find the closest point to it.</param>
/// <param name="result">The result point on the collider that is closest to the specified location.</param>
void ClosestPoint(const Vector3& position, Vector3* result) const;
void ClosestPoint(const Vector3& position, Vector3& result) const;
#if USE_EDITOR
@@ -373,7 +372,6 @@ private:
/// <summary>
/// Determines whether this patch has created collision representation.
/// </summary>
/// <returns><c>true</c> if this patch has collider; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool HasCollision() const
{
return _physicsShape != nullptr;