refactor(physics): integrated physics world into workspace (likely for bugs to ensue. I'll deal with that later)

This commit is contained in:
maelstrom 2025-04-11 00:34:39 +02:00
parent 35f49b8a45
commit 6a017b2238
11 changed files with 213 additions and 220 deletions

View file

@ -1,7 +1,6 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "objects/part.h"
#include "physics/simulation.h"
#include "rendering/renderer.h"
#include "common.h"
@ -34,7 +33,6 @@ int main() {
glewInit();
gDataModel->Init();
simulationInit();
renderInit(window, 1200, 900);
// Baseplate
@ -56,7 +54,7 @@ int main() {
for (InstanceRef inst : gWorkspace()->GetChildren()) {
if (inst->GetClass()->className != "Part") continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
syncPartPhysics(part);
gWorkspace()->SyncPartPhysics(part);
}
float lastTime = glfwGetTime();
@ -65,7 +63,7 @@ int main() {
lastTime = glfwGetTime();
processInput(window);
physicsStep(deltaTime);
gWorkspace()->PhysicsStep(deltaTime);
render(window);
glfwSwapBuffers(window);
@ -103,15 +101,15 @@ void processInput(GLFWwindow* window) {
shiftFactor *= deltaTime;
if (glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS) {
// lastPart->rotation *= glm::angleAxis(shiftFactor, glm::vec3(1, 0, 0));
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
if (glfwGetKey(window, GLFW_KEY_Y) == GLFW_PRESS) {
// lastPart->rotation *= glm::angleAxis(shiftFactor, glm::vec3(0, 1, 0));
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
if (glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS) {
// lastPart->rotation *= glm::angleAxis(shiftFactor, glm::vec3(0, 0, 1));
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
}
}
@ -152,35 +150,35 @@ void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods
.size = glm::vec3(1, 1, 1),
.color = glm::vec3(1.0f, 0.5f, 0.31f),
}));
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
float shiftFactor = (mods & GLFW_MOD_SHIFT) ? -0.2 : 0.2;
if (mode == 0) {
if (key == GLFW_KEY_X && action == GLFW_PRESS) {
// lastPart->position.x += shiftFactor;
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
if (key == GLFW_KEY_Y && action == GLFW_PRESS) {
// lastPart->position.y += shiftFactor;
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
if (key == GLFW_KEY_Z && action == GLFW_PRESS) {
// lastPart->position.z += shiftFactor;
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
} else if (mode == 1) {
if (key == GLFW_KEY_X && action == GLFW_PRESS) {
lastPart->size.x += shiftFactor;
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
if (key == GLFW_KEY_Y && action == GLFW_PRESS) {
lastPart->size.y += shiftFactor;
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
if (key == GLFW_KEY_Z && action == GLFW_PRESS) {
lastPart->size.z += shiftFactor;
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
}

View file

@ -88,14 +88,12 @@ bool Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) {
this->_workspace = std::nullopt;
}
// Algorithm for updating descendant's dataModel and workspace fields
if (oldDataModel != _dataModel || oldWorkspace != _workspace)
updateAncestry();
updateAncestry(this->shared<Instance>(), newParent);
return true;
}
void Instance::updateAncestry() {
void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedChild, std::optional<std::shared_ptr<Instance>> newParent) {
if (GetParent()) {
this->_dataModel = GetParent().value()->GetClass() == &DataModel::TYPE ? std::make_optional(std::dynamic_pointer_cast<DataModel>(GetParent().value())) : GetParent().value()->dataModel();
this->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::make_optional(std::dynamic_pointer_cast<Workspace>(GetParent().value())) : GetParent().value()->workspace();
@ -104,9 +102,11 @@ void Instance::updateAncestry() {
this->_workspace = std::nullopt;
}
OnAncestryChanged(updatedChild, newParent);
// Update ancestry in descendants
for (InstanceRef child : children) {
child->updateAncestry();
child->updateAncestry(updatedChild, newParent);
}
}
@ -141,6 +141,10 @@ void Instance::OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParen
// Empty stub
}
void Instance::OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) {
// Empty stub
}
// Properties
tl::expected<Data::Variant, MemberNotFound> Instance::GetPropertyValue(std::string name) {

View file

@ -61,7 +61,7 @@ private:
std::optional<std::weak_ptr<Workspace>> _workspace;
bool ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent);
void updateAncestry();
void updateAncestry(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent);
protected:
bool parentLocked = false;
std::unique_ptr<MemberMap> memberMap;
@ -70,6 +70,7 @@ protected:
virtual ~Instance();
virtual void OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent);
virtual void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent);
// The root data model this object is a descendant of
std::optional<std::shared_ptr<DataModel>> dataModel();

View file

@ -1,5 +1,6 @@
#include "part.h"
#include "base/instance.h"
#include "common.h"
#include "datatypes/base.h"
#include "datatypes/cframe.h"
#include "datatypes/color3.h"
@ -7,7 +8,6 @@
#include "objects/base/member.h"
#include <memory>
#include <optional>
#include "physics/simulation.h"
using Data::Vector3;
@ -106,24 +106,26 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(par
});
}
// This feels wrong. Get access to PhysicsWorld somehow else? Part will need access to this often though, most likely...
extern rp::PhysicsWorld* world;
Part::~Part() {
// This relies on physicsCommon still existing. Be very careful.
if (this->rigidBody)
world->destroyRigidBody(rigidBody);
if (this->rigidBody && workspace())
workspace().value()->DestroyRigidBody(rigidBody);
}
void Part::OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) {
void Part::OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) {
if (this->rigidBody)
this->rigidBody->setIsActive(newParent.has_value());
this->rigidBody->setIsActive(workspace().has_value());
if (workspace())
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
// TODO: Sleeping bodies that touch this one also need to be updated
}
void Part::onUpdated(std::string property) {
syncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
if (workspace())
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
}
// Expands provided extents to fit point

View file

@ -24,7 +24,7 @@ struct PartConstructParams {
class Part : public Instance {
protected:
void OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) override;
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) override;
void onUpdated(std::string);
public:
const static InstanceType TYPE;

View file

@ -1,5 +1,6 @@
#include "workspace.h"
#include "objects/base/instance.h"
#include "physics/util.h"
const InstanceType Workspace::TYPE = {
.super = &Instance::TYPE,
@ -13,5 +14,134 @@ const InstanceType* Workspace::GetClass() {
return &TYPE;
}
static rp::PhysicsCommon physicsCommon;
Workspace::Workspace(): Service(&TYPE) {
}
Workspace::~Workspace() {
if (physicsWorld)
physicsCommon.destroyPhysicsWorld(physicsWorld);
}
void Workspace::InitService() {
if (initialized) return;
initialized = true;
physicsWorld = physicsCommon.createPhysicsWorld();
physicsWorld->setGravity(rp::Vector3(0, -196.2, 0));
// world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::BAUMGARTE_CONTACTS);
physicsWorld->setNbIterationsPositionSolver(2000);
physicsWorld->setNbIterationsVelocitySolver(2000);
// physicsWorld->setEventListener(&eventListener);
// Sync all parts
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
InstanceRef obj = *it;
if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
this->SyncPartPhysics(part);
}
}
void Workspace::SyncPartPhysics(std::shared_ptr<Part> part) {
if (!physicsWorld) return;
glm::mat4 rotMat = glm::mat4(1.0f);
rp::Transform transform = part->cframe;
if (!part->rigidBody) {
part->rigidBody = physicsWorld->createRigidBody(transform);
} else {
part->rigidBody->setTransform(transform);
}
rp::BoxShape* shape = physicsCommon.createBoxShape(glmToRp(part->size * glm::vec3(0.5f)));
if (part->rigidBody->getNbColliders() > 0) {
part->rigidBody->removeCollider(part->rigidBody->getCollider(0));
}
if (part->rigidBody->getNbColliders() == 0)
part->rigidBody->addCollider(shape, rp::Transform());
part->rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC);
part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
part->rigidBody->setUserData(&*part);
}
void Workspace::PhysicsStep(float deltaTime) {
// Step the simulation a few steps
physicsWorld->update(std::min(deltaTime / 2, (1/60.f)));
// Naive implementation. Parts are only considered so if they are just under Workspace
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
InstanceRef obj = *it;
if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
const rp::Transform& transform = part->rigidBody->getTransform();
part->cframe = Data::CFrame(transform);
}
}
RaycastResult::RaycastResult(const rp::RaycastInfo& raycastInfo)
: worldPoint(raycastInfo.worldPoint)
, worldNormal(raycastInfo.worldNormal)
, hitFraction(raycastInfo.hitFraction)
, triangleIndex(raycastInfo.triangleIndex)
, body(raycastInfo.body)
, collider(raycastInfo.collider) {}
class NearestRayHit : public rp::RaycastCallback {
rp::Vector3 startPos;
std::optional<RaycastFilter> filter;
std::optional<RaycastResult> nearestHit;
float nearestHitDistance = -1;
// Order is not guaranteed, so we have to figure out the nearest object using a more sophisticated algorith,
rp::decimal notifyRaycastHit(const rp::RaycastInfo& raycastInfo) override {
// If the detected object is further away than the nearest object, continue.
int distance = (raycastInfo.worldPoint - startPos).length();
if (nearestHitDistance != -1 && distance >= nearestHitDistance)
return 1;
if (!filter) {
nearestHit = raycastInfo;
nearestHitDistance = distance;
return 1;
}
std::shared_ptr<Part> part = partFromBody(raycastInfo.body);
FilterResult result = filter.value()(part);
if (result == FilterResult::BLOCK) {
nearestHit = std::nullopt;
nearestHitDistance = distance;
return 1;
} else if (result == FilterResult::TARGET) {
nearestHit = raycastInfo;
nearestHitDistance = distance;
return 1;
}
return 1;
};
public:
NearestRayHit(rp::Vector3 startPos, std::optional<RaycastFilter> filter = std::nullopt) : startPos(startPos), filter(filter) {}
std::optional<const RaycastResult> getNearestHit() { return nearestHit; };
};
std::optional<const RaycastResult> Workspace::CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter, unsigned short categoryMaskBits) {
rp::Ray ray(glmToRp(point), glmToRp(glm::normalize(rotation)) * maxLength);
NearestRayHit rayHit(glmToRp(point), filter);
physicsWorld->raycast(ray, &rayHit, categoryMaskBits);
return rayHit.getNearestHit();
}
void Workspace::DestroyRigidBody(rp::RigidBody* rigidBody) {
physicsWorld->destroyRigidBody(rigidBody);
}

