MetaCore/Source/MetaCorePhysics/Private/MetaCorePhysicsSettings.cpp
2026-07-22 14:14:54 +08:00

124 lines
8.4 KiB
C++

#include "MetaCorePhysics/MetaCorePhysics.h"
#include "MetaCoreFoundation/MetaCoreArchive.h"
#include "MetaCoreFoundation/MetaCoreHash.h"
#include <algorithm>
#include <cmath>
#include <fstream>
#include <cstring>
#include <iomanip>
#include <sstream>
namespace MetaCore {
std::string MetaCoreBuildPhysicsCollisionCacheKey(MetaCoreAssetGuid meshGuid, std::uint64_t payloadHash,
glm::vec3 effectiveScale, std::uint32_t shapeType, std::uint64_t bakeSettingsHash,
std::string_view backendVersion) {
std::uint64_t hash = MetaCoreHashString(meshGuid.ToString());
hash = MetaCoreHashCombine(hash, payloadHash);
hash = MetaCoreHashCombine(hash, MetaCoreHashBytes(std::as_bytes(std::span(&effectiveScale, 1U))));
hash = MetaCoreHashCombine(hash, shapeType);
hash = MetaCoreHashCombine(hash, bakeSettingsHash);
hash = MetaCoreHashCombine(hash, MetaCoreHashString(backendVersion));
hash = MetaCoreHashCombine(hash, MetaCorePhysicsCollisionDerivedDataVersion);
std::ostringstream stream; stream << "physics-" << std::hex << std::setfill('0') << std::setw(16) << hash; return stream.str();
}
std::vector<std::byte> MetaCoreEncodePhysicsCollisionDerivedData(const MetaCorePhysicsMeshData& mesh) {
constexpr std::uint32_t magic = 0x5048434DU; // MCHP
const std::uint32_t vertexCount = static_cast<std::uint32_t>(mesh.Positions.size());
const std::uint32_t indexCount = static_cast<std::uint32_t>(mesh.Indices.size());
std::vector<std::byte> result(sizeof(magic) + sizeof(std::uint32_t) * 3U + sizeof(glm::vec3) * vertexCount + sizeof(std::uint32_t) * indexCount);
std::byte* cursor = result.data();
const auto write = [&](const void* source, std::size_t size) { std::memcpy(cursor, source, size); cursor += size; };
const std::uint32_t version = MetaCorePhysicsCollisionDerivedDataVersion;
write(&magic,sizeof(magic)); write(&version,sizeof(version)); write(&vertexCount,sizeof(vertexCount)); write(&indexCount,sizeof(indexCount));
if (vertexCount) write(mesh.Positions.data(),sizeof(glm::vec3)*vertexCount); if (indexCount) write(mesh.Indices.data(),sizeof(std::uint32_t)*indexCount);
return result;
}
std::optional<MetaCorePhysicsMeshData> MetaCoreDecodePhysicsCollisionDerivedData(std::span<const std::byte> bytes) {
constexpr std::uint32_t magic = 0x5048434DU; if (bytes.size() < sizeof(std::uint32_t)*4U) return std::nullopt;
const std::byte* cursor=bytes.data(); const auto read=[&](void* target,std::size_t size){if(static_cast<std::size_t>(cursor-bytes.data())+size>bytes.size())return false;std::memcpy(target,cursor,size);cursor+=size;return true;};
std::uint32_t storedMagic=0,version=0,vertexCount=0,indexCount=0;
if(!read(&storedMagic,4)||!read(&version,4)||!read(&vertexCount,4)||!read(&indexCount,4)||storedMagic!=magic||version!=MetaCorePhysicsCollisionDerivedDataVersion)return std::nullopt;
const std::size_t required=sizeof(glm::vec3)*static_cast<std::size_t>(vertexCount)+sizeof(std::uint32_t)*static_cast<std::size_t>(indexCount);
if(required>bytes.size()-static_cast<std::size_t>(cursor-bytes.data()))return std::nullopt;
MetaCorePhysicsMeshData result;result.Positions.resize(vertexCount);result.Indices.resize(indexCount);
if(vertexCount&&!read(result.Positions.data(),sizeof(glm::vec3)*vertexCount))return std::nullopt;if(indexCount&&!read(result.Indices.data(),sizeof(std::uint32_t)*indexCount))return std::nullopt;return result;
}
MetaCorePhysicsSettingsDocument MetaCoreBuildDefaultPhysicsSettings() {
MetaCorePhysicsSettingsDocument result;
result.LayerNames.resize(32U);
result.CollisionMasks.assign(32U, 0xFFFFFFFFU);
result.LayerNames[0] = "Default";
for (std::size_t index = 1; index < result.LayerNames.size(); ++index) result.LayerNames[index] = "Layer " + std::to_string(index);
return result;
}
bool MetaCoreValidatePhysicsSettings(MetaCorePhysicsSettingsDocument& document, std::vector<std::string>* issues) {
bool valid = true;
const auto report = [&](std::string value) { valid = false; if (issues) issues->push_back(std::move(value)); };
if (!std::isfinite(document.Gravity.x) || !std::isfinite(document.Gravity.y) || !std::isfinite(document.Gravity.z)) {
report("Gravity 必须为有限值"); document.Gravity = glm::vec3(0.0F, 0.0F, -9.81F);
}
if (!std::isfinite(document.FixedTimeStep) || document.FixedTimeStep < 0.001 || document.FixedTimeStep > 1.0) {
report("FixedTimeStep 必须位于 [0.001, 1.0]"); document.FixedTimeStep = 1.0 / 60.0;
}
if (!std::isfinite(document.MaxFrameDelta) || document.MaxFrameDelta < document.FixedTimeStep || document.MaxFrameDelta > 1.0) {
report("MaxFrameDelta 无效"); document.MaxFrameDelta = 0.25;
}
if (document.MaxFixedStepsPerFrame == 0U || document.MaxFixedStepsPerFrame > 32U) {
report("MaxFixedStepsPerFrame 必须位于 [1, 32]"); document.MaxFixedStepsPerFrame = 4U;
}
if (document.LayerNames.size() != 32U) { report("碰撞层名称必须恰好为 32 项"); document.LayerNames.resize(32U); }
if (document.CollisionMasks.size() != 32U) { report("碰撞矩阵必须恰好为 32 项"); document.CollisionMasks.resize(32U, 0xFFFFFFFFU); }
for (std::uint32_t lhs = 0; lhs < 32U; ++lhs) for (std::uint32_t rhs = lhs + 1U; rhs < 32U; ++rhs) {
const bool enabled = ((document.CollisionMasks[lhs] >> rhs) & 1U) != 0U && ((document.CollisionMasks[rhs] >> lhs) & 1U) != 0U;
if (enabled) { document.CollisionMasks[lhs] |= 1U << rhs; document.CollisionMasks[rhs] |= 1U << lhs; }
else { document.CollisionMasks[lhs] &= ~(1U << rhs); document.CollisionMasks[rhs] &= ~(1U << lhs); }
}
return valid;
}
bool MetaCoreWritePhysicsSettings(const std::filesystem::path& path, const MetaCorePhysicsSettingsDocument& document,
const MetaCoreTypeRegistry& registry) {
const auto bytes = MetaCoreSerializeToBytes(document, registry); if (!bytes) return false;
std::error_code error; std::filesystem::create_directories(path.parent_path(), error);
std::ofstream output(path, std::ios::binary | std::ios::trunc);
if (!output) return false;
output.write(reinterpret_cast<const char*>(bytes->data()), static_cast<std::streamsize>(bytes->size())); return output.good();
}
std::optional<MetaCorePhysicsSettingsDocument> MetaCoreReadPhysicsSettings(const std::filesystem::path& path,
const MetaCoreTypeRegistry& registry) {
std::ifstream input(path, std::ios::binary | std::ios::ate); if (!input) return std::nullopt;
const auto size = static_cast<std::size_t>(input.tellg()); input.seekg(0); std::vector<std::byte> bytes(size);
if (size && !input.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(size))) return std::nullopt;
MetaCorePhysicsSettingsDocument result;
if (!MetaCoreDeserializeFromBytes(std::span<const std::byte>(bytes.data(), bytes.size()), result, registry)) return std::nullopt;
// Version 1 used Bullet's Y-up convention. MetaCore is Z-up, so migrate the
// complete gravity vector instead of only replacing the old default value.
if (result.Version < 2U) {
result.Gravity = glm::vec3(result.Gravity.x, result.Gravity.z, result.Gravity.y);
result.Version = 2U;
}
(void)MetaCoreValidatePhysicsSettings(result); return result;
}
bool MetaCoreWritePhysicsMaterial(const std::filesystem::path& path, const MetaCorePhysicsMaterialDocument& document,
const MetaCoreTypeRegistry& registry) {
const auto bytes=MetaCoreSerializeToBytes(document,registry);if(!bytes)return false;std::error_code error;std::filesystem::create_directories(path.parent_path(),error);
std::ofstream output(path,std::ios::binary|std::ios::trunc);if(!output)return false;output.write(reinterpret_cast<const char*>(bytes->data()),static_cast<std::streamsize>(bytes->size()));return output.good();
}
std::optional<MetaCorePhysicsMaterialDocument> MetaCoreReadPhysicsMaterial(const std::filesystem::path& path,
const MetaCoreTypeRegistry& registry) {
std::ifstream input(path,std::ios::binary|std::ios::ate);if(!input)return std::nullopt;const auto size=static_cast<std::size_t>(input.tellg());input.seekg(0);std::vector<std::byte> bytes(size);
if(size&&!input.read(reinterpret_cast<char*>(bytes.data()),static_cast<std::streamsize>(size)))return std::nullopt;MetaCorePhysicsMaterialDocument result;if(!MetaCoreDeserializeFromBytes(std::span<const std::byte>(bytes.data(),bytes.size()),result,registry))return std::nullopt;return result;
}
} // namespace MetaCore