add Physics2D class; For Physics2D add DistanceJoint RevoluteJoint PrismaticJoint WeldJoint Component and serialize/deserialize code; use template to Copy Component from entity to entity; remove useless code, fix some error code

This commit is contained in:
2026-03-11 16:13:38 +08:00
parent a265e71e1a
commit 79e6631c50
15 changed files with 1003 additions and 206 deletions

View File

@ -105,7 +105,7 @@ namespace Prism
Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false);
const auto viewProj = m_EditorCamera.GetViewProjection();
Renderer2D::BeginScene(viewProj, false);
Renderer2D::DrawRotatedRect({ transform.Translation.x + collider.Offset.x, transform.Translation.y + collider.Offset.y }, collider.Size * 2.0f, transform.Rotation.z, { 0.0f, 0.0f, 1.0f, 1.0f });
Renderer2D::DrawRotatedRect({ transform.Translation.x + collider.Offset.x, transform.Translation.y + collider.Offset.y }, {transform.Scale.x * collider.Size.x, transform.Scale.y * collider.Size.y}, transform.Rotation.z, { 0.0f, 0.0f, 1.0f, 1.0f });
Renderer2D::EndScene();
Renderer::EndRenderPass();
}
@ -639,10 +639,9 @@ namespace Prism
ImVec2 maxBound = { minBound.x + windowSize.x, minBound.y + windowSize.y };
m_ViewportBounds[0] = { minBound.x, minBound.y };
m_ViewportBounds[1] = { maxBound.x, maxBound.y };
m_AllowViewportCameraEvents = ImGui::IsMouseHoveringRect(minBound, maxBound);
// ImGuizmo
if (m_GizmoType != -1 && !m_SelectionContext.empty())
if (m_GizmoType != -1 && !m_SelectionContext.empty() && m_SceneState == SceneState::Edit)
{
auto& selection = m_SelectionContext[0];

View File

@ -136,8 +136,6 @@ namespace Prism
Ref<Texture2D> m_PlayButtonTex, m_StopButtonTex, m_PauseButtonTex;
// configure button
bool m_AllowViewportCameraEvents = false;
bool m_UIShowBoundingBoxes = false;

View File

@ -32,6 +32,15 @@ namespace Prism
}
void AnimationController::SetSkeleton(const Ref<Skeleton>& skeleton)
{
m_skeleton = skeleton;
if (m_skeleton)
{
ComputeBindPoseTransforms();
}
}
void AnimationController::ComputeBoneTransforms(float time)
{
const auto boneCount = (uint32_t)m_skeleton->m_Bones.size();

View File

@ -28,16 +28,9 @@ namespace Prism
const std::vector<glm::mat4>& GetFinalBoneTransforms() const { return m_FinalBoneTransforms; }
void AnimationController::SetSkeleton(const Ref<Skeleton>& skeleton)
{
m_skeleton = skeleton;
if (m_skeleton)
{
ComputeBindPoseTransforms();
}
}
void AnimationController::SetAnimationClip(const Ref<AnimationClip>& clip) { m_currentClip = clip; }
void AnimationController::SetGlobalInverseTransform(const glm::mat4& inv) { m_GlobalInverseTransform = inv; }
void SetSkeleton(const Ref<Skeleton>& skeleton);
void SetAnimationClip(const Ref<AnimationClip>& clip) { m_currentClip = clip; }
void SetGlobalInverseTransform(const glm::mat4& inv) { m_GlobalInverseTransform = inv; }
void Play() { m_State = PLAY; }
void Pause() { m_State = PAUSE; }

View File

@ -26,6 +26,56 @@ namespace Prism
glm::mat4 Mat4FromAssimpMat4(const aiMatrix4x4& matrix);
void SceneHierarchyPanel::DrawConnectedBodySelector(Entity currentEntity, UUID& connectedBody, Scene* context, const char* popupId)
{
Entity connectedEntity = context->FindEntityByUUID(connectedBody);
std::string connectedName = connectedEntity ? connectedEntity.GetComponent<TagComponent>().Tag : "None";
ImGui::Text("Connected Body");
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
const std::string buttonLabel = connectedName + "##" + std::to_string(currentEntity.GetUUID());
if (ImGui::Button(buttonLabel.c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
ImGui::OpenPopup(popupId);
}
ImGui::PopItemWidth();
ImGui::NextColumn();
if (ImGui::BeginPopup(popupId))
{
ImGui::Text("Select Entity");
ImGui::Separator();
context->m_Registry.view<entt::entity>().each([&](auto entityID)
{
Entity e{entityID, context};
if (e.HasComponent<TagComponent>())
{
const auto& tag = e.GetComponent<TagComponent>().Tag;
if (e == currentEntity) return; // 排除自身
ImGui::PushID((const void*)(uintptr_t)(uint32_t)e);
if (ImGui::Selectable(tag.c_str(), e.GetUUID() == connectedBody))
{
connectedBody = e.GetUUID();
ImGui::CloseCurrentPopup();
}
ImGui::PopID();
}
});
ImGui::Separator();
if (ImGui::Selectable("None", connectedBody == 0))
{
connectedBody = 0;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
SceneHierarchyPanel::SceneHierarchyPanel(const Ref<Scene>& context)
: m_Context(context)
{
@ -564,6 +614,12 @@ namespace Prism
AddComponentPopup<BoxCollider2DComponent>("BoxCollider2D");
AddComponentPopup<CircleCollider2DComponent>("CircleCollider2D");
ImGui::SeparatorText("2D Joint Component");
AddComponentPopup<DistanceJoint2DComponent>("DistanceJoint2D");
AddComponentPopup<RevoluteJoint2DComponent>("RevoluteJoint2D");
AddComponentPopup<PrismaticJoint2DComponent>("PrismaticJoint2D");
AddComponentPopup<WeldJoint2DComponent>("WeldJoint2DComponent");
ImGui::SeparatorText("3D Component");
AddComponentPopup<MeshComponent>("Mesh");
AddComponentPopup<AnimationComponent>("Animation", [&](AnimationComponent& component)
@ -649,14 +705,169 @@ namespace Prism
}
DrawComponent<DistanceJoint2DComponent>("DistanceJoint2D", entity, [&](DistanceJoint2DComponent& component)
{
UI::BeginPropertyGrid();
DrawConnectedBodySelector(entity, component.ConnectedBody, m_Context.Raw(), "SelectConnectedBodyDist");
UI::Property("Local Anchor A", component.LocalAnchorA);
UI::Property("Local Anchor B", component.LocalAnchorB);
UI::Property("Length", component.Length);
UI::Separator();
UI::Property("Enable Spring", component.EnableSpring);
if (component.EnableSpring)
{
UI::Property("Lower Spring Force", component.LowerSpringForce);
UI::Property("Upper Spring Force", component.UpperSpringForce);
UI::Property("Hertz", component.Hertz);
UI::Property("Damping Ratio", component.DampingRatio);
}
UI::Separator();
UI::Property("Enable Limit", component.EnableLimit);
if (component.EnableLimit)
{
UI::Property("Min Length", component.MinLength);
UI::Property("Max Length", component.MaxLength);
}
UI::Separator();
UI::Property("Enable Motor", component.EnableMotor);
if (component.EnableMotor)
{
UI::Property("Max Motor Force", component.MaxMotorForce);
UI::Property("Motor Speed", component.MotorSpeed);
}
UI::Separator();
UI::EndPropertyGrid();
});
DrawComponent<RevoluteJoint2DComponent>("Revolute Joint 2D", entity, [&](RevoluteJoint2DComponent& component)
{
UI::BeginPropertyGrid();
// Connected Body selector
DrawConnectedBodySelector(entity, component.ConnectedBody, m_Context.Raw(), "SelectConnectedBodyDist");
// 锚点
UI::Property("Local Anchor A", component.LocalAnchorA);
UI::Property("Local Anchor B", component.LocalAnchorB);
UI::Separator();
// 弹簧参数
UI::Property("Enable Spring", component.EnableSpring);
if (component.EnableSpring)
{
UI::Property("Target Angle (rad)", component.TargetAngle);
UI::Property("Hertz", component.Hertz);
UI::Property("Damping Ratio", component.DampingRatio);
}
UI::Separator();
// 限制参数
UI::Property("Enable Limit", component.EnableLimit);
if (component.EnableLimit)
{
UI::Property("Lower Angle (rad)", component.LowerAngle);
UI::Property("Upper Angle (rad)", component.UpperAngle);
}
UI::Separator();
// 马达参数
UI::Property("Enable Motor", component.EnableMotor);
if (component.EnableMotor)
{
UI::Property("Motor Speed (rad/s)", component.MotorSpeed);
UI::Property("Max Motor Torque", component.MaxMotorTorque);
}
UI::Separator();
UI::EndPropertyGrid();
});
DrawComponent<PrismaticJoint2DComponent>("Prismatic Joint 2D", entity, [&](PrismaticJoint2DComponent& component)
{
UI::BeginPropertyGrid();
// Connected Body selector
DrawConnectedBodySelector(entity, component.ConnectedBody, m_Context.Raw(), "SelectConnectedBodyDist");
// 锚点
UI::Property("Local Anchor A", component.LocalAnchorA);
UI::Property("Local Anchor B", component.LocalAnchorB);
// 滑动轴
UI::Property("Local Axis A", component.LocalAxisA);
UI::Separator();
// 弹簧参数
UI::Property("Enable Spring", component.EnableSpring);
if (component.EnableSpring)
{
UI::Property("Target Translation", component.TargetTranslation);
UI::Property("Hertz", component.Hertz);
UI::Property("Damping Ratio", component.DampingRatio);
}
UI::Separator();
// 限制参数
UI::Property("Enable Limit", component.EnableLimit);
if (component.EnableLimit)
{
UI::Property("Lower Translation", component.LowerTranslation);
UI::Property("Upper Translation", component.UpperTranslation);
}
UI::Separator();
// 马达参数
UI::Property("Enable Motor", component.EnableMotor);
if (component.EnableMotor)
{
UI::Property("Motor Speed", component.MotorSpeed);
UI::Property("Max Motor Force", component.MaxMotorForce);
}
UI::Separator();
UI::EndPropertyGrid();
});
DrawComponent<WeldJoint2DComponent>("Weld Joint 2D", entity, [&](WeldJoint2DComponent& component)
{
UI::BeginPropertyGrid();
// Connected Body selector
DrawConnectedBodySelector(entity, component.ConnectedBody, m_Context.Raw(), "SelectConnectedBodyDist");
// 锚点
UI::Property("Local Anchor A", component.LocalAnchorA);
UI::Property("Local Anchor B", component.LocalAnchorB);
UI::Separator();
// 焊接关节特有参数(线性和角度的 Hertz 和阻尼)
UI::Property("Linear Hertz", component.LinearHertz);
UI::Property("Angular Hertz", component.AngularHertz);
UI::Property("Linear Damping Ratio", component.LinearDampingRatio);
UI::Property("Angular Damping Ratio", component.AngularDampingRatio);
UI::Separator();
UI::EndPropertyGrid();
});
DrawComponent<MeshComponent>("Mesh", entity, [](MeshComponent& meshComponent) {
UI::BeginPropertyGrid();
UI::PropertyAssetReference("Mesh", meshComponent.Mesh, AssetType::Mesh);
UI::Property("AnimationPlaying", meshComponent.Mesh->m_AnimationPlaying);
UI::Property("TimeMultiple", meshComponent.Mesh->m_TimeMultiplier);
UI::Property("AnimationTime", meshComponent.Mesh->m_AnimationTime);
UI::Property("WorldTime", meshComponent.Mesh->m_WorldTime);
// UI::Property("AnimationPlaying", meshComponent.Mesh->m_AnimationPlaying);
// UI::Property("TimeMultiple", meshComponent.Mesh->m_TimeMultiplier);
// UI::Property("AnimationTime", meshComponent.Mesh->m_AnimationTime);
// UI::Property("WorldTime", meshComponent.Mesh->m_WorldTime);
UI::EndPropertyGrid();
});

View File

@ -34,6 +34,8 @@ namespace Prism
template<class T, typename Function>
void AddComponentPopup(const char* componentName, Function function);
static void DrawConnectedBodySelector(Entity currentEntity, UUID& connectedBody, Scene* context, const char* popupId);
private:
Ref<Scene> m_Context;
Entity m_SelectionContext;

View File

@ -0,0 +1,383 @@
//
// Created by Atdunbg on 2026/3/11.
//
#include "Physics2D.h"
#include "Prism/Scene/Entity.h"
#include "Prism/Script/ScriptEngine.h"
namespace Prism
{
void Physics2D::CreateBody(Scene* scene)
{
auto sceneView = scene->m_Registry.view<Box2DWorldComponent>();
auto& world = scene->m_Registry.get<Box2DWorldComponent>(sceneView.front()).World;
{
auto view = scene->m_Registry.view<RigidBody2DComponent>();
scene->m_Physics2DBodyEntityBuffer = new Entity[view.size()];
uint32_t physicsBodyEntityBufferIndex = 0;
for (auto entity : view)
{
Entity e = { entity, scene };
auto& transform = e.Transform();
auto& rigidBody2D = scene->m_Registry.get<RigidBody2DComponent>(entity);
b2BodyDef bodyDef = b2DefaultBodyDef();
if (rigidBody2D.BodyType == RigidBody2DComponent::Type::Static)
bodyDef.type = b2_staticBody;
else if (rigidBody2D.BodyType == RigidBody2DComponent::Type::Dynamic)
bodyDef.type = b2_dynamicBody;
else if (rigidBody2D.BodyType == RigidBody2DComponent::Type::Kinematic)
bodyDef.type = b2_kinematicBody;
bodyDef.position = {transform.Translation.x, transform.Translation.y};
float rotationZ = transform.Rotation.z;
bodyDef.rotation = b2Rot{cos(rotationZ), sin(rotationZ)};
// box2D fixRotation renamed to MotionLocks
bodyDef.motionLocks = b2MotionLocks{false, false, rigidBody2D.FixedRotation};
Entity* entityStorage = &scene->m_Physics2DBodyEntityBuffer[physicsBodyEntityBufferIndex++];
*entityStorage = e;
bodyDef.userData = entityStorage;
rigidBody2D.RuntimeBodyID = b2CreateBody(world, &bodyDef);
}
}
// BoxCollider2DComponent
{
auto view = scene->m_Registry.view<BoxCollider2DComponent>();
for (auto entity : view)
{
Entity e = { entity, scene };
auto& boxCollider2D = scene->m_Registry.get<BoxCollider2DComponent>(entity);
if (e.HasComponent<RigidBody2DComponent>())
{
const auto& rigidBody2D = e.GetComponent<RigidBody2DComponent>();
const auto& transform = e.GetComponent<TransformComponent>();
PM_CORE_ASSERT(b2Body_IsValid(rigidBody2D.RuntimeBodyID));
b2BodyId bodyId = rigidBody2D.RuntimeBodyID;
glm::vec2 halfSize = boxCollider2D.Size * 0.5f;
b2Polygon boxShape = b2MakeOffsetBox(halfSize.x * transform.Scale.x, halfSize.y * transform.Scale.y, {boxCollider2D.Offset.x,boxCollider2D.Offset.y}, {1.0f, 0.0f});
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = boxCollider2D.Density;
shapeDef.material.friction = boxCollider2D.Friction;
shapeDef.enableContactEvents = true;
b2CreatePolygonShape(bodyId, &shapeDef, &boxShape);
}
}
}
// CircleCollider2DComponent
{
auto view = scene->m_Registry.view<CircleCollider2DComponent>();
for (auto entity : view)
{
Entity e = { entity, scene };
auto& circleCollider2D = scene->m_Registry.get<CircleCollider2DComponent>(entity);
if (e.HasComponent<RigidBody2DComponent>())
{
auto& rigidBody2D = e.GetComponent<RigidBody2DComponent>();
auto& transform = e.GetComponent<TransformComponent>();
PM_CORE_ASSERT(b2Body_IsValid(rigidBody2D.RuntimeBodyID));
b2BodyId bodyId = rigidBody2D.RuntimeBodyID;
b2Vec2 centor = {circleCollider2D.Offset.x, circleCollider2D.Offset.y};
b2Circle circleShape;
circleShape.center = centor;
circleShape.radius = circleCollider2D.Radius;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = circleCollider2D.Density;
shapeDef.material.friction = circleCollider2D.Friction;
shapeDef.enableContactEvents = true;
b2CreateCircleShape(bodyId, &shapeDef, &circleShape);
}
}
}
// DistanceJoint2DComponent
{
auto view = scene->m_Registry.view<DistanceJoint2DComponent>();
for (auto entity : view)
{
auto& jointComp = view.get<DistanceJoint2DComponent>(entity);
// 验证实体引用
Entity bodyA = { entity, scene };
Entity bodyB = scene->FindEntityByUUID(jointComp.ConnectedBody);
if (!bodyA || !bodyB)
{
PM_CORE_WARN("DistanceJoint has invalid body references");
continue;
}
// TODO: temp solve
if (!bodyA.HasComponent<RigidBody2DComponent>() || !bodyB.HasComponent<RigidBody2DComponent>()) continue;
auto& rbA = bodyA.GetComponent<RigidBody2DComponent>();
auto& rbB = bodyB.GetComponent<RigidBody2DComponent>();
if (!b2Body_IsValid(rbA.RuntimeBodyID) || !b2Body_IsValid(rbB.RuntimeBodyID))
{
PM_CORE_WARN("One of the bodies for DistanceJoint is not valid");
continue;
}
// 填充关节定义
b2DistanceJointDef jointDef = b2DefaultDistanceJointDef();
jointDef.base.bodyIdA = rbA.RuntimeBodyID;
jointDef.base.bodyIdB = rbB.RuntimeBodyID;
jointDef.base.localFrameA = {
{ jointComp.LocalAnchorA.x, jointComp.LocalAnchorA.y },
b2Rot_identity // 通常不需要旋转,保持单位即可
};
jointDef.base.localFrameB = {
{ jointComp.LocalAnchorB.x, jointComp.LocalAnchorB.y },
b2Rot_identity
};
jointDef.base.collideConnected = jointComp.CollideConnected;
// 设置距离关节特有参数
jointDef.length = jointComp.Length;
jointDef.enableSpring = jointComp.EnableSpring;
jointDef.lowerSpringForce = jointComp.LowerSpringForce;
jointDef.upperSpringForce = jointComp.UpperSpringForce;
jointDef.hertz = jointComp.Hertz;
jointDef.dampingRatio = jointComp.DampingRatio;
jointDef.enableLimit = jointComp.EnableLimit;
jointDef.minLength = jointComp.MinLength;
jointDef.maxLength = jointComp.MaxLength;
jointDef.enableMotor = jointComp.EnableMotor;
jointDef.maxMotorForce = jointComp.MaxMotorForce;
jointDef.motorSpeed = jointComp.MotorSpeed;
jointComp.RuntimeJointId = b2CreateDistanceJoint(world, &jointDef);
PM_CORE_ASSERT(b2Joint_IsValid(jointComp.RuntimeJointId));
}
}
// RevoluteJoint2DComponent
{
auto view = scene->m_Registry.view<RevoluteJoint2DComponent>();
for (auto entity : view)
{
auto& jointComp = view.get<RevoluteJoint2DComponent>(entity);
Entity bodyA = { entity, scene };
Entity bodyB = scene->FindEntityByUUID(jointComp.ConnectedBody);
if (!bodyA || !bodyB) continue;
// TODO: temp solve
if (!bodyA.HasComponent<RigidBody2DComponent>() || !bodyB.HasComponent<RigidBody2DComponent>()) continue;
auto& rbA = bodyA.GetComponent<RigidBody2DComponent>();
auto& rbB = bodyB.GetComponent<RigidBody2DComponent>();
if (!b2Body_IsValid(rbA.RuntimeBodyID) || !b2Body_IsValid(rbB.RuntimeBodyID))
continue;
b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
// 通用属性(必须通过 base 访问)
jointDef.base.bodyIdA = rbA.RuntimeBodyID;
jointDef.base.bodyIdB = rbB.RuntimeBodyID;
jointDef.base.localFrameA = {
{ jointComp.LocalAnchorA.x, jointComp.LocalAnchorA.y },
b2Rot_identity
};
jointDef.base.localFrameB = {
{ jointComp.LocalAnchorB.x, jointComp.LocalAnchorB.y },
b2Rot_identity
};
jointDef.base.collideConnected = jointComp.CollideConnected;
// 旋转关节特有属性
jointDef.targetAngle = jointComp.TargetAngle;
jointDef.enableSpring = jointComp.EnableSpring;
jointDef.hertz = jointComp.Hertz;
jointDef.dampingRatio = jointComp.DampingRatio;
jointDef.enableLimit = jointComp.EnableLimit;
jointDef.lowerAngle = jointComp.LowerAngle;
jointDef.upperAngle = jointComp.UpperAngle;
jointDef.enableMotor = jointComp.EnableMotor;
jointDef.motorSpeed = jointComp.MotorSpeed;
jointDef.maxMotorTorque = jointComp.MaxMotorTorque;
jointComp.RuntimeJointId = b2CreateRevoluteJoint(world, &jointDef);
PM_CORE_ASSERT(b2Joint_IsValid(jointComp.RuntimeJointId));
}
}
// PrismaticJoint2DComponent
{
auto view = scene->m_Registry.view<PrismaticJoint2DComponent>();
for (auto entity : view)
{
auto& jointComp = view.get<PrismaticJoint2DComponent>(entity);
Entity bodyA = { entity, scene };
Entity bodyB = scene->FindEntityByUUID(jointComp.ConnectedBody);
if (!bodyA || !bodyB) continue;
// TODO: temp solve
if (!bodyA.HasComponent<RigidBody2DComponent>() || !bodyB.HasComponent<RigidBody2DComponent>()) continue;
auto& rbA = bodyA.GetComponent<RigidBody2DComponent>();
auto& rbB = bodyB.GetComponent<RigidBody2DComponent>();
if (!b2Body_IsValid(rbA.RuntimeBodyID) || !b2Body_IsValid(rbB.RuntimeBodyID))
continue;
b2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();
jointDef.base.bodyIdA = rbA.RuntimeBodyID;
jointDef.base.bodyIdB = rbB.RuntimeBodyID;
float angle = atan2(jointComp.LocalAxisA.y, jointComp.LocalAxisA.x);
jointDef.base.localFrameA = {
{ jointComp.LocalAnchorA.x, jointComp.LocalAnchorA.y },
b2MakeRot(angle)
};
jointDef.base.localFrameB = {
{ jointComp.LocalAnchorB.x, jointComp.LocalAnchorB.y },
b2Rot_identity
};
jointDef.base.collideConnected = jointComp.CollideConnected;
jointDef.enableSpring = jointComp.EnableSpring;
jointDef.hertz = jointComp.Hertz;
jointDef.dampingRatio = jointComp.DampingRatio;
jointDef.targetTranslation = jointComp.TargetTranslation;
jointDef.enableLimit = jointComp.EnableLimit;
jointDef.lowerTranslation = jointComp.LowerTranslation;
jointDef.upperTranslation = jointComp.UpperTranslation;
jointDef.enableMotor = jointComp.EnableMotor;
jointDef.motorSpeed = jointComp.MotorSpeed;
jointDef.maxMotorForce = jointComp.MaxMotorForce;
jointComp.RuntimeJointId = b2CreatePrismaticJoint(world, &jointDef);
PM_CORE_ASSERT(b2Joint_IsValid(jointComp.RuntimeJointId));
}
}
// WeldJoint2DComponent
{
auto view = scene->m_Registry.view<WeldJoint2DComponent>();
for (auto entity : view)
{
auto& jointComp = view.get<WeldJoint2DComponent>(entity);
Entity bodyA = { entity, scene };
Entity bodyB = scene->FindEntityByUUID(jointComp.ConnectedBody);
if (!bodyA || !bodyB) continue;
// TODO: temp solve
if (!bodyA.HasComponent<RigidBody2DComponent>() || !bodyB.HasComponent<RigidBody2DComponent>()) continue;
auto& rbA = bodyA.GetComponent<RigidBody2DComponent>();
auto& rbB = bodyB.GetComponent<RigidBody2DComponent>();
if (!b2Body_IsValid(rbA.RuntimeBodyID) || !b2Body_IsValid(rbB.RuntimeBodyID))
continue;
b2WeldJointDef jointDef = b2DefaultWeldJointDef();
jointDef.base.bodyIdA = rbA.RuntimeBodyID;
jointDef.base.bodyIdB = rbB.RuntimeBodyID;
jointDef.base.localFrameA = {
{ jointComp.LocalAnchorA.x, jointComp.LocalAnchorA.y },
b2Rot_identity
};
jointDef.base.localFrameB = {
{ jointComp.LocalAnchorB.x, jointComp.LocalAnchorB.y },
b2Rot_identity
};
jointDef.base.collideConnected = jointComp.CollideConnected;
jointDef.linearHertz = jointComp.LinearHertz;
jointDef.angularHertz = jointComp.AngularHertz;
jointDef.linearDampingRatio = jointComp.LinearDampingRatio;
jointDef.angularDampingRatio = jointComp.AngularDampingRatio;
jointComp.RuntimeJointId = b2CreateWeldJoint(world, &jointDef);
PM_CORE_ASSERT(b2Joint_IsValid(jointComp.RuntimeJointId));
}
}
}
void Physics2D::ProcessContactEvents(const b2WorldId worldId) {
const b2ContactEvents contactEvents = b2World_GetContactEvents(worldId);
// Contact Begin Touch
for (int i = 0; i < contactEvents.beginCount; ++i) {
const b2ContactBeginTouchEvent& event = contactEvents.beginEvents[i];
auto& entityA = *static_cast<Entity *>(b2Body_GetUserData(b2Shape_GetBody(event.shapeIdA)));
auto& entityB = *static_cast<Entity *>(b2Body_GetUserData(b2Shape_GetBody(event.shapeIdB)));
if (entityA.HasComponent<ScriptComponent>() && ScriptEngine::ModuleExists(entityA.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnCollision2DBegin(entityA);
if (entityB.HasComponent<ScriptComponent>() && ScriptEngine::ModuleExists(entityB.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnCollision2DBegin(entityB);
}
// Contact Begin Touch
for (int i = 0; i < contactEvents.endCount; ++i) {
const b2ContactEndTouchEvent& event = contactEvents.endEvents[i];
auto& entityA = *static_cast<Entity *>(b2Body_GetUserData(b2Shape_GetBody(event.shapeIdA)));
auto& entityB = *static_cast<Entity *>(b2Body_GetUserData(b2Shape_GetBody(event.shapeIdB)));
if (entityA.HasComponent<ScriptComponent>() && ScriptEngine::ModuleExists(entityA.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnCollision2DEnd(entityA);
if (entityB.HasComponent<ScriptComponent>() && ScriptEngine::ModuleExists(entityB.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnCollision2DEnd(entityB);
}
}
void Physics2D::OnTransformConstruct(entt::registry& registry, entt::entity entity)
{
// PM_CORE_TRACE("Transform Component constructed!");
}
void Physics2D::OnScriptComponentConstruct(entt::registry& registry, entt::entity entity)
{
// Note: there should be exactly one scene component per registry
const auto sceneView = registry.view<SceneComponent>();
const UUID sceneID = registry.get<SceneComponent>(sceneView.front()).SceneID;
Scene* scene = Scene::s_ActiveScenes[sceneID];
const auto entityID = registry.get<IDComponent>(entity).ID;
PM_CORE_ASSERT(scene->m_EntityIDMap.find(entityID) != scene->m_EntityIDMap.end());
ScriptEngine::InitScriptEntity(scene->m_EntityIDMap.at(entityID));
}
void Physics2D::OnScriptComponentDestroy(entt::registry& registry, const entt::entity entity)
{
const auto sceneView = registry.view<SceneComponent>();
const UUID sceneID = registry.get<SceneComponent>(sceneView.front()).SceneID;
const auto entityID = registry.get<IDComponent>(entity).ID;
ScriptEngine::OnScriptComponentDestroyed(sceneID, entityID);
}
}

View File

@ -0,0 +1,41 @@
//
// Created by Atdunbg on 2026/3/11.
//
#ifndef PRISM_PHYSICS2D_H
#define PRISM_PHYSICS2D_H
#include <box2d/box2d.h>
#include "entt/entity/registry.hpp"
#include "Prism/Scene/Scene.h"
namespace Prism
{
struct Box2DWorldComponent
{
b2WorldId World{};
Box2DWorldComponent() = delete;
Box2DWorldComponent(const b2Vec2& gravity) {
b2WorldDef worldDef = b2DefaultWorldDef();
worldDef.gravity = gravity;
World = b2CreateWorld(&worldDef);
}
};
class Physics2D
{
public:
static void CreateBody(Scene* scene);
static void ProcessContactEvents(b2WorldId worldId);
static void OnTransformConstruct(entt::registry& registry, entt::entity entity);
static void OnScriptComponentConstruct(entt::registry& registry, entt::entity entity);
static void OnScriptComponentDestroy(entt::registry& registry, entt::entity entity);
};
}
#endif //PRISM_PHYSICS2D_H

View File

@ -9,7 +9,7 @@ namespace Prism
template<typename T, typename ConditionFunction>
static bool RemoveIfExists(std::vector<T>& vector, ConditionFunction condition)
{
for (std::vector<T>::iterator it = vector.begin(); it != vector.end(); ++it)
for (auto it = vector.begin(); it != vector.end(); ++it)
{
if (condition(*it))
{

View File

@ -176,10 +176,10 @@ namespace Prism
// Animation
bool m_IsAnimated = false;
bool m_AnimationPlaying = false;
float m_AnimationTime = 0.0f;
float m_WorldTime = 0.0f;
float m_TimeMultiplier = 1.0f;
// bool m_AnimationPlaying = false;
// float m_AnimationTime = 0.0f;
// float m_WorldTime = 0.0f;
// float m_TimeMultiplier = 1.0f;
std::string m_FilePath;
private:

View File

@ -432,7 +432,6 @@ namespace Prism
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
s_Data.CompositeShader->SetFloat("u_EnableBloom", s_Data.EnableBloom);
s_Data.CompositeShader->SetInt("u_DepthTexture", 1);
s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture();
Renderer::Submit([]()

View File

@ -187,6 +187,99 @@ namespace Prism
CircleCollider2DComponent(const CircleCollider2DComponent& other) = default;
};
struct DistanceJoint2DComponent
{
UUID ConnectedBody = 0;
bool CollideConnected = false;
// 局部锚点(相对于刚体原点)
glm::vec2 LocalAnchorA = {0.0f, 0.0f};
glm::vec2 LocalAnchorB = {0.0f, 0.0f};
// 距离关节特有参数与b2DistanceJointDef对应
float Length = 1.0f;
bool EnableSpring = false;
float LowerSpringForce = 0.0f;
float UpperSpringForce = 0.0f;
float Hertz = 0.0f;
float DampingRatio = 0.0f;
bool EnableLimit = false;
float MinLength = 0.0f;
float MaxLength = 1.0f;
bool EnableMotor = false;
float MaxMotorForce = 0.0f;
float MotorSpeed = 0.0f;
b2JointId RuntimeJointId = b2_nullJointId;
};
struct RevoluteJoint2DComponent
{
UUID ConnectedBody = 0;
bool CollideConnected = false;
glm::vec2 LocalAnchorA = {0.0f, 0.0f};
glm::vec2 LocalAnchorB = {0.0f, 0.0f};
// 旋转关节特有属性(直接对应 b2RevoluteJointDef 成员)
float TargetAngle = 0.0f; // 弹簧驱动的目标角度(弧度)
bool EnableSpring = false;
float Hertz = 0.0f;
float DampingRatio = 0.0f;
bool EnableLimit = false;
float LowerAngle = 0.0f;
float UpperAngle = 0.0f;
bool EnableMotor = false;
float MotorSpeed = 0.0f;
float MaxMotorTorque = 0.0f;
b2JointId RuntimeJointId = b2_nullJointId;
};
struct PrismaticJoint2DComponent
{
UUID ConnectedBody = 0;
bool CollideConnected = false;
glm::vec2 LocalAnchorA = {0.0f, 0.0f}; // 局部锚点 A
glm::vec2 LocalAnchorB = {0.0f, 0.0f}; // 局部锚点 B
glm::vec2 LocalAxisA = {1.0f, 0.0f}; // 局部轴方向(在 BodyA 空间中,应归一化)
// 弹簧参数
bool EnableSpring = false;
float Hertz = 0.0f;
float DampingRatio = 0.0f;
float TargetTranslation = 0.0f;
// 限制参数
bool EnableLimit = false;
float LowerTranslation = 0.0f;
float UpperTranslation = 0.0f;
// 马达参数
bool EnableMotor = false;
float MotorSpeed = 0.0f;
float MaxMotorForce = 0.0f;
b2JointId RuntimeJointId = b2_nullJointId;
};
struct WeldJoint2DComponent
{
UUID ConnectedBody = 0;
bool CollideConnected = false;
glm::vec2 LocalAnchorA = {0.0f, 0.0f};
glm::vec2 LocalAnchorB = {0.0f, 0.0f};
float LinearHertz = 0.0f;
float AngularHertz = 0.0f;
float LinearDampingRatio = 0.0f;
float AngularDampingRatio = 0.0f;
b2JointId RuntimeJointId = b2_nullJointId;
};
struct SphereColliderComponent
{
@ -305,6 +398,22 @@ namespace Prism
}
};
template<typename... Components>
struct ComponentGroup
{
};
using AllComponent = ComponentGroup<
TagComponent, RelationshipComponent,
TransformComponent, SpriteRendererComponent,
RigidBody2DComponent, BoxCollider2DComponent, CircleCollider2DComponent,
DistanceJoint2DComponent, RevoluteJoint2DComponent, PrismaticJoint2DComponent, WeldJoint2DComponent,
MeshComponent,
RigidBodyComponent, BoxColliderComponent, SphereColliderComponent, CapsuleColliderComponent, MeshColliderComponent,
AnimationComponent,
DirectionalLightComponent, SkyLightComponent,
ScriptComponent,
CameraComponent
>;
}

View File

@ -8,7 +8,6 @@
#include "Entity.h"
#include "Prism/Renderer/SceneRenderer.h"
#include "Prism/Script/ScriptEngine.h"
#include <box2d/box2d.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/quaternion.hpp>
@ -22,102 +21,26 @@
#include "Prism/Asset/AssetsManager.h"
#include "Prism/Core/Input.h"
#include "Prism/Core/Math/Math.h"
#include "Prism/Physics/Physics2D.h"
#include "Prism/Renderer/Renderer.h"
namespace Prism
{
std::unordered_map<UUID, Scene*> s_ActiveScenes;
std::unordered_map<UUID, Scene*> Scene::s_ActiveScenes;
static uint32_t s_SceneIDCounter = 0;
struct SceneComponent
{
UUID SceneID;
};
struct Box2DWorldComponent
{
b2WorldId World;
Box2DWorldComponent() = delete;
Box2DWorldComponent(const b2Vec2& gravity) {
b2WorldDef worldDef = b2DefaultWorldDef();
worldDef.gravity = gravity;
World = b2CreateWorld(&worldDef);
}
};
void ProcessContactEvents(const b2WorldId worldId) {
const b2ContactEvents contactEvents = b2World_GetContactEvents(worldId);
// Contact Begin Touch
for (int i = 0; i < contactEvents.beginCount; ++i) {
const b2ContactBeginTouchEvent& event = contactEvents.beginEvents[i];
auto& entityA = *static_cast<Entity *>(b2Body_GetUserData(b2Shape_GetBody(event.shapeIdA)));
auto& entityB = *static_cast<Entity *>(b2Body_GetUserData(b2Shape_GetBody(event.shapeIdB)));
if (entityA.HasComponent<ScriptComponent>() && ScriptEngine::ModuleExists(entityA.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnCollision2DBegin(entityA);
if (entityB.HasComponent<ScriptComponent>() && ScriptEngine::ModuleExists(entityB.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnCollision2DBegin(entityB);
}
// Contact Begin Touch
for (int i = 0; i < contactEvents.endCount; ++i) {
const b2ContactEndTouchEvent& event = contactEvents.endEvents[i];
auto& entityA = *static_cast<Entity *>(b2Body_GetUserData(b2Shape_GetBody(event.shapeIdA)));
auto& entityB = *static_cast<Entity *>(b2Body_GetUserData(b2Shape_GetBody(event.shapeIdB)));
if (entityA.HasComponent<ScriptComponent>() && ScriptEngine::ModuleExists(entityA.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnCollision2DEnd(entityA);
if (entityB.HasComponent<ScriptComponent>() && ScriptEngine::ModuleExists(entityB.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnCollision2DEnd(entityB);
}
}
void OnTransformConstruct(entt::registry& registry, entt::entity entity)
{
// PM_CORE_TRACE("Transform Component constructed!");
}
void OnScriptComponentConstruct(entt::registry& registry, entt::entity entity)
{
// Note: there should be exactly one scene component per registry
const auto sceneView = registry.view<SceneComponent>();
UUID sceneID = registry.get<SceneComponent>(sceneView.front()).SceneID;
Scene* scene = s_ActiveScenes[sceneID];
auto entityID = registry.get<IDComponent>(entity).ID;
PM_CORE_ASSERT(scene->m_EntityIDMap.find(entityID) != scene->m_EntityIDMap.end());
ScriptEngine::InitScriptEntity(scene->m_EntityIDMap.at(entityID));
}
void OnScriptComponentDestroy(entt::registry& registry, entt::entity entity)
{
const auto sceneView = registry.view<SceneComponent>();
const UUID sceneID = registry.get<SceneComponent>(sceneView.front()).SceneID;
const auto entityID = registry.get<IDComponent>(entity).ID;
ScriptEngine::OnScriptComponentDestroyed(sceneID, entityID);
}
Scene::Scene(const std::string& debugName, const bool isEditorScene)
: m_SceneID(++s_SceneIDCounter), m_DebugName(debugName)
{
m_Registry.on_construct<TransformComponent>().connect<&OnTransformConstruct>();
m_Registry.on_construct<ScriptComponent>().connect<&OnScriptComponentConstruct>();
m_Registry.on_construct<TransformComponent>().connect<&Physics2D::OnTransformConstruct>();
m_Registry.on_construct<ScriptComponent>().connect<&Physics2D::OnScriptComponentConstruct>();
m_SceneEntity = m_Registry.create();
m_Registry.emplace<SceneComponent>(m_SceneEntity, m_SceneID);
@ -152,14 +75,12 @@ namespace Prism
m_CameraIcon = AssetsManager::GetAsset<Texture2D>("assets/editor/Camera.png");
m_LightIcon = AssetsManager::GetAsset<Texture2D>("assets/editor/light.png");
}
void Scene::OnShutdown()
{
/*
auto b2WorldView = m_Registry.view<Box2DWorldComponent>();
b2DestroyWorld(m_Registry.get<Box2DWorldComponent>(m_SceneEntity).World);
*/
}
void Scene::OnUpdate(TimeStep ts)
@ -175,7 +96,7 @@ namespace Prism
b2World_Step(box2DWorld, ts, positionIterations);
// Process Contact Envents box2d version 3.0^ should impl contact event after b2World_Step
ProcessContactEvents(box2DWorld);
Physics2D::ProcessContactEvents(box2DWorld);
{
const auto view = m_Registry.view<RigidBody2DComponent>();
@ -283,7 +204,7 @@ namespace Prism
m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod);
SceneRenderer::BeginScene(this, { static_cast<Camera>(camera), cameraViewMatrix });
SceneRenderer::BeginScene(this, { static_cast<Camera>(camera), cameraViewMatrix }, false);
{
const auto group = m_Registry.group<MeshComponent>(entt::get<TransformComponent>);
for (const auto entity : group)
@ -367,8 +288,8 @@ namespace Prism
m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod);
// TODO: this value should be storage and can modify
SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix(), 0.1f, 1000.0f, 45.0f});
// TODO: Renderer2D cannot blend with Renderer3D
SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix(), 0.1f, 1000.0f, 45.0f}, false);
{
const auto group = m_Registry.group<MeshComponent>(entt::get<TransformComponent>);
{
@ -471,97 +392,7 @@ namespace Prism
// Box2D physics
auto sceneView = m_Registry.view<Box2DWorldComponent>();
auto& world = m_Registry.get<Box2DWorldComponent>(sceneView.front()).World;
{
auto view = m_Registry.view<RigidBody2DComponent>();
m_Physics2DBodyEntityBuffer = new Entity[view.size()];
uint32_t physicsBodyEntityBufferIndex = 0;
for (auto entity : view)
{
Entity e = { entity, this };
auto& transform = e.Transform();
auto& rigidBody2D = m_Registry.get<RigidBody2DComponent>(entity);
b2BodyDef bodyDef = b2DefaultBodyDef();
if (rigidBody2D.BodyType == RigidBody2DComponent::Type::Static)
bodyDef.type = b2_staticBody;
else if (rigidBody2D.BodyType == RigidBody2DComponent::Type::Dynamic)
bodyDef.type = b2_dynamicBody;
else if (rigidBody2D.BodyType == RigidBody2DComponent::Type::Kinematic)
bodyDef.type = b2_kinematicBody;
bodyDef.position = {transform.Translation.x, transform.Translation.y};
float rotationZ = transform.Rotation.z;
bodyDef.rotation = b2Rot{cos(rotationZ), sin(rotationZ)};
// box2D fixRotation renamed to motionlocks
bodyDef.motionLocks = b2MotionLocks{false, false, rigidBody2D.FixedRotation};
Entity* entityStorage = &m_Physics2DBodyEntityBuffer[physicsBodyEntityBufferIndex++];
*entityStorage = e;
bodyDef.userData = entityStorage;
rigidBody2D.RuntimeBodyID = b2CreateBody(world, &bodyDef);
}
}
{
auto view = m_Registry.view<BoxCollider2DComponent>();
for (auto entity : view)
{
Entity e = { entity, this };
auto& boxCollider2D = m_Registry.get<BoxCollider2DComponent>(entity);
if (e.HasComponent<RigidBody2DComponent>())
{
const auto& rigidBody2D = e.GetComponent<RigidBody2DComponent>();
PM_CORE_ASSERT(b2Body_IsValid(rigidBody2D.RuntimeBodyID));
b2BodyId bodyId = rigidBody2D.RuntimeBodyID;
b2Polygon boxShape = b2MakeOffsetBox(boxCollider2D.Size.x, boxCollider2D.Size.y, {boxCollider2D.Offset.x,boxCollider2D.Offset.y}, {1.0f, 0.0f});
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = boxCollider2D.Density;
shapeDef.material.friction = boxCollider2D.Friction;
shapeDef.enableContactEvents = true;
b2CreatePolygonShape(bodyId, &shapeDef, &boxShape);
}
}
}
{
auto view = m_Registry.view<CircleCollider2DComponent>();
for (auto entity : view)
{
Entity e = { entity, this };
auto& circleCollider2D = m_Registry.get<CircleCollider2DComponent>(entity);
if (e.HasComponent<RigidBody2DComponent>())
{
auto& rigidBody2D = e.GetComponent<RigidBody2DComponent>();
PM_CORE_ASSERT(b2Body_IsValid(rigidBody2D.RuntimeBodyID));
b2BodyId bodyId = rigidBody2D.RuntimeBodyID;
b2Vec2 centor = {circleCollider2D.Offset.x, circleCollider2D.Offset.y};
b2Circle circleShape;
circleShape.center = centor;
circleShape.radius = circleCollider2D.Radius;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = circleCollider2D.Density;
shapeDef.material.friction = circleCollider2D.Friction;
shapeDef.enableContactEvents = true;
b2CreateCircleShape(bodyId, &shapeDef, &circleShape);
}
}
}
Physics2D::CreateBody(this);
{
auto view = m_Registry.view<RigidBodyComponent>();
@ -663,9 +494,14 @@ namespace Prism
auto& destComponent = dstRegistry.emplace_or_replace<T>(destEntity, srcComponent);
}
}
template<typename ... Component>
static void CopyComponent(ComponentGroup<Component ...>, entt::registry& dst, entt::registry& src, const std::unordered_map<UUID, entt::entity>& enttMap)
{
(CopyComponent<Component>(dst, src, enttMap), ...);
}
template<typename T>
static void CopyComponentIfExists(entt::entity dst, entt::entity src, entt::registry& registry)
static void CopyComponentIfExists(const entt::entity dst, const entt::entity src, entt::registry& registry)
{
if (registry.storage<T>().contains(src))
{
@ -673,6 +509,11 @@ namespace Prism
registry.emplace_or_replace<T>(dst, srcComponent);
}
}
template<typename ... Component>
static auto CopyComponentIfExists(ComponentGroup<Component...>, const entt::entity dst, const entt::entity src, entt::registry& registry) -> void
{
(CopyComponentIfExists<Component>(dst, src, registry), ...);
}
void Scene::DuplicateEntity(Entity entity)
@ -683,6 +524,8 @@ namespace Prism
else
newEntity = CreateEntity();
CopyComponentIfExists(AllComponent{},newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
/*
CopyComponentIfExists<TransformComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<RelationshipComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
@ -701,6 +544,14 @@ namespace Prism
CopyComponentIfExists<SphereColliderComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<CapsuleColliderComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<MeshColliderComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<AnimationComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<DistanceJoint2DComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<RevoluteJoint2DComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<PrismaticJoint2DComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<WeldJoint2DComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
*/
}
Entity Scene::FindEntityByTag(const std::string &tag) {
@ -759,7 +610,8 @@ namespace Prism
Entity e = target->CreateEntityWithID(uuid, "", true);
enttMap[uuid] = e.m_EntityHandle;
}
CopyComponent(AllComponent{},target->m_Registry, m_Registry, enttMap);
/*
CopyComponent<TagComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<TransformComponent>(target->m_Registry, m_Registry, enttMap);
@ -779,6 +631,12 @@ namespace Prism
CopyComponent<SphereColliderComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<CapsuleColliderComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<MeshColliderComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<AnimationComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<DistanceJoint2DComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<RevoluteJoint2DComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<PrismaticJoint2DComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<WeldJoint2DComponent>(target->m_Registry, m_Registry, enttMap);
*/
const auto& entityInstanceMap = ScriptEngine::GetEntityInstanceMap();
if (entityInstanceMap.find(target->GetUUID()) != entityInstanceMap.end())

View File

@ -46,6 +46,11 @@ namespace Prism
class Entity;
using EntityMap = std::unordered_map<UUID, Entity>;
struct SceneComponent
{
UUID SceneID;
};
class PRISM_API Scene : public RefCounted
{
public:
@ -141,8 +146,10 @@ namespace Prism
Ref<Texture2D> m_CameraIcon;
Ref<Texture2D> m_LightIcon;
static std::unordered_map<UUID, Scene*> s_ActiveScenes;
private:
friend class Entity;
friend class Physics2D;
friend class Renderer3D;
friend class SceneRenderer;
friend class SceneSerializer;

View File

@ -370,6 +370,109 @@ namespace Prism
out << YAML::EndMap; // CircleCollider2DComponent
}
if (entity.HasComponent<DistanceJoint2DComponent>())
{
out << YAML::Key << "DistanceJoint2DComponent";
out << YAML::BeginMap; // DistanceJoint2DComponent
const auto& distanceJoint2DComponent = entity.GetComponent<DistanceJoint2DComponent>();
out << YAML::Key << "ConnectedBody" << YAML::Value << distanceJoint2DComponent.ConnectedBody;
out << YAML::Key << "LocalAnchorA" << YAML::Value << distanceJoint2DComponent.LocalAnchorA;
out << YAML::Key << "LocalAnchorB" << YAML::Value << distanceJoint2DComponent.LocalAnchorB;
out << YAML::Key << "Length" << YAML::Value << distanceJoint2DComponent.Length;
out << YAML::Key << "EnableSpring" << YAML::Value << distanceJoint2DComponent.EnableSpring;
out << YAML::Key << "LowerSpringForce" << YAML::Value << distanceJoint2DComponent.LowerSpringForce;
out << YAML::Key << "UpperSpringForce" << YAML::Value << distanceJoint2DComponent.UpperSpringForce;
out << YAML::Key << "Hertz" << YAML::Value << distanceJoint2DComponent.Hertz;
out << YAML::Key << "DampingRatio" << YAML::Value << distanceJoint2DComponent.DampingRatio;
out << YAML::Key << "EnableLimit" << YAML::Value << distanceJoint2DComponent.EnableLimit;
out << YAML::Key << "MinLength" << YAML::Value << distanceJoint2DComponent.MinLength;
out << YAML::Key << "MaxLength" << YAML::Value << distanceJoint2DComponent.MaxLength;
out << YAML::Key << "EnableMotor" << YAML::Value << distanceJoint2DComponent.EnableMotor;
out << YAML::Key << "MaxMotorForce" << YAML::Value << distanceJoint2DComponent.MaxMotorForce;
out << YAML::Key << "MotorSpeed" << YAML::Value << distanceJoint2DComponent.MotorSpeed;
out << YAML::EndMap; // CircleBollider2DComponent
}
if (entity.HasComponent<RevoluteJoint2DComponent>())
{
out << YAML::Key << "RevoluteJoint2DComponent";
out << YAML::BeginMap;
const auto& component = entity.GetComponent<RevoluteJoint2DComponent>();
out << YAML::Key << "ConnectedBody" << YAML::Value << component.ConnectedBody;
out << YAML::Key << "CollideConnected" << YAML::Value << component.CollideConnected;
out << YAML::Key << "LocalAnchorA" << YAML::Value << component.LocalAnchorA;
out << YAML::Key << "LocalAnchorB" << YAML::Value << component.LocalAnchorB;
out << YAML::Key << "TargetAngle" << YAML::Value << component.TargetAngle;
out << YAML::Key << "EnableSpring" << YAML::Value << component.EnableSpring;
out << YAML::Key << "Hertz" << YAML::Value << component.Hertz;
out << YAML::Key << "DampingRatio" << YAML::Value << component.DampingRatio;
out << YAML::Key << "EnableLimit" << YAML::Value << component.EnableLimit;
out << YAML::Key << "LowerAngle" << YAML::Value << component.LowerAngle;
out << YAML::Key << "UpperAngle" << YAML::Value << component.UpperAngle;
out << YAML::Key << "EnableMotor" << YAML::Value << component.EnableMotor;
out << YAML::Key << "MotorSpeed" << YAML::Value << component.MotorSpeed;
out << YAML::Key << "MaxMotorTorque" << YAML::Value << component.MaxMotorTorque;
out << YAML::EndMap;
}
if (entity.HasComponent<PrismaticJoint2DComponent>())
{
out << YAML::Key << "PrismaticJoint2DComponent";
out << YAML::BeginMap;
const auto& component = entity.GetComponent<PrismaticJoint2DComponent>();
out << YAML::Key << "ConnectedBody" << YAML::Value << component.ConnectedBody;
out << YAML::Key << "CollideConnected" << YAML::Value << component.CollideConnected;
out << YAML::Key << "LocalAnchorA" << YAML::Value << component.LocalAnchorA;
out << YAML::Key << "LocalAnchorB" << YAML::Value << component.LocalAnchorB;
out << YAML::Key << "LocalAxisA" << YAML::Value << component.LocalAxisA;
out << YAML::Key << "EnableSpring" << YAML::Value << component.EnableSpring;
out << YAML::Key << "Hertz" << YAML::Value << component.Hertz;
out << YAML::Key << "DampingRatio" << YAML::Value << component.DampingRatio;
out << YAML::Key << "TargetTranslation" << YAML::Value << component.TargetTranslation;
out << YAML::Key << "EnableLimit" << YAML::Value << component.EnableLimit;
out << YAML::Key << "LowerTranslation" << YAML::Value << component.LowerTranslation;
out << YAML::Key << "UpperTranslation" << YAML::Value << component.UpperTranslation;
out << YAML::Key << "EnableMotor" << YAML::Value << component.EnableMotor;
out << YAML::Key << "MotorSpeed" << YAML::Value << component.MotorSpeed;
out << YAML::Key << "MaxMotorForce" << YAML::Value << component.MaxMotorForce;
out << YAML::EndMap;
}
if (entity.HasComponent<WeldJoint2DComponent>())
{
out << YAML::Key << "WeldJoint2DComponent";
out << YAML::BeginMap;
const auto& component = entity.GetComponent<WeldJoint2DComponent>();
out << YAML::Key << "ConnectedBody" << YAML::Value << component.ConnectedBody;
out << YAML::Key << "CollideConnected" << YAML::Value << component.CollideConnected;
out << YAML::Key << "LocalAnchorA" << YAML::Value << component.LocalAnchorA;
out << YAML::Key << "LocalAnchorB" << YAML::Value << component.LocalAnchorB;
out << YAML::Key << "LinearHertz" << YAML::Value << component.LinearHertz;
out << YAML::Key << "AngularHertz" << YAML::Value << component.AngularHertz;
out << YAML::Key << "LinearDampingRatio" << YAML::Value << component.LinearDampingRatio;
out << YAML::Key << "AngularDampingRatio" << YAML::Value << component.AngularDampingRatio;
out << YAML::EndMap;
}
if (entity.HasComponent<RigidBodyComponent>())
{
out << YAML::Key << "RigidBodyComponent";
@ -825,6 +928,91 @@ namespace Prism
component.Friction = boxCollider2DComponent["Friction"] ? boxCollider2DComponent["Friction"].as<float>() : 1.0f;
}
if (auto distanceJoint2DComponent = entity["DistanceJoint2DComponent"])
{
auto& component = deserializedEntity.AddComponent<DistanceJoint2DComponent>();
component.ConnectedBody = distanceJoint2DComponent["ConnectedBody"].as<uint64_t>();
component.LocalAnchorA = distanceJoint2DComponent["LocalAnchorA"].as<glm::vec2>();
component.LocalAnchorB = distanceJoint2DComponent["LocalAnchorB"].as<glm::vec2>();
component.Length = distanceJoint2DComponent["Length"].as<float>();
component.EnableSpring = distanceJoint2DComponent["EnableSpring"].as<bool>();
component.LowerSpringForce = distanceJoint2DComponent["LowerSpringForce"].as<float>();
component.UpperSpringForce = distanceJoint2DComponent["UpperSpringForce"].as<float>();
component.Hertz = distanceJoint2DComponent["Hertz"].as<float>();
component.DampingRatio = distanceJoint2DComponent["DampingRatio"].as<float>();
component.EnableLimit = distanceJoint2DComponent["EnableLimit"].as<bool>();
component.MinLength = distanceJoint2DComponent["MinLength"].as<float>();
component.MaxLength = distanceJoint2DComponent["MaxLength"].as<float>();
component.EnableMotor = distanceJoint2DComponent["EnableMotor"].as<bool>();
component.MaxMotorForce = distanceJoint2DComponent["MaxMotorForce"].as<float>();
component.MotorSpeed = distanceJoint2DComponent["MotorSpeed"].as<float>();
}
if (auto revoluteJoint = entity["RevoluteJoint2DComponent"])
{
auto& component = deserializedEntity.AddComponent<RevoluteJoint2DComponent>();
component.ConnectedBody = revoluteJoint["ConnectedBody"].as<uint64_t>();
component.CollideConnected = revoluteJoint["CollideConnected"].as<bool>();
component.LocalAnchorA = revoluteJoint["LocalAnchorA"].as<glm::vec2>();
component.LocalAnchorB = revoluteJoint["LocalAnchorB"].as<glm::vec2>();
component.TargetAngle = revoluteJoint["TargetAngle"].as<float>();
component.EnableSpring = revoluteJoint["EnableSpring"].as<bool>();
component.Hertz = revoluteJoint["Hertz"].as<float>();
component.DampingRatio = revoluteJoint["DampingRatio"].as<float>();
component.EnableLimit = revoluteJoint["EnableLimit"].as<bool>();
component.LowerAngle = revoluteJoint["LowerAngle"].as<float>();
component.UpperAngle = revoluteJoint["UpperAngle"].as<float>();
component.EnableMotor = revoluteJoint["EnableMotor"].as<bool>();
component.MotorSpeed = revoluteJoint["MotorSpeed"].as<float>();
component.MaxMotorTorque = revoluteJoint["MaxMotorTorque"].as<float>();
}
if (auto prismaticJoint = entity["PrismaticJoint2DComponent"])
{
auto& component = deserializedEntity.AddComponent<PrismaticJoint2DComponent>();
component.ConnectedBody = prismaticJoint["ConnectedBody"].as<uint64_t>();
component.CollideConnected = prismaticJoint["CollideConnected"].as<bool>();
component.LocalAnchorA = prismaticJoint["LocalAnchorA"].as<glm::vec2>();
component.LocalAnchorB = prismaticJoint["LocalAnchorB"].as<glm::vec2>();
component.LocalAxisA = prismaticJoint["LocalAxisA"].as<glm::vec2>();
component.EnableSpring = prismaticJoint["EnableSpring"].as<bool>();
component.Hertz = prismaticJoint["Hertz"].as<float>();
component.DampingRatio = prismaticJoint["DampingRatio"].as<float>();
component.TargetTranslation = prismaticJoint["TargetTranslation"].as<float>();
component.EnableLimit = prismaticJoint["EnableLimit"].as<bool>();
component.LowerTranslation = prismaticJoint["LowerTranslation"].as<float>();
component.UpperTranslation = prismaticJoint["UpperTranslation"].as<float>();
component.EnableMotor = prismaticJoint["EnableMotor"].as<bool>();
component.MotorSpeed = prismaticJoint["MotorSpeed"].as<float>();
component.MaxMotorForce = prismaticJoint["MaxMotorForce"].as<float>();
}
if (auto weldJoint = entity["WeldJoint2DComponent"])
{
auto& component = deserializedEntity.AddComponent<WeldJoint2DComponent>();
component.ConnectedBody = weldJoint["ConnectedBody"].as<uint64_t>();
component.CollideConnected = weldJoint["CollideConnected"].as<bool>();
component.LocalAnchorA = weldJoint["LocalAnchorA"].as<glm::vec2>();
component.LocalAnchorB = weldJoint["LocalAnchorB"].as<glm::vec2>();
component.LinearHertz = weldJoint["LinearHertz"].as<float>();
component.AngularHertz = weldJoint["AngularHertz"].as<float>();
component.LinearDampingRatio = weldJoint["LinearDampingRatio"].as<float>();
component.AngularDampingRatio = weldJoint["AngularDampingRatio"].as<float>();
}
if (auto circleCollider2DComponent = entity["CircleCollider2DComponent"])
{
auto& component = deserializedEntity.AddComponent<CircleCollider2DComponent>();