View file

@ -1,17 +1,52 @@
#pragma once
#include "base.h"
#include "objects/base/service.h"
#include <memory>
#include <reactphysics3d/body/RigidBody.h>
#include <reactphysics3d/engine/PhysicsCommon.h>
#include <reactphysics3d/engine/PhysicsWorld.h>
struct RaycastResult {
rp::Vector3 worldPoint;
rp::Vector3 worldNormal;
rp::decimal hitFraction;
int triangleIndex;
rp::Body* body;
rp::Collider* collider;
RaycastResult(const rp::RaycastInfo& raycastInfo);
};
enum FilterResult {
TARGET, // The object is captured
BLOCK, // The object blocks any objects behind it, but is not captured
PASS, // The object is transparent, ignore it
};
class Part;
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
class Workspace : public Service {
//private:
rp::PhysicsWorld *physicsWorld = nullptr;
protected:
void InitService() override;
bool initialized = false;
public:
const static InstanceType TYPE;
Workspace();
~Workspace();
// static inline std::shared_ptr<Workspace> New() { return std::make_shared<Workspace>(); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Workspace>(); };
virtual const InstanceType* GetClass() override;
void SyncPartPhysics(std::shared_ptr<Part> part);
void DestroyRigidBody(rp::RigidBody* rigidBody);
void PhysicsStep(float deltaTime);
std::optional<const RaycastResult> CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter = std::nullopt, unsigned short categoryMaskBits = 0xFFFF);
};

