124 lines
8.4 KiB
C++
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
|