* 物理引擎调试绘图、模型包围盒计算、使用包围盒形状

This commit is contained in:
2026-05-19 11:55:29 +08:00
parent 63283adbb0
commit dcc3f8a26c
14 changed files with 167 additions and 109 deletions
+56 -22
View File
@@ -17,6 +17,7 @@
#include <Jolt/Physics/Body/BodyActivationListener.h>
#include <Jolt/Physics/Body/Body.h>
#include <Jolt/Renderer/DebugRendererSimple.h>
#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
#include "dl_rigid_body.h"
#include "base/dl_color.h"
@@ -41,15 +42,8 @@ static Position3 to_pos3(const RVec3& pos)
struct RigidBodyShape
{
std::vector<JPH::BoxShapeSettings*> _vecSetting;
// 不同缩放使用不同形状
std::unordered_map<float, JPH::ShapeRefC> _mapRef;
~RigidBodyShape()
{
for (auto& iter : _vecSetting)
delete iter;
}
std::unordered_map<uint64_t, JPH::ShapeRefC> _mapRef;
};
@@ -240,6 +234,11 @@ public:
}
};
inline Color to_color(JPH::Color& color)
{
return createColor(color.r, color.g, color.b, color.a);
}
class MyDebugRenderer : public JPH::DebugRendererSimple
{
public:
@@ -248,7 +247,7 @@ public:
{
auto p0 = to_pos3(inFrom);
auto p1 = to_pos3(inTo);
Color color = createColor(inColor.GetUInt32());
Color color = to_color(inColor);
g_gfx->SetColor(color);
g_gfx->Line(p0, p1);
}
@@ -260,7 +259,7 @@ public:
auto p0 = to_pos3(v1);
auto p1 = to_pos3(v2);
auto p2 = to_pos3(v3);
Color color = createColor(col.GetUInt32());
Color color = to_color(col);
g_gfx->SetColor(color);
g_gfx->Triangle(p0, p1, p2);
}
@@ -373,12 +372,33 @@ dl::RigidBody* Physics::CreateRigidBody(JPH::ShapeRefC* ref,
dl::RigidBody* Physics::CreateRigidBodyDefault(Transform* trans)
{
auto ref = CreateShapeBox(0, { 1,1,1 }, { 1,1,1 });
auto ref = CreateShapeBox(0, { 1,1,1 });
return CreateRigidBody(ref, {}, {}, RigidBodyType::DYNAMIC, true);
}
inline uint64_t hash3f_64(const Position3& pos)
{
auto ux = *(uint32_t*)(&pos.x);
auto uy = *(uint32_t*)(&pos.y);
auto uz = *(uint32_t*)(&pos.z);
JPH::ShapeRefC* Physics::CreateShapeBox(size_t shape_id, const Position3& box, const Position3& scale)
uint64_t h = (
(uint64_t)ux * 0x123456789 +
(uint64_t)uy * 0x987654321 +
(uint64_t)uz * 0x555555555
);
h ^= h >> 32;
h *= 0x9E3779B97F4A7C15; // 黄金比例常数
h ^= h >> 32;
return h;
}
JPH::ShapeRefC* Physics::CreateShapeBox(size_t shape_id,
const Position3& box,
const Position3& scale,
const Position3& position,
const Position3& rotate)
{
RigidBodyShape* shape;
@@ -391,26 +411,40 @@ JPH::ShapeRefC* Physics::CreateShapeBox(size_t shape_id, const Position3& box, c
else
shape = iter->second;
float step = 10000.0f;
assert(scale.x < step && scale.y < step && scale.z < step);
uint64_t s_index = hash3f_64(scale);
float s = scale.x + scale.y * step + scale.z * step * step;
auto iter1 = shape->_mapRef.find(s);
auto iter1 = shape->_mapRef.find(s_index);
if (iter1 == shape->_mapRef.end())
{
Vec3 vec{
box.x * scale.x,
box.y * scale.y,
box.z * scale.z };
BoxShapeSettings* setting = new BoxShapeSettings(vec);
shape->_vecSetting.push_back(setting);
BoxShapeSettings setting{ vec };
ShapeSettings::ShapeResult result = setting->Create();
ShapeSettings::ShapeResult result = setting.Create();
if (result.HasError())
log_err("JPH: {}", result.GetError());
log_err("JPH Shape: {}", result.GetError());
ShapeRefC ref = result.Get();
shape->_mapRef[s] = result.Get();
return &shape->_mapRef[s];
// 偏移,旋转
if (!Every::Zero(position)
|| !Every::Zero(scale))
{
RotatedTranslatedShapeSettings setting{
to_rvec3(position),
JPH::Quat::sEulerAngles(to_rvec3(rotate)),
ref
};
ShapeSettings::ShapeResult result = setting.Create();
if (result.HasError())
log_err("JPH Shape: {}", result.GetError());
ref = result.Get();
}
shape->_mapRef[s_index] = ref;
return &shape->_mapRef[s_index];
}
else
return &iter1->second;
+13 -2
View File
@@ -57,13 +57,24 @@ public:
RigidBody* CreateRigidBodyDefault(Transform* trans);
// 创建(取得)形状 box
// 一般来说一个模型拥有固定的 形状和物理属性
// 一般来说一个模型拥有固定的 形状和物理属性()
// shape_id 等价模型id
// box 大小
// scale 缩放
/**
* @brief 创建(取得)形状 box
* @param[in] shape_id 形状id
* @param[in] box 轴对称包围盒
* @param[in] offset 偏移
* @param[in] rotate 旋转
* @param[in] scale 缩放
*/
JPH::ShapeRefC* CreateShapeBox(size_t shape_id,
const Position3& box,
const Position3& scale);
const Position3& scale = {1.0f, 1.0f, 1.0f},
const Position3& offset = {},
const Position3& rotate = {});
void Update();
void RenderDebug();
+7
View File
@@ -31,4 +31,11 @@ dl::Position3 RigidBody::GetPosition()
return { pos.GetX(), pos.GetY(), pos.GetZ() };
}
dl::Position3 RigidBody::GetRotate()
{
auto quat = _body->GetRotation();
auto vec = quat.GetEulerAngles();
return { vec.GetX(), vec.GetY(), vec.GetZ() };
}
}
+1
View File
@@ -33,6 +33,7 @@ class RigidBody
friend class Physics;
public:
Position3 GetPosition();
Position3 GetRotate();
~RigidBody();
private:
+28 -3
View File
@@ -30,10 +30,15 @@ void Actor::Render()
if (_rigidBody)
{
_trans->SetPosition(_rigidBody->GetPosition());
_trans->SetRotate(_rigidBody->GetRotate());
}
//--------------------render-------------
assert(_idModel < g_vk->GetMulModelSize());
if (_idModel >= g_vk->GetMulModelSize())
{
log_warn("错误的Actor!");
return;
}
std::vector<Model*>& mul_model = g_vk->GetMulModel(_idModel);
//ub骨骼id
@@ -110,11 +115,31 @@ void Actor::CreateRigidBody(RigidBodyType type, bool active)
}
std::vector<Model*>& mul_model = g_vk->GetMulModel(_idModel);
assert(mul_model.size() == 1);
BoundingBox aabb;
for (auto& iter : mul_model)
{
for (auto& iter1 : iter->_treeNode)
{
if (iter1._aabb._vaild)
{
Matrix4 mat_to_world;
if (_modelAnimationInstance)
mat_to_world = iter1.GetMatrixWorld(_modelAnimationInstance);
else
mat_to_world = iter1.GetMatrixLocal();
aabb.Add(iter1._aabb.getAABB(mat_to_world));
}
}
}
// 没有包围盒,创建一个默认的
if (!aabb._vaild)
aabb = BoundingBox({ -1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f, 1.0f });
Position3 aabb_center = (aabb._max + aabb._min) / 2.0f;
//
JPH::ShapeRefC* ref = g_physics->CreateShapeBox(
_idModel, {1, 1, 1}, _trans->GetScale());
_idModel, aabb._max - aabb_center, _trans->GetScale(), aabb_center);
//
_rigidBody = g_physics->CreateRigidBody(ref,
_trans->GetPosition(),
+1 -1
View File
@@ -44,7 +44,7 @@ private:
std::vector<int> _treeNodeParent;
std::vector<ModelAnimation> _allAnimation; // 动画
std::vector<ModelSkin> _allSkin; // 骨骼
std::vector<ModelDrawCall> _allSub; // 网格
std::vector<ModelDrawCall> _allSub; // 网格
std::vector<Instance> _mulInstance; // 实例(每个actor有一个)
};
+22 -15
View File
@@ -13,6 +13,7 @@ namespace dl
s.Write(&_indexSkin);
s.Write(&_indexParent);
s.Write(&_trans2Parent);
s.Write(&_aabb);
}
void ModelNodeRt::Load(StreamInput& s)
@@ -21,6 +22,7 @@ namespace dl
s.Read(&_indexSkin);
s.Read(&_indexParent);
s.Read(&_trans2Parent);
s.Read(&_aabb);
}
dl::Matrix4 ModelNodeRt::GetMatrixLocal()
@@ -89,16 +91,21 @@ namespace dl
// 2.骨骼空间 -> 世界空间
}
BoundingBox::BoundingBox()
void BoundingBox::Add(const BoundingBox& b)
{
}
BoundingBox::BoundingBox(Position3 imin, Position3 imax):
min{imin},
max{imax}
{
if (!b._vaild)
return;
if (!_vaild)
{
_min = b._min;
_max = b._max;
_vaild = b._vaild;
}
else
{
_min = Each::Min(_min, b._min);
_max = Each::Max(_max, b._max);
}
}
BoundingBox BoundingBox::getAABB(Matrix4 m)
@@ -108,20 +115,20 @@ namespace dl
Position3 v0, v1;
Position3 right = toShorter(m[0]);
v0 = right * this->min[0];
v1 = right * this->max[0];
v0 = right * this->_min[0];
v1 = right * this->_max[0];
min += Each::Min(v0, v1);
max += Each::Max(v0, v1);
Position3 up = toShorter(m[1]);
v0 = up * this->min[1];
v1 = up * this->max[1];
v0 = up * this->_min[1];
v1 = up * this->_max[1];
min += Each::Min(v0, v1);
max += Each::Max(v0, v1);
Position3 back = toShorter(m[2]);
v0 = back * this->min[2];
v1 = back * this->max[2];
v0 = back * this->_min[2];
v1 = back * this->_max[2];
min += Each::Min(v0, v1);
max += Each::Max(v0, v1);
+27 -14
View File
@@ -48,6 +48,28 @@ struct ModelNodeTransform
};
//
struct BoundingBox
{
Position3 _min;
Position3 _max;
bool _vaild;
public:
BoundingBox() :
_vaild{ false }
{
}
BoundingBox(Position3 min, Position3 max) :
_min{ min },
_max{ max },
_vaild{ true }
{
}
void Add(const BoundingBox& b);
BoundingBox getAABB(Matrix4 m);
};
// 模型节点子网格
struct ModelDrawCall
@@ -61,16 +83,6 @@ struct ModelDrawCall
}
};
//
struct BoundingBox
{
Position3 min;
Position3 max;
bool valid = false;
BoundingBox();
BoundingBox(Position3 min, Position3 max);
BoundingBox getAABB(Matrix4 m);
};
struct ModelPrimitive
{
uint32_t _indexBeg;
@@ -78,15 +90,15 @@ struct ModelPrimitive
uint32_t _vertexIndex;
uint32_t _vertexCount;
uint32_t _indexMaterial;
BoundingBox bb;
bool hasIndices;
BoundingBox _aabb;
bool _bHasIndices;
};
// 模型网格
struct ModelMesh
{
std::vector<ModelPrimitive> primitives;
BoundingBox bb;
std::vector<ModelPrimitive> _primitives;
BoundingBox _aabb;
};
struct ModelSkin;
@@ -116,6 +128,7 @@ struct ModelNodeRt
int _indexSkin; // 整个骨骼是在一个节点上
int _indexParent; // 父节点索引
ModelNodeTransform _trans2Parent; // 到父节点的变换(具有初始值,动画只改变srt)
BoundingBox _aabb;
bool _bNeedUpdateMatrixWorld;
bool _bNeedUpdateMatrixLocal;
+11 -49
View File
@@ -210,6 +210,7 @@ ModelObject* ModelLoader::_create_model_object(ModelData& data)
node_rt._index = node->_index;
node_rt._indexSkin = node->_indexSkin;
node_rt._trans2Parent = node->_trans2Parent;
node_rt._aabb = node->_mesh._aabb;
// parent_id 重映射
if (!node->_parent)
node_rt._indexParent = -1;
@@ -260,39 +261,16 @@ std::vector<ModelObject*> ModelLoader::_load_gltf(
log_info("子模型数:{}", model.meshes.size());
log_info("材质数:{}", model.materials.size());
// 只生成一个ModelObject(animation可能是节点动画,暂不支持)
bool has_skin = !model.skins.empty();
/*std::vector<ModelObject*> ret;
ModelObject* model_object = new ModelObject;
std::vector<Vertex>& mo_vertices = model_object->_allVertex;
std::vector<VertexBone>& mo_vertices_bone = model_object->_allVertexBone;
std::vector<uint32_t>& mo_indices = model_object->_allIndex;
std::vector<ModelObjectMaterial>& mo_material = model_object->_allMaterial;
ret.push_back(model_object);*/
ModelData model_data;
_gltf_load_material(model, res_name, model_data._allMaterial);
_gltf_load_scene(model, model_data);
/*if (!has_bone)
{
assert(mo_vertices_bone.empty());
}
else
{
assert(mo_vertices.empty());
}*/
ModelObject* obj = _create_model_object(model_data);
std::vector<ModelObject*> ret;
ret.push_back(obj);
/*log_msg("解析gltf模型完成:v_{},i_{}",
!has_bone ? mo_vertices.size() : mo_vertices_bone.size(),
mo_indices.size());*/
return ret;
}
@@ -472,9 +450,8 @@ void ModelLoader::_gltf_load_scene(const tinygltf::Model& model, ModelData& data
mat_n.Transpose();
Matrix3 mat_t = Mat::ToMatrix3(mat_world);
for (auto& pri : node->_mesh.primitives)
for (auto& pri : node->_mesh._primitives)
{
//mat_world.Dump();
// 是否多个pri会引用同一个顶点,有待验证
for (size_t i = pri._vertexIndex; i < pri._vertexIndex + pri._vertexCount; ++i)
{
@@ -491,7 +468,7 @@ void ModelLoader::_gltf_load_scene(const tinygltf::Model& model, ModelData& data
// pri -> all_sub
for (auto& iter : data._allNode)
{
for (ModelPrimitive& pri : iter->GetValue()->_mesh.primitives)
for (ModelPrimitive& pri : iter->GetValue()->_mesh._primitives)
{
ModelDrawCall sub;
sub._indexMaterial = pri._indexMaterial;
@@ -569,15 +546,8 @@ void ModelLoader::_gltf_load_node(const tinygltf::Model& model, int node,
{
model_node->_trans2Parent._mat = _mat4_cast(nn.matrix);
}
// gltf R(up:+y forward:+z) -> dl R(up:+z forward:+x)
//if (model_node_parent == nullptr)
//{
// //data._matRoot = model_node->_trans2Parent.GetMatrix();
// data._matRoot = Mat::CreateRotation(AXIS_X, Math::PI(1, 2))* data._matRoot;
// data._matRoot = Mat::CreateRotation(AXIS_Z, Math::PI(1, 2))* data._matRoot;
//}
// gltf R(up:+y forward:+z)
// dl R(up:+z forward:+x)
// 构建树
data._allNode.Add(model_node, model_node_parent);
@@ -816,22 +786,14 @@ void ModelLoader::_gltf_load_node(const tinygltf::Model& model, int node,
model_primitive._vertexIndex = vertex_start;
model_primitive._vertexCount = vertex_count;
model_primitive._indexMaterial = primitive.material;
model_primitive.hasIndices = index_count > 0;
model_primitive.bb.min = pos_min;
model_primitive.bb.max = pos_max;
model_primitive.bb.valid = true;
model_mesh.primitives.push_back(model_primitive);
model_primitive._bHasIndices = index_count > 0;
model_primitive._aabb = BoundingBox(pos_min, pos_max);
model_mesh._primitives.push_back(model_primitive);
}
// Mesh BB from BBs of primitives
for (ModelPrimitive& p : model_mesh.primitives)
for (ModelPrimitive& p : model_mesh._primitives)
{
if (p.bb.valid && !model_mesh.bb.valid)
{
model_mesh.bb = p.bb;
model_mesh.bb.valid = true;
}
model_mesh.bb.min = Each::Min(model_mesh.bb.min, p.bb.min);
model_mesh.bb.max = Each::Max(model_mesh.bb.max, p.bb.max);
model_mesh._aabb.Add(p._aabb);
}
model_node->_mesh = model_mesh;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -3
View File
@@ -355,8 +355,6 @@ void main()
//--------------------------------------------------------------------------
// 1. 获取最终表面法线(纹理法线 / 模型法线)
vec3 n = (material.normalTextureSet > -1) ? getNormal() : normalize(inNormal);
//n.y *= -1.0f;
// 2. 计算PBR核心方向向量
vec3 v = normalize(ubo.camPos - inPosition); // 点 -> 相机
vec3 l = normalize(-pass.light[0].direction); // 点 -> 光源(平行光需要取反)
@@ -463,7 +461,7 @@ void main()
outColor.rgba = material.baseColorTextureSet > -1 ? texture(diffuseMap, material.baseColorTextureSet == 0 ? inUV0 : inUV1) : vec4(1.0f);
break;
case 2:
outColor.rgb = (material.normalTextureSet > -1) ? texture(normalMap, material.normalTextureSet == 0 ? inUV0 : inUV1).rgb : normalize(inNormal);
outColor.rgb = (material.normalTextureSet > -1) ? texture(normalMap, material.normalTextureSet == 0 ? inUV0 : inUV1).rgb : abs(inNormal);
break;
case 3:
outColor.rgb = (material.occlusionTextureSet > -1) ? texture(aoMap, material.occlusionTextureSet == 0 ? inUV0 : inUV1).rrr : vec3(0.0f);