View file

@ -1,141 +0,0 @@
#include <cstdio>
#include <glm/ext/matrix_float3x3.hpp>
#include <glm/gtc/quaternion.hpp>
#include <memory>
#include <reactphysics3d/collision/RaycastInfo.h>
#include <reactphysics3d/collision/shapes/BoxShape.h>
#include <reactphysics3d/collision/shapes/CollisionShape.h>
#include <reactphysics3d/components/RigidBodyComponents.h>
#include <reactphysics3d/configuration.h>
#include <reactphysics3d/engine/EventListener.h>
#include <reactphysics3d/engine/PhysicsCommon.h>
#include <reactphysics3d/mathematics/Quaternion.h>
#include <reactphysics3d/mathematics/Ray.h>
#include <reactphysics3d/mathematics/Transform.h>
#include <reactphysics3d/mathematics/Vector3.h>
#include <reactphysics3d/memory/DefaultAllocator.h>
#include <reactphysics3d/memory/MemoryAllocator.h>
#include <reactphysics3d/reactphysics3d.h>
#include "../common.h"
#include "../objects/part.h"
#include "datatypes/cframe.h"
#include "util.h"
#include "simulation.h"
namespace rp = reactphysics3d;
class PhysicsListener : public rp::EventListener {
void onContact(const CollisionCallback::CallbackData& /*callbackData*/) override {
// printf("Collision occurred!\n");
}
};
rp::PhysicsCommon* physicsCommon;
rp::PhysicsWorld* world;
PhysicsListener eventListener;
void simulationInit() {
physicsCommon = new rp::PhysicsCommon; // I allocate this on the heap to ensure it exists while Parts are getting destructed. This is probably not great
world = physicsCommon->createPhysicsWorld();
world->setGravity(rp::Vector3(0, -196.2, 0));
// world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::BAUMGARTE_CONTACTS);
world->setNbIterationsPositionSolver(2000);
world->setNbIterationsVelocitySolver(2000);
world->setEventListener(&eventListener);
}
void syncPartPhysics(std::shared_ptr<Part> part) {
glm::mat4 rotMat = glm::mat4(1.0f);
rp::Transform transform = part->cframe;
if (!part->rigidBody) {
part->rigidBody = world->createRigidBody(transform);
} else {
part->rigidBody->setTransform(transform);
}
rp::BoxShape* shape = physicsCommon->createBoxShape(glmToRp(part->size * glm::vec3(0.5f)));
if (part->rigidBody->getNbColliders() > 0) {
part->rigidBody->removeCollider(part->rigidBody->getCollider(0));
}
if (part->rigidBody->getNbColliders() == 0)
part->rigidBody->addCollider(shape, rp::Transform());
part->rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC);
part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
part->rigidBody->setUserData(&*part);
}
void physicsStep(float deltaTime) {
// Step the simulation a few steps
world->update(std::min(deltaTime / 2, (1/60.f)));
// Naive implementation. Parts are only considered so if they are just under Workspace
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
InstanceRef obj = *it;
if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
const rp::Transform& transform = part->rigidBody->getTransform();
part->cframe = Data::CFrame(transform);
}
}
RaycastResult::RaycastResult(const rp::RaycastInfo& raycastInfo)
: worldPoint(raycastInfo.worldPoint)
, worldNormal(raycastInfo.worldNormal)
, hitFraction(raycastInfo.hitFraction)
, triangleIndex(raycastInfo.triangleIndex)
, body(raycastInfo.body)
, collider(raycastInfo.collider) {}
class NearestRayHit : public rp::RaycastCallback {
rp::Vector3 startPos;
std::optional<RaycastFilter> filter;
std::optional<RaycastResult> nearestHit;
float nearestHitDistance = -1;
// Order is not guaranteed, so we have to figure out the nearest object using a more sophisticated algorith,
rp::decimal notifyRaycastHit(const rp::RaycastInfo& raycastInfo) override {
// If the detected object is further away than the nearest object, continue.
int distance = (raycastInfo.worldPoint - startPos).length();
if (nearestHitDistance != -1 && distance >= nearestHitDistance)
return 1;
if (!filter) {
nearestHit = raycastInfo;
nearestHitDistance = distance;
return 1;
}
std::shared_ptr<Part> part = partFromBody(raycastInfo.body);
FilterResult result = filter.value()(part);
if (result == FilterResult::BLOCK) {
nearestHit = std::nullopt;
nearestHitDistance = distance;
return 1;
} else if (result == FilterResult::TARGET) {
nearestHit = raycastInfo;
nearestHitDistance = distance;
return 1;
}
return 1;
};
public:
NearestRayHit(rp::Vector3 startPos, std::optional<RaycastFilter> filter = std::nullopt) : startPos(startPos), filter(filter) {}
std::optional<const RaycastResult> getNearestHit() { return nearestHit; };
};
std::optional<const RaycastResult> castRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter, unsigned short categoryMaskBits) {
rp::Ray ray(glmToRp(point), glmToRp(glm::normalize(rotation)) * maxLength);
NearestRayHit rayHit(glmToRp(point), filter);
world->raycast(ray, &rayHit, categoryMaskBits);
return rayHit.getNearestHit();
}

