simplified mesh collision loading for play mode, thanks physX 😭
This commit is contained in:
@@ -1419,7 +1419,6 @@ void Engine::setParent(int childId, int parentId) {
|
|||||||
if (projectManager.currentProject.isLoaded) {
|
if (projectManager.currentProject.isLoaded) {
|
||||||
projectManager.currentProject.hasUnsavedChanges = true;
|
projectManager.currentProject.hasUnsavedChanges = true;
|
||||||
}
|
}
|
||||||
logToConsole("Reparented object");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::addConsoleMessage(const std::string& message, ConsoleMessageType type) {
|
void Engine::addConsoleMessage(const std::string& message, ConsoleMessageType type) {
|
||||||
|
|||||||
@@ -173,6 +173,13 @@ ModelLoadResult ModelLoader::loadModel(const std::string& filepath) {
|
|||||||
loaded.boundsMin = raw.boundsMin;
|
loaded.boundsMin = raw.boundsMin;
|
||||||
loaded.boundsMax = raw.boundsMax;
|
loaded.boundsMax = raw.boundsMax;
|
||||||
loaded.triangleVertices = std::move(triPositions);
|
loaded.triangleVertices = std::move(triPositions);
|
||||||
|
loaded.positions = raw.positions;
|
||||||
|
loaded.triangleIndices.reserve(raw.faces.size() * 3);
|
||||||
|
for (const auto& face : raw.faces) {
|
||||||
|
loaded.triangleIndices.push_back(face.x);
|
||||||
|
loaded.triangleIndices.push_back(face.y);
|
||||||
|
loaded.triangleIndices.push_back(face.z);
|
||||||
|
}
|
||||||
|
|
||||||
loadedMeshes.push_back(std::move(loaded));
|
loadedMeshes.push_back(std::move(loaded));
|
||||||
|
|
||||||
@@ -209,6 +216,8 @@ ModelLoadResult ModelLoader::loadModel(const std::string& filepath) {
|
|||||||
glm::vec3 boundsMin(FLT_MAX);
|
glm::vec3 boundsMin(FLT_MAX);
|
||||||
glm::vec3 boundsMax(-FLT_MAX);
|
glm::vec3 boundsMax(-FLT_MAX);
|
||||||
std::vector<glm::vec3> triPositions;
|
std::vector<glm::vec3> triPositions;
|
||||||
|
std::vector<glm::vec3> positions;
|
||||||
|
std::vector<uint32_t> indices;
|
||||||
|
|
||||||
// Process all meshes in the scene
|
// Process all meshes in the scene
|
||||||
std::vector<float> vertices;
|
std::vector<float> vertices;
|
||||||
@@ -218,7 +227,7 @@ ModelLoadResult ModelLoader::loadModel(const std::string& filepath) {
|
|||||||
result.hasTangents = false;
|
result.hasTangents = false;
|
||||||
|
|
||||||
// Process the root node recursively
|
// Process the root node recursively
|
||||||
processNode(scene->mRootNode, scene, aiMatrix4x4(), vertices, triPositions, boundsMin, boundsMax);
|
processNode(scene->mRootNode, scene, aiMatrix4x4(), vertices, triPositions, positions, indices, boundsMin, boundsMax);
|
||||||
|
|
||||||
// Check mesh properties
|
// Check mesh properties
|
||||||
for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
|
for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
|
||||||
@@ -248,6 +257,8 @@ ModelLoadResult ModelLoader::loadModel(const std::string& filepath) {
|
|||||||
loaded.boundsMin = boundsMin;
|
loaded.boundsMin = boundsMin;
|
||||||
loaded.boundsMax = boundsMax;
|
loaded.boundsMax = boundsMax;
|
||||||
loaded.triangleVertices = std::move(triPositions);
|
loaded.triangleVertices = std::move(triPositions);
|
||||||
|
loaded.positions = std::move(positions);
|
||||||
|
loaded.triangleIndices = std::move(indices);
|
||||||
|
|
||||||
loadedMeshes.push_back(std::move(loaded));
|
loadedMeshes.push_back(std::move(loaded));
|
||||||
|
|
||||||
@@ -521,6 +532,14 @@ bool ModelLoader::updateRawMesh(int meshIndex, const RawMeshAsset& asset, std::s
|
|||||||
loaded.boundsMin = asset.boundsMin;
|
loaded.boundsMin = asset.boundsMin;
|
||||||
loaded.boundsMax = asset.boundsMax;
|
loaded.boundsMax = asset.boundsMax;
|
||||||
loaded.triangleVertices = std::move(triPositions);
|
loaded.triangleVertices = std::move(triPositions);
|
||||||
|
loaded.positions = asset.positions;
|
||||||
|
loaded.triangleIndices.clear();
|
||||||
|
loaded.triangleIndices.reserve(asset.faces.size() * 3);
|
||||||
|
for (const auto& face : asset.faces) {
|
||||||
|
loaded.triangleIndices.push_back(face.x);
|
||||||
|
loaded.triangleIndices.push_back(face.y);
|
||||||
|
loaded.triangleIndices.push_back(face.z);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -589,27 +608,54 @@ static void collectRawMeshData(aiNode* node, const aiScene* scene, const aiMatri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelLoader::processNode(aiNode* node, const aiScene* scene, const aiMatrix4x4& parentTransform, std::vector<float>& vertices, std::vector<glm::vec3>& triPositions, glm::vec3& boundsMin, glm::vec3& boundsMax) {
|
void ModelLoader::processNode(aiNode* node, const aiScene* scene, const aiMatrix4x4& parentTransform,
|
||||||
|
std::vector<float>& vertices, std::vector<glm::vec3>& triPositions,
|
||||||
|
std::vector<glm::vec3>& positions, std::vector<uint32_t>& indices,
|
||||||
|
glm::vec3& boundsMin, glm::vec3& boundsMax) {
|
||||||
aiMatrix4x4 currentTransform = parentTransform * node->mTransformation;
|
aiMatrix4x4 currentTransform = parentTransform * node->mTransformation;
|
||||||
// Process all meshes in this node
|
// Process all meshes in this node
|
||||||
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
|
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
|
||||||
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
||||||
processMesh(mesh, currentTransform, vertices, triPositions, boundsMin, boundsMax);
|
processMesh(mesh, currentTransform, vertices, triPositions, positions, indices, boundsMin, boundsMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process children nodes
|
// Process children nodes
|
||||||
for (unsigned int i = 0; i < node->mNumChildren; i++) {
|
for (unsigned int i = 0; i < node->mNumChildren; i++) {
|
||||||
processNode(node->mChildren[i], scene, currentTransform, vertices, triPositions, boundsMin, boundsMax);
|
processNode(node->mChildren[i], scene, currentTransform, vertices, triPositions, positions, indices, boundsMin, boundsMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelLoader::processMesh(aiMesh* mesh, const aiMatrix4x4& transform, std::vector<float>& vertices, std::vector<glm::vec3>& triPositions, glm::vec3& boundsMin, glm::vec3& boundsMax) {
|
void ModelLoader::processMesh(aiMesh* mesh, const aiMatrix4x4& transform,
|
||||||
|
std::vector<float>& vertices, std::vector<glm::vec3>& triPositions,
|
||||||
|
std::vector<glm::vec3>& positions, std::vector<uint32_t>& indices,
|
||||||
|
glm::vec3& boundsMin, glm::vec3& boundsMax) {
|
||||||
glm::mat4 gTransform = aiToGlm(transform);
|
glm::mat4 gTransform = aiToGlm(transform);
|
||||||
glm::mat3 normalMat = glm::transpose(glm::inverse(glm::mat3(gTransform)));
|
glm::mat3 normalMat = glm::transpose(glm::inverse(glm::mat3(gTransform)));
|
||||||
|
|
||||||
|
size_t baseIndex = positions.size();
|
||||||
|
positions.reserve(baseIndex + mesh->mNumVertices);
|
||||||
|
for (unsigned int v = 0; v < mesh->mNumVertices; v++) {
|
||||||
|
glm::vec3 pos(mesh->mVertices[v].x, mesh->mVertices[v].y, mesh->mVertices[v].z);
|
||||||
|
glm::vec4 transformed = gTransform * glm::vec4(pos, 1.0f);
|
||||||
|
glm::vec3 finalPos = glm::vec3(transformed) / (transformed.w == 0.0f ? 1.0f : transformed.w);
|
||||||
|
positions.push_back(finalPos);
|
||||||
|
|
||||||
|
boundsMin.x = std::min(boundsMin.x, finalPos.x);
|
||||||
|
boundsMin.y = std::min(boundsMin.y, finalPos.y);
|
||||||
|
boundsMin.z = std::min(boundsMin.z, finalPos.z);
|
||||||
|
boundsMax.x = std::max(boundsMax.x, finalPos.x);
|
||||||
|
boundsMax.y = std::max(boundsMax.y, finalPos.y);
|
||||||
|
boundsMax.z = std::max(boundsMax.z, finalPos.z);
|
||||||
|
}
|
||||||
|
|
||||||
// Process each face
|
// Process each face
|
||||||
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
|
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
|
||||||
aiFace face = mesh->mFaces[i];
|
aiFace face = mesh->mFaces[i];
|
||||||
|
if (face.mNumIndices == 3) {
|
||||||
|
indices.push_back(static_cast<uint32_t>(baseIndex + face.mIndices[0]));
|
||||||
|
indices.push_back(static_cast<uint32_t>(baseIndex + face.mIndices[1]));
|
||||||
|
indices.push_back(static_cast<uint32_t>(baseIndex + face.mIndices[2]));
|
||||||
|
}
|
||||||
|
|
||||||
// Process each vertex of the face
|
// Process each vertex of the face
|
||||||
for (unsigned int j = 0; j < face.mNumIndices; j++) {
|
for (unsigned int j = 0; j < face.mNumIndices; j++) {
|
||||||
@@ -627,13 +673,6 @@ void ModelLoader::processMesh(aiMesh* mesh, const aiMatrix4x4& transform, std::v
|
|||||||
|
|
||||||
triPositions.push_back(finalPos);
|
triPositions.push_back(finalPos);
|
||||||
|
|
||||||
boundsMin.x = std::min(boundsMin.x, finalPos.x);
|
|
||||||
boundsMin.y = std::min(boundsMin.y, finalPos.y);
|
|
||||||
boundsMin.z = std::min(boundsMin.z, finalPos.z);
|
|
||||||
boundsMax.x = std::max(boundsMax.x, finalPos.x);
|
|
||||||
boundsMax.y = std::max(boundsMax.y, finalPos.y);
|
|
||||||
boundsMax.z = std::max(boundsMax.z, finalPos.z);
|
|
||||||
|
|
||||||
// Normal
|
// Normal
|
||||||
if (mesh->mNormals) {
|
if (mesh->mNormals) {
|
||||||
glm::vec3 n(mesh->mNormals[index].x,
|
glm::vec3 n(mesh->mNormals[index].x,
|
||||||
|
|||||||
@@ -89,8 +89,14 @@ private:
|
|||||||
ModelLoader& operator=(const ModelLoader&) = delete;
|
ModelLoader& operator=(const ModelLoader&) = delete;
|
||||||
|
|
||||||
// Process Assimp scene
|
// Process Assimp scene
|
||||||
void processNode(aiNode* node, const aiScene* scene, const aiMatrix4x4& parentTransform, std::vector<float>& vertices, std::vector<glm::vec3>& triPositions, glm::vec3& boundsMin, glm::vec3& boundsMax);
|
void processNode(aiNode* node, const aiScene* scene, const aiMatrix4x4& parentTransform,
|
||||||
void processMesh(aiMesh* mesh, const aiMatrix4x4& transform, std::vector<float>& vertices, std::vector<glm::vec3>& triPositions, glm::vec3& boundsMin, glm::vec3& boundsMax);
|
std::vector<float>& vertices, std::vector<glm::vec3>& triPositions,
|
||||||
|
std::vector<glm::vec3>& positions, std::vector<uint32_t>& indices,
|
||||||
|
glm::vec3& boundsMin, glm::vec3& boundsMax);
|
||||||
|
void processMesh(aiMesh* mesh, const aiMatrix4x4& transform,
|
||||||
|
std::vector<float>& vertices, std::vector<glm::vec3>& triPositions,
|
||||||
|
std::vector<glm::vec3>& positions, std::vector<uint32_t>& indices,
|
||||||
|
glm::vec3& boundsMin, glm::vec3& boundsMax);
|
||||||
|
|
||||||
// Storage for loaded meshes (reusing OBJLoader::LoadedMesh structure)
|
// Storage for loaded meshes (reusing OBJLoader::LoadedMesh structure)
|
||||||
std::vector<OBJLoader::LoadedMesh> loadedMeshes;
|
std::vector<OBJLoader::LoadedMesh> loadedMeshes;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "ModelLoader.h"
|
#include "ModelLoader.h"
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <new>
|
||||||
#include "extensions/PxRigidBodyExt.h"
|
#include "extensions/PxRigidBodyExt.h"
|
||||||
|
|
||||||
using namespace physx;
|
using namespace physx;
|
||||||
@@ -72,6 +73,7 @@ bool PhysicsSystem::init() {
|
|||||||
mCookParams = PxCookingParams(scale);
|
mCookParams = PxCookingParams(scale);
|
||||||
mCookParams.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE;
|
mCookParams.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE;
|
||||||
mCookParams.meshPreprocessParams |= PxMeshPreprocessingFlag::eWELD_VERTICES;
|
mCookParams.meshPreprocessParams |= PxMeshPreprocessingFlag::eWELD_VERTICES;
|
||||||
|
mCookParams.meshWeldTolerance = std::max(0.0001f, 0.001f * scale.length);
|
||||||
|
|
||||||
PxSceneDesc sceneDesc(mPhysics->getTolerancesScale());
|
PxSceneDesc sceneDesc(mPhysics->getTolerancesScale());
|
||||||
sceneDesc.gravity = PxVec3(0.0f, -9.81f, 0.0f);
|
sceneDesc.gravity = PxVec3(0.0f, -9.81f, 0.0f);
|
||||||
@@ -110,7 +112,21 @@ bool PhysicsSystem::gatherMeshData(const SceneObject& obj, std::vector<PxVec3>&
|
|||||||
} else if (obj.type == ObjectType::Model && obj.meshId >= 0) {
|
} else if (obj.type == ObjectType::Model && obj.meshId >= 0) {
|
||||||
meshInfo = getModelLoader().getMeshInfo(obj.meshId);
|
meshInfo = getModelLoader().getMeshInfo(obj.meshId);
|
||||||
}
|
}
|
||||||
if (!meshInfo || meshInfo->triangleVertices.empty()) {
|
if (!meshInfo) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!meshInfo->positions.empty() && meshInfo->triangleIndices.size() >= 3) {
|
||||||
|
vertices.reserve(meshInfo->positions.size());
|
||||||
|
indices.reserve(meshInfo->triangleIndices.size());
|
||||||
|
for (const auto& v : meshInfo->positions) {
|
||||||
|
vertices.emplace_back(v.x, v.y, v.z);
|
||||||
|
}
|
||||||
|
indices.insert(indices.end(), meshInfo->triangleIndices.begin(), meshInfo->triangleIndices.end());
|
||||||
|
return !vertices.empty() && (indices.size() % 3 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meshInfo->triangleVertices.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +146,7 @@ PxTriangleMesh* PhysicsSystem::cookTriangleMesh(const std::vector<PxVec3>& verti
|
|||||||
const std::vector<uint32_t>& indices) const {
|
const std::vector<uint32_t>& indices) const {
|
||||||
if (vertices.empty() || indices.size() < 3) return nullptr;
|
if (vertices.empty() || indices.size() < 3) return nullptr;
|
||||||
|
|
||||||
|
try {
|
||||||
PxTriangleMeshDesc desc;
|
PxTriangleMeshDesc desc;
|
||||||
desc.points.count = static_cast<uint32_t>(vertices.size());
|
desc.points.count = static_cast<uint32_t>(vertices.size());
|
||||||
desc.points.stride = sizeof(PxVec3);
|
desc.points.stride = sizeof(PxVec3);
|
||||||
@@ -144,11 +161,15 @@ PxTriangleMesh* PhysicsSystem::cookTriangleMesh(const std::vector<PxVec3>& verti
|
|||||||
}
|
}
|
||||||
PxDefaultMemoryInputData input(buf.getData(), buf.getSize());
|
PxDefaultMemoryInputData input(buf.getData(), buf.getSize());
|
||||||
return mPhysics->createTriangleMesh(input);
|
return mPhysics->createTriangleMesh(input);
|
||||||
|
} catch (const std::bad_alloc&) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PxConvexMesh* PhysicsSystem::cookConvexMesh(const std::vector<PxVec3>& vertices) const {
|
PxConvexMesh* PhysicsSystem::cookConvexMesh(const std::vector<PxVec3>& vertices) const {
|
||||||
if (vertices.size() < 4) return nullptr;
|
if (vertices.size() < 4) return nullptr;
|
||||||
|
|
||||||
|
try {
|
||||||
PxConvexMeshDesc desc;
|
PxConvexMeshDesc desc;
|
||||||
desc.points.count = static_cast<uint32_t>(vertices.size());
|
desc.points.count = static_cast<uint32_t>(vertices.size());
|
||||||
desc.points.stride = sizeof(PxVec3);
|
desc.points.stride = sizeof(PxVec3);
|
||||||
@@ -162,6 +183,9 @@ PxConvexMesh* PhysicsSystem::cookConvexMesh(const std::vector<PxVec3>& vertices)
|
|||||||
}
|
}
|
||||||
PxDefaultMemoryInputData input(buf.getData(), buf.getSize());
|
PxDefaultMemoryInputData input(buf.getData(), buf.getSize());
|
||||||
return mPhysics->createConvexMesh(input);
|
return mPhysics->createConvexMesh(input);
|
||||||
|
} catch (const std::bad_alloc&) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PhysicsSystem::attachPrimitiveShape(PxRigidActor* actor, const SceneObject& obj, bool isDynamic) const {
|
bool PhysicsSystem::attachPrimitiveShape(PxRigidActor* actor, const SceneObject& obj, bool isDynamic) const {
|
||||||
@@ -255,12 +279,66 @@ bool PhysicsSystem::attachColliderShape(PxRigidActor* actor, const SceneObject&
|
|||||||
}
|
}
|
||||||
minDim = std::min(radius * 2.0f, halfHeight * 2.0f);
|
minDim = std::min(radius * 2.0f, halfHeight * 2.0f);
|
||||||
} else {
|
} else {
|
||||||
std::vector<PxVec3> verts;
|
const OBJLoader::LoadedMesh* meshInfo = nullptr;
|
||||||
std::vector<uint32_t> indices;
|
if (obj.type == ObjectType::OBJMesh && obj.meshId >= 0) {
|
||||||
if (!gatherMeshData(obj, verts, indices)) {
|
meshInfo = g_objLoader.getMeshInfo(obj.meshId);
|
||||||
|
} else if (obj.type == ObjectType::Model && obj.meshId >= 0) {
|
||||||
|
meshInfo = getModelLoader().getMeshInfo(obj.meshId);
|
||||||
|
}
|
||||||
|
if (!meshInfo) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const bool hasIndexed = !meshInfo->positions.empty() && meshInfo->triangleIndices.size() >= 3;
|
||||||
|
const bool hasTriVerts = !meshInfo->triangleVertices.empty();
|
||||||
|
if (!hasIndexed && !hasTriVerts) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto makeBoundsShape = [&](const glm::vec3& boundsMin, const glm::vec3& boundsMax) {
|
||||||
|
glm::vec3 halfExtents = glm::max((boundsMax - boundsMin) * 0.5f, glm::vec3(0.01f));
|
||||||
|
glm::vec3 center = (boundsMax + boundsMin) * 0.5f;
|
||||||
|
PxShape* box = mPhysics->createShape(PxBoxGeometry(ToPxVec3(halfExtents)), *mDefaultMaterial, true);
|
||||||
|
if (box) {
|
||||||
|
box->setLocalPose(PxTransform(ToPxVec3(center), PxQuat(PxIdentity)));
|
||||||
|
}
|
||||||
|
return box;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t kMaxCookVertices = 1000000;
|
||||||
|
size_t cookVertices = hasIndexed ? meshInfo->positions.size() : meshInfo->triangleVertices.size();
|
||||||
|
if (cookVertices > kMaxCookVertices) {
|
||||||
|
glm::vec3 boundsMin(FLT_MAX);
|
||||||
|
glm::vec3 boundsMax(-FLT_MAX);
|
||||||
|
const auto& sourceVerts = hasIndexed ? meshInfo->positions : meshInfo->triangleVertices;
|
||||||
|
for (const auto& v : sourceVerts) {
|
||||||
|
glm::vec3 scaled = v * obj.scale;
|
||||||
|
boundsMin = glm::min(boundsMin, scaled);
|
||||||
|
boundsMax = glm::max(boundsMax, scaled);
|
||||||
|
}
|
||||||
|
minDim = std::max(0.01f, std::min({boundsMax.x - boundsMin.x, boundsMax.y - boundsMin.y, boundsMax.z - boundsMin.z}));
|
||||||
|
shape = makeBoundsShape(boundsMin, boundsMax);
|
||||||
|
} else {
|
||||||
|
std::vector<PxVec3> verts;
|
||||||
|
std::vector<uint32_t> indices;
|
||||||
|
bool hasMeshData = false;
|
||||||
|
try {
|
||||||
|
hasMeshData = gatherMeshData(obj, verts, indices);
|
||||||
|
} catch (const std::bad_alloc&) {
|
||||||
|
hasMeshData = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasMeshData) {
|
||||||
|
glm::vec3 boundsMin(FLT_MAX);
|
||||||
|
glm::vec3 boundsMax(-FLT_MAX);
|
||||||
|
const auto& sourceVerts = hasIndexed ? meshInfo->positions : meshInfo->triangleVertices;
|
||||||
|
for (const auto& v : sourceVerts) {
|
||||||
|
glm::vec3 scaled = v * obj.scale;
|
||||||
|
boundsMin = glm::min(boundsMin, scaled);
|
||||||
|
boundsMax = glm::max(boundsMax, scaled);
|
||||||
|
}
|
||||||
|
minDim = std::max(0.01f, std::min({boundsMax.x - boundsMin.x, boundsMax.y - boundsMin.y, boundsMax.z - boundsMin.z}));
|
||||||
|
shape = makeBoundsShape(boundsMin, boundsMax);
|
||||||
|
} else {
|
||||||
bool useConvex = obj.collider.convex || obj.collider.type == ColliderType::ConvexMesh || isDynamic;
|
bool useConvex = obj.collider.convex || obj.collider.type == ColliderType::ConvexMesh || isDynamic;
|
||||||
glm::vec3 boundsMin(FLT_MAX);
|
glm::vec3 boundsMin(FLT_MAX);
|
||||||
glm::vec3 boundsMax(-FLT_MAX);
|
glm::vec3 boundsMax(-FLT_MAX);
|
||||||
@@ -275,19 +353,27 @@ bool PhysicsSystem::attachColliderShape(PxRigidActor* actor, const SceneObject&
|
|||||||
minDim = std::max(0.01f, std::min({boundsMax.x - boundsMin.x, boundsMax.y - boundsMin.y, boundsMax.z - boundsMin.z}));
|
minDim = std::max(0.01f, std::min({boundsMax.x - boundsMin.x, boundsMax.y - boundsMin.y, boundsMax.z - boundsMin.z}));
|
||||||
if (useConvex) {
|
if (useConvex) {
|
||||||
PxConvexMesh* convex = cookConvexMesh(verts);
|
PxConvexMesh* convex = cookConvexMesh(verts);
|
||||||
if (!convex) return false;
|
if (convex) {
|
||||||
PxConvexMeshGeometry geom(convex, PxMeshScale(ToPxVec3(obj.scale), PxQuat(PxIdentity)));
|
PxConvexMeshGeometry geom(convex, PxMeshScale(ToPxVec3(obj.scale), PxQuat(PxIdentity)));
|
||||||
shape = mPhysics->createShape(geom, *mDefaultMaterial, true);
|
shape = mPhysics->createShape(geom, *mDefaultMaterial, true);
|
||||||
convex->release();
|
convex->release();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
PxTriangleMesh* tri = cookTriangleMesh(verts, indices);
|
PxTriangleMesh* tri = cookTriangleMesh(verts, indices);
|
||||||
if (!tri) return false;
|
if (tri) {
|
||||||
PxTriangleMeshGeometry geom(tri, PxMeshScale(ToPxVec3(obj.scale), PxQuat(PxIdentity)));
|
PxTriangleMeshGeometry geom(tri, PxMeshScale(ToPxVec3(obj.scale), PxQuat(PxIdentity)));
|
||||||
shape = mPhysics->createShape(geom, *mDefaultMaterial, true);
|
shape = mPhysics->createShape(geom, *mDefaultMaterial, true);
|
||||||
tri->release();
|
tri->release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!shape) {
|
||||||
|
shape = makeBoundsShape(boundsMin, boundsMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tuneShape(shape, std::max(0.01f, minDim), isDynamic || obj.hasPlayerController);
|
tuneShape(shape, std::max(0.01f, minDim), isDynamic || obj.hasPlayerController);
|
||||||
|
|
||||||
if (!shape) return false;
|
if (!shape) return false;
|
||||||
@@ -385,6 +471,51 @@ void PhysicsSystem::onPlayStart(const std::vector<SceneObject>& objects) {
|
|||||||
clearActors();
|
clearActors();
|
||||||
createGroundPlane();
|
createGroundPlane();
|
||||||
|
|
||||||
|
struct MeshCookInfo {
|
||||||
|
std::string name;
|
||||||
|
size_t vertices = 0;
|
||||||
|
size_t triangles = 0;
|
||||||
|
size_t duplicateVertices = 0;
|
||||||
|
};
|
||||||
|
std::vector<MeshCookInfo> cookInfos;
|
||||||
|
cookInfos.reserve(objects.size());
|
||||||
|
|
||||||
|
for (const auto& obj : objects) {
|
||||||
|
if (!obj.enabled || !obj.hasCollider || !obj.collider.enabled) continue;
|
||||||
|
if (obj.collider.type == ColliderType::Box || obj.collider.type == ColliderType::Capsule) continue;
|
||||||
|
const OBJLoader::LoadedMesh* meshInfo = nullptr;
|
||||||
|
if (obj.type == ObjectType::OBJMesh && obj.meshId >= 0) {
|
||||||
|
meshInfo = g_objLoader.getMeshInfo(obj.meshId);
|
||||||
|
} else if (obj.type == ObjectType::Model && obj.meshId >= 0) {
|
||||||
|
meshInfo = getModelLoader().getMeshInfo(obj.meshId);
|
||||||
|
}
|
||||||
|
if (!meshInfo) continue;
|
||||||
|
const bool hasIndexed = !meshInfo->positions.empty() && meshInfo->triangleIndices.size() >= 3;
|
||||||
|
const bool hasTriVerts = !meshInfo->triangleVertices.empty();
|
||||||
|
if (!hasIndexed && !hasTriVerts) continue;
|
||||||
|
MeshCookInfo info;
|
||||||
|
info.name = obj.name;
|
||||||
|
info.vertices = hasIndexed ? meshInfo->positions.size() : meshInfo->triangleVertices.size();
|
||||||
|
info.triangles = hasIndexed ? (meshInfo->triangleIndices.size() / 3) : (meshInfo->triangleVertices.size() / 3);
|
||||||
|
info.duplicateVertices = meshInfo->triangleVertices.size();
|
||||||
|
cookInfos.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cookInfos.empty()) {
|
||||||
|
std::sort(cookInfos.begin(), cookInfos.end(),
|
||||||
|
[](const MeshCookInfo& a, const MeshCookInfo& b) { return a.vertices > b.vertices; });
|
||||||
|
size_t reportCount = std::min<size_t>(cookInfos.size(), 5);
|
||||||
|
std::cerr << "[Physics] Mesh collider stats (top " << reportCount << " by vertex count):\n";
|
||||||
|
for (size_t i = 0; i < reportCount; ++i) {
|
||||||
|
const auto& info = cookInfos[i];
|
||||||
|
std::cerr << " " << info.name
|
||||||
|
<< " verts=" << info.vertices
|
||||||
|
<< " tris=" << info.triangles
|
||||||
|
<< " dupVerts=" << info.duplicateVertices
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& obj : objects) {
|
for (const auto& obj : objects) {
|
||||||
if (!obj.enabled) continue;
|
if (!obj.enabled) continue;
|
||||||
ActorRecord rec = createActorFor(obj);
|
ActorRecord rec = createActorFor(obj);
|
||||||
|
|||||||
@@ -363,6 +363,13 @@ int OBJLoader::loadOBJ(const std::string& filepath, std::string& errorMsg) {
|
|||||||
glm::vec3 boundsMin(FLT_MAX);
|
glm::vec3 boundsMin(FLT_MAX);
|
||||||
glm::vec3 boundsMax(-FLT_MAX);
|
glm::vec3 boundsMax(-FLT_MAX);
|
||||||
std::vector<glm::vec3> triPositions;
|
std::vector<glm::vec3> triPositions;
|
||||||
|
std::vector<glm::vec3> positions;
|
||||||
|
std::vector<uint32_t> triangleIndices;
|
||||||
|
|
||||||
|
positions.reserve(attrib.vertices.size() / 3);
|
||||||
|
for (size_t i = 0; i + 2 < attrib.vertices.size(); i += 3) {
|
||||||
|
positions.emplace_back(attrib.vertices[i], attrib.vertices[i + 1], attrib.vertices[i + 2]);
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& shape : shapes) {
|
for (const auto& shape : shapes) {
|
||||||
size_t indexOffset = 0;
|
size_t indexOffset = 0;
|
||||||
@@ -376,6 +383,8 @@ int OBJLoader::loadOBJ(const std::string& filepath, std::string& errorMsg) {
|
|||||||
bool hasNormal = false;
|
bool hasNormal = false;
|
||||||
};
|
};
|
||||||
std::vector<TempVertex> faceVerts;
|
std::vector<TempVertex> faceVerts;
|
||||||
|
std::vector<int> facePosIndices;
|
||||||
|
facePosIndices.reserve(static_cast<size_t>(fv));
|
||||||
|
|
||||||
for (int v = 0; v < fv; v++) {
|
for (int v = 0; v < fv; v++) {
|
||||||
tinyobj::index_t idx = shape.mesh.indices[indexOffset + v];
|
tinyobj::index_t idx = shape.mesh.indices[indexOffset + v];
|
||||||
@@ -407,6 +416,7 @@ int OBJLoader::loadOBJ(const std::string& filepath, std::string& errorMsg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
faceVerts.push_back(tv);
|
faceVerts.push_back(tv);
|
||||||
|
facePosIndices.push_back(idx.vertex_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasNormalsInFile && fv >= 3) {
|
if (!hasNormalsInFile && fv >= 3) {
|
||||||
@@ -424,6 +434,15 @@ int OBJLoader::loadOBJ(const std::string& filepath, std::string& errorMsg) {
|
|||||||
for (int v = 1; v < fv - 1; v++) {
|
for (int v = 1; v < fv - 1; v++) {
|
||||||
const TempVertex* tri[3] = { &faceVerts[0], &faceVerts[v], &faceVerts[v+1] };
|
const TempVertex* tri[3] = { &faceVerts[0], &faceVerts[v], &faceVerts[v+1] };
|
||||||
|
|
||||||
|
int idx0 = facePosIndices[0];
|
||||||
|
int idx1 = facePosIndices[v];
|
||||||
|
int idx2 = facePosIndices[v + 1];
|
||||||
|
if (idx0 >= 0 && idx1 >= 0 && idx2 >= 0) {
|
||||||
|
triangleIndices.push_back(static_cast<uint32_t>(idx0));
|
||||||
|
triangleIndices.push_back(static_cast<uint32_t>(idx1));
|
||||||
|
triangleIndices.push_back(static_cast<uint32_t>(idx2));
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
triPositions.push_back(tri[i]->pos);
|
triPositions.push_back(tri[i]->pos);
|
||||||
vertices.push_back(tri[i]->pos.x);
|
vertices.push_back(tri[i]->pos.x);
|
||||||
@@ -457,6 +476,8 @@ int OBJLoader::loadOBJ(const std::string& filepath, std::string& errorMsg) {
|
|||||||
loaded.boundsMin = boundsMin;
|
loaded.boundsMin = boundsMin;
|
||||||
loaded.boundsMax = boundsMax;
|
loaded.boundsMax = boundsMax;
|
||||||
loaded.triangleVertices = std::move(triPositions);
|
loaded.triangleVertices = std::move(triPositions);
|
||||||
|
loaded.positions = std::move(positions);
|
||||||
|
loaded.triangleIndices = std::move(triangleIndices);
|
||||||
|
|
||||||
loadedMeshes.push_back(std::move(loaded));
|
loadedMeshes.push_back(std::move(loaded));
|
||||||
return static_cast<int>(loadedMeshes.size() - 1);
|
return static_cast<int>(loadedMeshes.size() - 1);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "../include/Textures/Texture.h"
|
#include "../include/Textures/Texture.h"
|
||||||
#include "../include/Skybox/Skybox.h"
|
#include "../include/Skybox/Skybox.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
// Cube vertex data (position + normal + texcoord)
|
// Cube vertex data (position + normal + texcoord)
|
||||||
extern float vertices[];
|
extern float vertices[];
|
||||||
@@ -41,6 +42,8 @@ public:
|
|||||||
glm::vec3 boundsMin = glm::vec3(FLT_MAX);
|
glm::vec3 boundsMin = glm::vec3(FLT_MAX);
|
||||||
glm::vec3 boundsMax = glm::vec3(-FLT_MAX);
|
glm::vec3 boundsMax = glm::vec3(-FLT_MAX);
|
||||||
std::vector<glm::vec3> triangleVertices; // positions duplicated per-triangle for picking
|
std::vector<glm::vec3> triangleVertices; // positions duplicated per-triangle for picking
|
||||||
|
std::vector<glm::vec3> positions; // unique vertex positions for physics
|
||||||
|
std::vector<uint32_t> triangleIndices; // triangle indices into positions
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user