View file

@ -1,30 +0,0 @@
#pragma once
#include "../objects/part.h"
#include <glm/ext/vector_float3.hpp>
#include <memory>
#include <reactphysics3d/collision/RaycastInfo.h>
struct RaycastResult {
rp::Vector3 worldPoint;
rp::Vector3 worldNormal;
rp::decimal hitFraction;
int triangleIndex;
rp::Body* body;
rp::Collider* collider;
RaycastResult(const rp::RaycastInfo& raycastInfo);
};
enum FilterResult {
TARGET, // The object is captured
BLOCK, // The object blocks any objects behind it, but is not captured
PASS, // The object is transparent, ignore it
};
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
void simulationInit();
void syncPartPhysics(std::shared_ptr<Part> part);
void physicsStep(float deltaTime);
std::optional<const RaycastResult> castRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter = std::nullopt, unsigned short categoryMaskBits = 0xFFFF);

View file

@ -3,7 +3,6 @@
#include "mainglwidget.h"
#include "common.h"
#include "math_helper.h"
#include "physics/simulation.h"
#include "physics/util.h"
#include "rendering/renderer.h"
#include "rendering/shader.h"
@ -112,7 +111,7 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
QPoint position = evt->pos();
glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
std::optional<const RaycastResult> rayHit = castRayNearest(camera.cameraPos, pointDir, 50000, [](std::shared_ptr<Part> part) {
std::optional<const RaycastResult> rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000, [](std::shared_ptr<Part> part) {
return (part == draggingObject->lock()) ? FilterResult::PASS : FilterResult::TARGET;
});
@ -145,7 +144,7 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
draggingObject->lock()->cframe = newFrame + unsinkOffset;
syncPartPhysics(draggingObject->lock());
gWorkspace()->SyncPartPhysics(draggingObject->lock());
}
inline glm::vec3 vec3fy(glm::vec4 vec) {
@ -222,7 +221,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
if (mainWindow()->editSoundEffects && (oldSize != part->size) && QFile::exists("./assets/excluded/switch.wav"))
playSound("./assets/excluded/switch.wav");
syncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
}
// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490)
@ -264,7 +263,7 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles);
syncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
}
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
@ -282,7 +281,7 @@ void MainGLWidget::handleCursorChange(QMouseEvent* evt) {
return;
};
std::optional<const RaycastResult> rayHit = castRayNearest(camera.cameraPos, pointDir, 50000);
std::optional<const RaycastResult> rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000);
if (rayHit && partFromBody(rayHit->body)->name != "Baseplate") {
setCursor(Qt::OpenHandCursor);
return;
@ -339,7 +338,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
}
// raycast part
std::optional<const RaycastResult> rayHit = castRayNearest(camera.cameraPos, pointDir, 50000);
std::optional<const RaycastResult> rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000);
if (!rayHit || !partFromBody(rayHit->body)) return;
std::shared_ptr<Part> part = partFromBody(rayHit->body);
if (part->name == "Baseplate") return;
@ -416,7 +415,7 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
.size = glm::vec3(1, 1, 1),
.color = glm::vec3(1.0f, 0.5f, 0.31f),
}));
syncPartPhysics(lastPart);
gWorkspace()->SyncPartPhysics(lastPart);
}
if (evt->key() == Qt::Key_U)

View file

@ -1,7 +1,6 @@
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "common.h"
#include "physics/simulation.h"
#include <qclipboard.h>
#include <qmessagebox.h>
#include <qmimedata.h>
@ -160,9 +159,6 @@ MainWindow::MainWindow(QWidget *parent)
gDataModel->Init();
ui->explorerView->updateRoot(gDataModel);
// TODO: Remove this and use a proper fix. This *WILL* cause a leak and memory issues in the future
simulationInit();
// Baseplate
gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
.position = glm::vec3(0, -5, 0),
@ -172,7 +168,7 @@ MainWindow::MainWindow(QWidget *parent)
.anchored = true,
}));
ui->mainWidget->lastPart->name = "Baseplate";
syncPartPhysics(ui->mainWidget->lastPart);
gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
});
connect(ui->actionSave, &QAction::triggered, this, [&]() {
@ -201,6 +197,7 @@ MainWindow::MainWindow(QWidget *parent)
// simulationInit();
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path.value());
gDataModel = newModel;
newModel->Init();
ui->explorerView->updateRoot(newModel);
});
@ -333,8 +330,6 @@ MainWindow::MainWindow(QWidget *parent)
// ui->explorerView->Init(ui);
simulationInit();
// Baseplate
gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
.position = glm::vec3(0, -5, 0),
@ -344,7 +339,7 @@ MainWindow::MainWindow(QWidget *parent)
.anchored = true,
}));
ui->mainWidget->lastPart->name = "Baseplate";
syncPartPhysics(ui->mainWidget->lastPart);
gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
.position = glm::vec3(0),
@ -352,7 +347,7 @@ MainWindow::MainWindow(QWidget *parent)
.size = glm::vec3(4, 1.2, 2),
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
}));
syncPartPhysics(ui->mainWidget->lastPart);
gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
}
void MainWindow::closeEvent(QCloseEvent* evt) {
@ -399,7 +394,7 @@ void MainWindow::timerEvent(QTimerEvent* evt) {
lastTime = std::chrono::steady_clock::now();
if (simulationPlaying)
physicsStep(deltaTime);
gWorkspace()->PhysicsStep(deltaTime);
ui->mainWidget->update();
ui->mainWidget->updateCycle();
}