Compare commits
No commits in common. "d4f75827805c371880271497cfebd8d186d1398c" and "df9e285954df4a0ae650b80b10108d4c7fdf35f0" have entirely different histories.
d4f7582780
...
df9e285954
25 changed files with 96 additions and 583 deletions
3
BUILD.md
3
BUILD.md
|
@ -22,6 +22,9 @@ The project will be built using VCPKG and MSVC
|
||||||
* Qt 6.8.3 or higher, with MSVC toolchain
|
* Qt 6.8.3 or higher, with MSVC toolchain
|
||||||
* CMake
|
* CMake
|
||||||
* Git (for cloning the repo, optional)
|
* Git (for cloning the repo, optional)
|
||||||
|
* ReactPhysics3D prebuilt library
|
||||||
|
|
||||||
|
You will have to build and install ReactPhysics3D yourself. Check the [build instructions on ReactPhysics3D](https://www.reactphysics3d.com/documentation/index.html#building) for help
|
||||||
|
|
||||||
To start, clone the repository:
|
To start, clone the repository:
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,6 @@
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "vcpkg-linux",
|
|
||||||
"generator": "Ninja",
|
|
||||||
"binaryDir": "${sourceDir}/build",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -17,7 +17,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "ptr_helpers.h"
|
|
||||||
|
|
||||||
// Static so that this variable name is "local" to this source file
|
// Static so that this variable name is "local" to this source file
|
||||||
const InstanceType Instance::TYPE = {
|
const InstanceType Instance::TYPE = {
|
||||||
|
@ -56,6 +55,17 @@ Instance::Instance(const InstanceType* type) {
|
||||||
Instance::~Instance () {
|
Instance::~Instance () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool operator ==(std::optional<std::weak_ptr<T>> a, std::optional<std::weak_ptr<T>> b) {
|
||||||
|
return (!a.has_value() || a.value().expired()) && (!b.has_value() || b.value().expired())
|
||||||
|
|| (a.has_value() && !a.value().expired()) && (b.has_value() && !b.value().expired()) && a.value().lock() == b.value().lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool operator ==(std::weak_ptr<T> a, std::weak_ptr<T> b) {
|
||||||
|
return a.expired() && b.expired() || (!a.expired() && !b.expired() && a.lock() == b.lock());
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::weak_ptr<T> optional_to_weak(std::optional<std::shared_ptr<T>> a) {
|
std::weak_ptr<T> optional_to_weak(std::optional<std::shared_ptr<T>> a) {
|
||||||
return a ? a.value() : std::weak_ptr<T>();
|
return a ? a.value() : std::weak_ptr<T>();
|
||||||
|
@ -141,19 +151,6 @@ std::optional<std::shared_ptr<Instance>> Instance::GetParent() {
|
||||||
return parent.lock();
|
return parent.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::Destroy() {
|
|
||||||
if (parentLocked) return;
|
|
||||||
// TODO: Implement proper distruction stuff
|
|
||||||
SetParent(std::nullopt);
|
|
||||||
parentLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Instance::IsA(std::string className) {
|
|
||||||
const InstanceType* cur = GetClass();
|
|
||||||
while (cur && cur->className != className) { cur = cur->super; }
|
|
||||||
return cur != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::shared_ptr<Instance> DUMMY_INSTANCE;
|
static std::shared_ptr<Instance> DUMMY_INSTANCE;
|
||||||
DescendantsIterator Instance::GetDescendantsStart() {
|
DescendantsIterator Instance::GetDescendantsStart() {
|
||||||
return DescendantsIterator(GetChildren().size() > 0 ? GetChildren()[0] : DUMMY_INSTANCE);
|
return DescendantsIterator(GetChildren().size() > 0 ? GetChildren()[0] : DUMMY_INSTANCE);
|
||||||
|
@ -388,7 +385,6 @@ std::optional<std::shared_ptr<Instance>> Instance::Clone(RefState<_RefStatePrope
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Data::Variant value = GetPropertyValue(property).expect();
|
Data::Variant value = GetPropertyValue(property).expect();
|
||||||
// printf("property: %s, value: %s\n", property.c_str(), std::string(value.ToString()).c_str());
|
|
||||||
newInstance->SetPropertyValue(property, value).expect();
|
newInstance->SetPropertyValue(property, value).expect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ struct InstanceType {
|
||||||
typedef std::pair<std::shared_ptr<Instance>, std::string> _RefStatePropertyCell;
|
typedef std::pair<std::shared_ptr<Instance>, std::string> _RefStatePropertyCell;
|
||||||
|
|
||||||
class DescendantsIterator;
|
class DescendantsIterator;
|
||||||
class JointInstance;
|
class Snap;
|
||||||
|
|
||||||
// Base class for all instances in the data model
|
// Base class for all instances in the data model
|
||||||
// Note: enable_shared_from_this HAS to be public or else its field will not be populated
|
// Note: enable_shared_from_this HAS to be public or else its field will not be populated
|
||||||
|
@ -60,7 +60,7 @@ private:
|
||||||
bool ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent);
|
bool ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent);
|
||||||
void updateAncestry(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent);
|
void updateAncestry(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent);
|
||||||
|
|
||||||
friend JointInstance; // This isn't ideal, but oh well
|
friend Snap; // This isn't ideal, but oh well
|
||||||
protected:
|
protected:
|
||||||
bool parentLocked = false;
|
bool parentLocked = false;
|
||||||
std::unique_ptr<MemberMap> memberMap;
|
std::unique_ptr<MemberMap> memberMap;
|
||||||
|
@ -91,10 +91,6 @@ public:
|
||||||
std::optional<std::shared_ptr<Instance>> GetParent();
|
std::optional<std::shared_ptr<Instance>> GetParent();
|
||||||
bool IsParentLocked();
|
bool IsParentLocked();
|
||||||
inline const std::vector<std::shared_ptr<Instance>> GetChildren() { return children; }
|
inline const std::vector<std::shared_ptr<Instance>> GetChildren() { return children; }
|
||||||
void Destroy();
|
|
||||||
// Determines whether this object is an instance of, or an instance of a subclass of the sepcified type's class name
|
|
||||||
bool IsA(std::string className);
|
|
||||||
template <typename T> bool IsA() { return IsA(T::TYPE.className); }
|
|
||||||
|
|
||||||
DescendantsIterator GetDescendantsStart();
|
DescendantsIterator GetDescendantsStart();
|
||||||
DescendantsIterator GetDescendantsEnd();
|
DescendantsIterator GetDescendantsEnd();
|
||||||
|
@ -115,7 +111,7 @@ public:
|
||||||
result<std::shared_ptr<T>, InstanceCastError> CastTo() {
|
result<std::shared_ptr<T>, InstanceCastError> CastTo() {
|
||||||
// TODO: Too lazy to implement a manual check
|
// TODO: Too lazy to implement a manual check
|
||||||
std::shared_ptr<T> result = std::dynamic_pointer_cast<T>(shared_from_this());
|
std::shared_ptr<T> result = std::dynamic_pointer_cast<T>(shared_from_this());
|
||||||
if (result == nullptr)
|
if (result != nullptr)
|
||||||
return InstanceCastError(GetClass()->className, T::TYPE.className);
|
return InstanceCastError(GetClass()->className, T::TYPE.className);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
#include "jointinstance.h"
|
|
||||||
|
|
||||||
#include "datatypes/cframe.h"
|
|
||||||
#include "datatypes/ref.h"
|
|
||||||
#include "objects/datamodel.h"
|
|
||||||
#include "objects/jointsservice.h"
|
|
||||||
#include "objects/part.h"
|
|
||||||
#include "objects/workspace.h"
|
|
||||||
#include <memory>
|
|
||||||
#include <reactphysics3d/constraint/FixedJoint.h>
|
|
||||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
|
||||||
#include "ptr_helpers.h"
|
|
||||||
|
|
||||||
const InstanceType JointInstance::TYPE = {
|
|
||||||
.super = &Instance::TYPE,
|
|
||||||
.className = "JointInstance",
|
|
||||||
};
|
|
||||||
|
|
||||||
const InstanceType* JointInstance::GetClass() {
|
|
||||||
return &TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
JointInstance::JointInstance(const InstanceType* type): Instance(type) {
|
|
||||||
this->memberMap = std::make_unique<MemberMap>(MemberMap {
|
|
||||||
.super = std::move(this->memberMap),
|
|
||||||
.members = {
|
|
||||||
{ "Part0", {
|
|
||||||
.backingField = &part0,
|
|
||||||
.type = &Data::InstanceRef::TYPE,
|
|
||||||
.codec = fieldCodecOf<Data::InstanceRef, std::weak_ptr<Instance>>(),
|
|
||||||
.updateCallback = memberFunctionOf(&JointInstance::onUpdated, this),
|
|
||||||
}}, { "Part1", {
|
|
||||||
.backingField = &part1,
|
|
||||||
.type = &Data::InstanceRef::TYPE,
|
|
||||||
.codec = fieldCodecOf<Data::InstanceRef, std::weak_ptr<Instance>>(),
|
|
||||||
.updateCallback = memberFunctionOf(&JointInstance::onUpdated, this),
|
|
||||||
}}, { "C0", {
|
|
||||||
.backingField = &c0,
|
|
||||||
.type = &Data::CFrame::TYPE,
|
|
||||||
.codec = fieldCodecOf<Data::CFrame>(),
|
|
||||||
.updateCallback = memberFunctionOf(&JointInstance::onUpdated, this),
|
|
||||||
}}, { "C1", {
|
|
||||||
.backingField = &c1,
|
|
||||||
.type = &Data::CFrame::TYPE,
|
|
||||||
.codec = fieldCodecOf<Data::CFrame>(),
|
|
||||||
.updateCallback = memberFunctionOf(&JointInstance::onUpdated, this),
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
JointInstance::~JointInstance() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void JointInstance::OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) {
|
|
||||||
// Destroy and rebuild the joint, it's the simplest solution that actually works
|
|
||||||
|
|
||||||
breakJoint();
|
|
||||||
buildJoint();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JointInstance::onUpdated(std::string property) {
|
|
||||||
// Add ourselves to the attached parts, or remove, if applicable
|
|
||||||
|
|
||||||
// Parts differ, delete
|
|
||||||
if (part0 != oldPart0 && !oldPart0.expired()) {
|
|
||||||
oldPart0.lock()->untrackJoint(shared<JointInstance>());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (part1 != oldPart1 && !oldPart1.expired()) {
|
|
||||||
oldPart1.lock()->untrackJoint(shared<JointInstance>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parts differ, add
|
|
||||||
if (part0 != oldPart0 && !part0.expired()) {
|
|
||||||
part0.lock()->trackJoint(shared<JointInstance>());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (part1 != oldPart1 && !part1.expired()) {
|
|
||||||
part1.lock()->trackJoint(shared<JointInstance>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy and rebuild the joint, if applicable
|
|
||||||
|
|
||||||
breakJoint();
|
|
||||||
buildJoint();
|
|
||||||
|
|
||||||
oldPart0 = part0;
|
|
||||||
oldPart1 = part1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::shared_ptr<Workspace>> JointInstance::workspaceOfPart(std::shared_ptr<Part> part) {
|
|
||||||
return part->workspace();
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "objects/base/instance.h"
|
|
||||||
#include "objects/joint/jointinstance.h"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class Snap : public JointInstance {
|
|
||||||
rp::FixedJoint* joint = nullptr;
|
|
||||||
|
|
||||||
virtual void buildJoint() override;
|
|
||||||
virtual void breakJoint() override;
|
|
||||||
public:
|
|
||||||
const static InstanceType TYPE;
|
|
||||||
|
|
||||||
Snap();
|
|
||||||
~Snap();
|
|
||||||
|
|
||||||
static inline std::shared_ptr<Snap> New() { return std::make_shared<Snap>(); };
|
|
||||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Snap>(); };
|
|
||||||
virtual const InstanceType* GetClass() override;
|
|
||||||
};
|
|
|
@ -1,59 +0,0 @@
|
||||||
#include "weld.h"
|
|
||||||
|
|
||||||
#include "datatypes/cframe.h"
|
|
||||||
#include "objects/datamodel.h"
|
|
||||||
#include "objects/joint/jointinstance.h"
|
|
||||||
#include "objects/jointsservice.h"
|
|
||||||
#include "objects/part.h"
|
|
||||||
#include "objects/workspace.h"
|
|
||||||
#include <memory>
|
|
||||||
#include <reactphysics3d/constraint/FixedJoint.h>
|
|
||||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
|
||||||
|
|
||||||
const InstanceType Weld::TYPE = {
|
|
||||||
.super = &JointInstance::TYPE,
|
|
||||||
.className = "Weld",
|
|
||||||
.constructor = &Weld::Create,
|
|
||||||
};
|
|
||||||
|
|
||||||
const InstanceType* Weld::GetClass() {
|
|
||||||
return &TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Weld::Weld(): JointInstance(&TYPE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Weld::~Weld() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Weld::buildJoint() {
|
|
||||||
// Only if both parts are set, are not the same part, are part of a workspace, and are part of the same workspace, we build the joint
|
|
||||||
if (part0.expired() || part1.expired() || part0.lock() == part1.lock() || !workspaceOfPart(part0.lock()) || workspaceOfPart(part0.lock()) != workspaceOfPart(part1.lock())) return;
|
|
||||||
|
|
||||||
// Don't build the joint if we're not part of either a workspace or JointsService
|
|
||||||
if ((!GetParent() || GetParent().value()->GetClass() != &JointsService::TYPE) && !workspace()) return;
|
|
||||||
|
|
||||||
std::shared_ptr<Workspace> workspace = workspaceOfPart(part0.lock()).value();
|
|
||||||
if (!workspace->physicsWorld) return;
|
|
||||||
|
|
||||||
// Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it
|
|
||||||
// used to be rather than specifying an anchor rotation, so whatever.
|
|
||||||
Data::CFrame newFrame = part0.lock()->cframe * (c1.Inverse() * c0);
|
|
||||||
part1.lock()->cframe = newFrame;
|
|
||||||
workspace->SyncPartPhysics(part1.lock());
|
|
||||||
|
|
||||||
// printf("c1.Rotation: ");
|
|
||||||
// printVec(c1.ToEulerAnglesXYZ());
|
|
||||||
rp::FixedJointInfo jointInfo(part0.lock()->rigidBody, part1.lock()->rigidBody, (c0.Inverse() * c1).Position());
|
|
||||||
this->joint = dynamic_cast<rp::FixedJoint*>(workspace->physicsWorld->createJoint(jointInfo));
|
|
||||||
jointWorkspace = workspace;
|
|
||||||
}
|
|
||||||
|
|
||||||
// !!! REMINDER: This has to be called manually when parts are destroyed/removed from the workspace, or joints will linger
|
|
||||||
void Weld::breakJoint() {
|
|
||||||
// If the joint doesn't exist, or its workspace expired (not our problem anymore), then no need to do anything
|
|
||||||
if (!this->joint || jointWorkspace.expired() || !jointWorkspace.lock()->physicsWorld) return;
|
|
||||||
|
|
||||||
jointWorkspace.lock()->physicsWorld->destroyJoint(this->joint);
|
|
||||||
this->joint = nullptr;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "objects/base/instance.h"
|
|
||||||
#include "objects/joint/jointinstance.h"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class Weld : public JointInstance {
|
|
||||||
rp::FixedJoint* joint = nullptr;
|
|
||||||
|
|
||||||
virtual void buildJoint() override;
|
|
||||||
virtual void breakJoint() override;
|
|
||||||
public:
|
|
||||||
const static InstanceType TYPE;
|
|
||||||
|
|
||||||
Weld();
|
|
||||||
~Weld();
|
|
||||||
|
|
||||||
static inline std::shared_ptr<Weld> New() { return std::make_shared<Weld>(); };
|
|
||||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Weld>(); };
|
|
||||||
virtual const InstanceType* GetClass() override;
|
|
||||||
};
|
|
|
@ -2,9 +2,12 @@
|
||||||
|
|
||||||
#include "objects/base/service.h"
|
#include "objects/base/service.h"
|
||||||
|
|
||||||
|
class Snap;
|
||||||
class JointsService : public Service {
|
class JointsService : public Service {
|
||||||
private:
|
private:
|
||||||
std::optional<std::shared_ptr<Workspace>> jointWorkspace();
|
std::optional<std::shared_ptr<Workspace>> jointWorkspace();
|
||||||
|
|
||||||
|
friend Snap;
|
||||||
protected:
|
protected:
|
||||||
void InitService() override;
|
void InitService() override;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "objects/joint/jointinstance.h"
|
|
||||||
#include "objects/jointsservice.h"
|
#include "objects/jointsservice.h"
|
||||||
#include "objects/part.h"
|
#include "objects/part.h"
|
||||||
#include "objects/joint/snap.h"
|
#include "objects/snap.h"
|
||||||
#include "objects/workspace.h"
|
#include "objects/workspace.h"
|
||||||
|
|
||||||
std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||||
|
@ -11,6 +10,5 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||||
{ "Workspace", &Workspace::TYPE },
|
{ "Workspace", &Workspace::TYPE },
|
||||||
{ "DataModel", &DataModel::TYPE },
|
{ "DataModel", &DataModel::TYPE },
|
||||||
{ "Snap", &Snap::TYPE },
|
{ "Snap", &Snap::TYPE },
|
||||||
{ "JointInstance", &JointInstance::TYPE },
|
|
||||||
{ "JointsService", &JointsService::TYPE },
|
{ "JointsService", &JointsService::TYPE },
|
||||||
};
|
};
|
|
@ -6,11 +6,6 @@
|
||||||
#include "datatypes/color3.h"
|
#include "datatypes/color3.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "objects/base/member.h"
|
#include "objects/base/member.h"
|
||||||
#include "objects/joint/weld.h"
|
|
||||||
#include "objects/jointsservice.h"
|
|
||||||
#include "objects/joint/jointinstance.h"
|
|
||||||
#include "objects/joint/snap.h"
|
|
||||||
#include "rendering/surface.h"
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
@ -94,11 +89,6 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame::Fr
|
||||||
.codec = cframeRotationCodec(),
|
.codec = cframeRotationCodec(),
|
||||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
||||||
.flags = PropertyFlags::PROP_NOSAVE
|
.flags = PropertyFlags::PROP_NOSAVE
|
||||||
}}, { "Velocity", {
|
|
||||||
.backingField = &velocity,
|
|
||||||
.type = &Vector3::TYPE,
|
|
||||||
.codec = fieldCodecOf<Data::Vector3>(),
|
|
||||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
|
||||||
}}, { "CFrame", {
|
}}, { "CFrame", {
|
||||||
.backingField = &cframe,
|
.backingField = &cframe,
|
||||||
.type = &Data::CFrame::TYPE,
|
.type = &Data::CFrame::TYPE,
|
||||||
|
@ -182,22 +172,12 @@ void Part::OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std
|
||||||
if (workspace())
|
if (workspace())
|
||||||
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
||||||
|
|
||||||
// Destroy joints
|
|
||||||
if (!workspace()) BreakJoints();
|
|
||||||
|
|
||||||
// TODO: Sleeping bodies that touch this one also need to be updated
|
// TODO: Sleeping bodies that touch this one also need to be updated
|
||||||
}
|
}
|
||||||
|
|
||||||
void Part::onUpdated(std::string property) {
|
void Part::onUpdated(std::string property) {
|
||||||
// Reset velocity
|
|
||||||
if (property != "Velocity")
|
|
||||||
velocity = Data::Vector3::ZERO;
|
|
||||||
|
|
||||||
if (workspace())
|
if (workspace())
|
||||||
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
||||||
|
|
||||||
// When position/rotation/size is manually edited, break all joints, they don't apply anymore
|
|
||||||
BreakJoints();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expands provided extents to fit point
|
// Expands provided extents to fit point
|
||||||
|
@ -226,180 +206,4 @@ Vector3 Part::GetAABB() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (min - max).Abs() / 2;
|
return (min - max).Abs() / 2;
|
||||||
}
|
|
||||||
|
|
||||||
void Part::BreakJoints() {
|
|
||||||
for (std::weak_ptr<JointInstance> joint : primaryJoints) {
|
|
||||||
if (joint.expired()) continue;
|
|
||||||
joint.lock()->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::weak_ptr<JointInstance> joint : secondaryJoints) {
|
|
||||||
if (joint.expired()) continue;
|
|
||||||
joint.lock()->Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Data::Vector3 FACES[6] = {
|
|
||||||
{1, 0, 0},
|
|
||||||
{0, 1, 0},
|
|
||||||
{0, 0, 1},
|
|
||||||
{-1, 0, 0},
|
|
||||||
{0, -1, 0},
|
|
||||||
{0, 0, -1},
|
|
||||||
};
|
|
||||||
|
|
||||||
SurfaceType Part::surfaceFromFace(NormalId face) {
|
|
||||||
switch (face) {
|
|
||||||
case Top: return topSurface;
|
|
||||||
case Bottom: return bottomSurface;
|
|
||||||
case Right: return rightSurface;
|
|
||||||
case Left: return leftSurface;
|
|
||||||
case Front: return frontSurface;
|
|
||||||
case Back: return backSurface;
|
|
||||||
}
|
|
||||||
return SurfaceSmooth; // Unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Part::checkJointContinuity(std::shared_ptr<Part> otherPart) {
|
|
||||||
// Make sure that the two parts don't depend on one another
|
|
||||||
|
|
||||||
return checkJointContinuityUp(otherPart) && checkJointContinuityDown(otherPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Part::checkJointContinuityDown(std::shared_ptr<Part> otherPart) {
|
|
||||||
if (shared<Part>() == otherPart) return false;
|
|
||||||
for (auto joint : primaryJoints) {
|
|
||||||
if (joint.expired() || joint.lock()->part1.expired()) continue;
|
|
||||||
if (!joint.lock()->part1.lock()->checkJointContinuityDown(otherPart))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Part::checkJointContinuityUp(std::shared_ptr<Part> otherPart) {
|
|
||||||
if (shared<Part>() == otherPart) return false;
|
|
||||||
for (auto joint : secondaryJoints) {
|
|
||||||
if (joint.expired() || joint.lock()->part0.expired()) continue;
|
|
||||||
if (!joint.lock()->part0.lock()->checkJointContinuityUp(otherPart))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::shared_ptr<JointInstance>> makeJointFromSurfaces(SurfaceType a, SurfaceType b) {
|
|
||||||
if (a == SurfaceWeld || b == SurfaceWeld || a == SurfaceGlue || b == SurfaceGlue) return Weld::New();
|
|
||||||
if ((a == SurfaceStuds && (b == SurfaceInlets || b == SurfaceUniversal))
|
|
||||||
|| (a == SurfaceInlets && (b == SurfaceStuds || b == SurfaceUniversal))
|
|
||||||
|| (a == SurfaceUniversal && (b == SurfaceStuds || b == SurfaceInlets || b == SurfaceUniversal)))
|
|
||||||
return Snap::New();
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Part::MakeJoints() {
|
|
||||||
// Algorithm: Find nearby parts
|
|
||||||
// Make sure parts are not dependant on each other (via primary/secondaryJoints)
|
|
||||||
// Find matching surfaces (surface normal dot product < -0.999)
|
|
||||||
// Get surface cframe of this part
|
|
||||||
// Transform surface center of other part to local via surface cframe of this part
|
|
||||||
// Make sure z of transformed center is not greater than 0.05
|
|
||||||
|
|
||||||
if (!workspace()) return;
|
|
||||||
|
|
||||||
// TEMPORARY
|
|
||||||
// TODO: Use more efficient algorithm to *actually* find nearby parts)
|
|
||||||
for (auto it = workspace().value()->GetDescendantsStart(); it != workspace().value()->GetDescendantsEnd(); it++) {
|
|
||||||
InstanceRef obj = *it;
|
|
||||||
if (obj == shared_from_this()) continue; // Skip ourselves
|
|
||||||
if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
|
|
||||||
std::shared_ptr<Part> otherPart = obj->CastTo<Part>().expect();
|
|
||||||
|
|
||||||
for (Data::Vector3 myFace : FACES) {
|
|
||||||
Data::Vector3 myWorldNormal = cframe.Rotation() * myFace;
|
|
||||||
Data::Vector3 validUp = cframe.Rotation() * Data::Vector3(1,1,1).Unit(); // If myFace == (0, 1, 0), then (0, 1, 0) would produce NaN as up, so we fudge the up so that it works
|
|
||||||
Data::CFrame surfaceFrame(cframe.Position(), cframe * (myFace * size), validUp);
|
|
||||||
|
|
||||||
for (Data::Vector3 otherFace : FACES) {
|
|
||||||
Data::Vector3 otherWorldNormal = otherPart->cframe.Rotation() * otherFace;
|
|
||||||
Data::Vector3 otherSurfaceCenter = otherPart->cframe * (otherFace * otherPart->size);
|
|
||||||
Data::Vector3 surfacePointLocalToMyFrame = surfaceFrame.Inverse() * otherSurfaceCenter;
|
|
||||||
|
|
||||||
float dot = myWorldNormal.Dot(otherWorldNormal);
|
|
||||||
if (dot > -0.99) continue; // Surface is pointing opposite to ours
|
|
||||||
if (abs(surfacePointLocalToMyFrame.Z()) > 0.05) continue; // Surfaces are within 0.05 studs of one another
|
|
||||||
if (!checkJointContinuity(otherPart)) continue;
|
|
||||||
|
|
||||||
SurfaceType mySurface = surfaceFromFace(faceFromNormal(myFace));
|
|
||||||
SurfaceType otherSurface = surfaceFromFace(faceFromNormal(otherFace));
|
|
||||||
|
|
||||||
auto joint_ = makeJointFromSurfaces(mySurface, otherSurface);
|
|
||||||
if (!joint_) continue;
|
|
||||||
std::shared_ptr<JointInstance> joint = joint_.value();
|
|
||||||
joint->part0 = shared<Part>();
|
|
||||||
joint->part1 = otherPart->shared<Part>();
|
|
||||||
joint->c1 = cframe;
|
|
||||||
joint->c0 = otherPart->cframe;
|
|
||||||
dataModel().value()->GetService<JointsService>()->AddChild(joint);
|
|
||||||
joint->UpdateProperty("Part0");
|
|
||||||
|
|
||||||
Logger::debugf("Made joint between %s and %s!\n", name.c_str(), otherPart->name.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Part::trackJoint(std::shared_ptr<JointInstance> joint) {
|
|
||||||
if (!joint->part0.expired() && joint->part0.lock() == shared_from_this()) {
|
|
||||||
for (auto it = primaryJoints.begin(); it != primaryJoints.end();) {
|
|
||||||
// Clean expired refs
|
|
||||||
if (it->expired()) {
|
|
||||||
primaryJoints.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the joint is already tracked, skip
|
|
||||||
if (it->lock() == joint)
|
|
||||||
return;
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
|
|
||||||
primaryJoints.push_back(joint);
|
|
||||||
} else if (!joint->part1.expired() && joint->part1.lock() == shared_from_this()) {
|
|
||||||
for (auto it = secondaryJoints.begin(); it != secondaryJoints.end();) {
|
|
||||||
// Clean expired refs
|
|
||||||
if (it->expired()) {
|
|
||||||
secondaryJoints.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the joint is already tracked, skip
|
|
||||||
if (it->lock() == joint)
|
|
||||||
return;
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
|
|
||||||
secondaryJoints.push_back(joint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Part::untrackJoint(std::shared_ptr<JointInstance> joint) {
|
|
||||||
for (auto it = primaryJoints.begin(); it != primaryJoints.end();) {
|
|
||||||
// Clean expired refs
|
|
||||||
if (it->expired() || it->lock() == joint) {
|
|
||||||
primaryJoints.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = secondaryJoints.begin(); it != secondaryJoints.end();) {
|
|
||||||
// Clean expired refs
|
|
||||||
if (it->expired() || it->lock() == joint) {
|
|
||||||
secondaryJoints.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -8,9 +8,7 @@
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
#include "rendering/surface.h"
|
#include "rendering/surface.h"
|
||||||
#include <optional>
|
|
||||||
#include <reactphysics3d/reactphysics3d.h>
|
#include <reactphysics3d/reactphysics3d.h>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace rp = reactphysics3d;
|
namespace rp = reactphysics3d;
|
||||||
|
|
||||||
|
@ -25,31 +23,13 @@ struct PartConstructParams {
|
||||||
bool locked = false;
|
bool locked = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Snap;
|
|
||||||
|
|
||||||
class Part : public Instance {
|
class Part : public Instance {
|
||||||
protected:
|
protected:
|
||||||
// Joints where this part is Part0
|
|
||||||
std::vector<std::weak_ptr<JointInstance>> primaryJoints;
|
|
||||||
// Joints where this part is Part1
|
|
||||||
std::vector<std::weak_ptr<JointInstance>> secondaryJoints;
|
|
||||||
|
|
||||||
void trackJoint(std::shared_ptr<JointInstance>);
|
|
||||||
void untrackJoint(std::shared_ptr<JointInstance>);
|
|
||||||
|
|
||||||
SurfaceType surfaceFromFace(NormalId);
|
|
||||||
bool checkJointContinuity(std::shared_ptr<Part>);
|
|
||||||
bool checkJointContinuityUp(std::shared_ptr<Part>);
|
|
||||||
bool checkJointContinuityDown(std::shared_ptr<Part>);
|
|
||||||
|
|
||||||
friend JointInstance;
|
|
||||||
|
|
||||||
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, 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);
|
void onUpdated(std::string);
|
||||||
public:
|
public:
|
||||||
const static InstanceType TYPE;
|
const static InstanceType TYPE;
|
||||||
|
|
||||||
Data::Vector3 velocity;
|
|
||||||
Data::CFrame cframe;
|
Data::CFrame cframe;
|
||||||
glm::vec3 size;
|
glm::vec3 size;
|
||||||
Data::Color3 color;
|
Data::Color3 color;
|
||||||
|
@ -78,9 +58,6 @@ public:
|
||||||
|
|
||||||
inline Data::Vector3 position() { return cframe.Position(); }
|
inline Data::Vector3 position() { return cframe.Position(); }
|
||||||
|
|
||||||
void MakeJoints();
|
|
||||||
void BreakJoints();
|
|
||||||
|
|
||||||
// Calculate size of axis-aligned bounding box
|
// Calculate size of axis-aligned bounding box
|
||||||
Data::Vector3 GetAABB();
|
Data::Vector3 GetAABB();
|
||||||
};
|
};
|
|
@ -1,17 +1,16 @@
|
||||||
#include "snap.h"
|
#include "snap.h"
|
||||||
|
|
||||||
#include "datatypes/cframe.h"
|
#include "datatypes/cframe.h"
|
||||||
|
#include "datatypes/ref.h"
|
||||||
#include "objects/datamodel.h"
|
#include "objects/datamodel.h"
|
||||||
#include "objects/joint/jointinstance.h"
|
|
||||||
#include "objects/jointsservice.h"
|
#include "objects/jointsservice.h"
|
||||||
#include "objects/part.h"
|
#include "workspace.h"
|
||||||
#include "objects/workspace.h"
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <reactphysics3d/constraint/FixedJoint.h>
|
#include <reactphysics3d/constraint/FixedJoint.h>
|
||||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
#include <reactphysics3d/engine/PhysicsWorld.h>
|
||||||
|
|
||||||
const InstanceType Snap::TYPE = {
|
const InstanceType Snap::TYPE = {
|
||||||
.super = &JointInstance::TYPE,
|
.super = &Instance::TYPE,
|
||||||
.className = "Snap",
|
.className = "Snap",
|
||||||
.constructor = &Snap::Create,
|
.constructor = &Snap::Create,
|
||||||
};
|
};
|
||||||
|
@ -20,20 +19,60 @@ const InstanceType* Snap::GetClass() {
|
||||||
return &TYPE;
|
return &TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Snap::Snap(): JointInstance(&TYPE) {
|
Snap::Snap(): Instance(&TYPE) {
|
||||||
|
this->memberMap = std::make_unique<MemberMap>(MemberMap {
|
||||||
|
.super = std::move(this->memberMap),
|
||||||
|
.members = {
|
||||||
|
{ "Part0", {
|
||||||
|
.backingField = &part0,
|
||||||
|
.type = &Data::InstanceRef::TYPE,
|
||||||
|
.codec = fieldCodecOf<Data::InstanceRef, std::weak_ptr<Instance>>(),
|
||||||
|
.updateCallback = memberFunctionOf(&Snap::onUpdated, this),
|
||||||
|
}}, { "Part1", {
|
||||||
|
.backingField = &part1,
|
||||||
|
.type = &Data::InstanceRef::TYPE,
|
||||||
|
.codec = fieldCodecOf<Data::InstanceRef, std::weak_ptr<Instance>>(),
|
||||||
|
.updateCallback = memberFunctionOf(&Snap::onUpdated, this),
|
||||||
|
}}, { "C0", {
|
||||||
|
.backingField = &c0,
|
||||||
|
.type = &Data::CFrame::TYPE,
|
||||||
|
.codec = fieldCodecOf<Data::CFrame>(),
|
||||||
|
.updateCallback = memberFunctionOf(&Snap::onUpdated, this),
|
||||||
|
}}, { "C1", {
|
||||||
|
.backingField = &c1,
|
||||||
|
.type = &Data::CFrame::TYPE,
|
||||||
|
.codec = fieldCodecOf<Data::CFrame>(),
|
||||||
|
.updateCallback = memberFunctionOf(&Snap::onUpdated, this),
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Snap::~Snap() {
|
Snap::~Snap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Snap::OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) {
|
||||||
|
// Destroy and rebuild the joint, it's the simplest solution that actually works
|
||||||
|
|
||||||
|
breakJoint();
|
||||||
|
buildJoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Snap::onUpdated(std::string property) {
|
||||||
|
// Destroy and rebuild the joint, if applicable
|
||||||
|
|
||||||
|
breakJoint();
|
||||||
|
buildJoint();
|
||||||
|
}
|
||||||
|
|
||||||
void Snap::buildJoint() {
|
void Snap::buildJoint() {
|
||||||
// Only if both parts are set, are not the same part, are part of a workspace, and are part of the same workspace, we build the joint
|
// Only if both parts are set, are not the same part, are part of a workspace, and are part of the same workspace, we build the joint
|
||||||
if (part0.expired() || part1.expired() || part0.lock() == part1.lock() || !workspaceOfPart(part0.lock()) || workspaceOfPart(part0.lock()) != workspaceOfPart(part1.lock())) return;
|
if (part0.expired() || part1.expired() || part0.lock() == part1.lock() || !part0.lock()->workspace() || part0.lock()->workspace() != part1.lock()->workspace()) return;
|
||||||
|
|
||||||
// Don't build the joint if we're not part of either a workspace or JointsService
|
// Don't build the joint if we're not part of either a workspace or JointsService
|
||||||
if ((!GetParent() || GetParent().value()->GetClass() != &JointsService::TYPE) && !workspace()) return;
|
if ((!GetParent() || GetParent().value()->GetClass() != &JointsService::TYPE) && !workspace()) return;
|
||||||
|
|
||||||
std::shared_ptr<Workspace> workspace = workspaceOfPart(part0.lock()).value();
|
std::shared_ptr<Workspace> workspace = part0.lock()->workspace().value();
|
||||||
if (!workspace->physicsWorld) return;
|
if (!workspace->physicsWorld) return;
|
||||||
|
|
||||||
// Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it
|
// Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it
|
|
@ -7,19 +7,17 @@
|
||||||
class Part;
|
class Part;
|
||||||
class Workspace;
|
class Workspace;
|
||||||
|
|
||||||
class JointInstance : public Instance {
|
class Snap : public Instance {
|
||||||
std::weak_ptr<Part> oldPart0;
|
rp::FixedJoint* joint = nullptr;
|
||||||
std::weak_ptr<Part> oldPart1;
|
|
||||||
protected:
|
|
||||||
// The workspace the joint was created in, if it exists
|
// The workspace the joint was created in, if it exists
|
||||||
std::weak_ptr<Workspace> jointWorkspace;
|
std::weak_ptr<Workspace> jointWorkspace;
|
||||||
|
protected:
|
||||||
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) override;
|
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) override;
|
||||||
|
|
||||||
std::optional<std::shared_ptr<Workspace>> workspaceOfPart(std::shared_ptr<Part>);
|
|
||||||
void onUpdated(std::string property);
|
void onUpdated(std::string property);
|
||||||
virtual void buildJoint() = 0;
|
void buildJoint();
|
||||||
virtual void breakJoint() = 0;
|
void breakJoint();
|
||||||
public:
|
public:
|
||||||
const static InstanceType TYPE;
|
const static InstanceType TYPE;
|
||||||
|
|
||||||
|
@ -28,8 +26,10 @@ public:
|
||||||
Data::CFrame c0;
|
Data::CFrame c0;
|
||||||
Data::CFrame c1;
|
Data::CFrame c1;
|
||||||
|
|
||||||
JointInstance(const InstanceType*);
|
Snap();
|
||||||
~JointInstance();
|
~Snap();
|
||||||
|
|
||||||
|
static inline std::shared_ptr<Snap> New() { return std::make_shared<Snap>(); };
|
||||||
|
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Snap>(); };
|
||||||
virtual const InstanceType* GetClass() override;
|
virtual const InstanceType* GetClass() override;
|
||||||
};
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
#include "objects/jointsservice.h"
|
#include "objects/jointsservice.h"
|
||||||
#include "objects/joint/jointinstance.h"
|
#include "objects/snap.h"
|
||||||
#include "physics/util.h"
|
#include "physics/util.h"
|
||||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
#include <reactphysics3d/engine/PhysicsCommon.h>
|
||||||
|
|
||||||
|
@ -45,23 +45,22 @@ void Workspace::InitService() {
|
||||||
// Sync all parts
|
// Sync all parts
|
||||||
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
||||||
InstanceRef obj = *it;
|
InstanceRef obj = *it;
|
||||||
if (!obj->IsA<Part>()) continue;
|
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 = obj->CastTo<Part>().expect();
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
|
||||||
this->SyncPartPhysics(part);
|
this->SyncPartPhysics(part);
|
||||||
part->MakeJoints();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate all joints
|
// Activate all joints
|
||||||
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
||||||
InstanceRef obj = *it;
|
InstanceRef obj = *it;
|
||||||
if (!obj->IsA<JointInstance>()) continue;
|
if (obj->GetClass()->className != "Snap") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
|
||||||
std::shared_ptr<JointInstance> joint = obj->CastTo<JointInstance>().expect();
|
std::shared_ptr<Snap> joint = std::dynamic_pointer_cast<Snap>(obj);
|
||||||
joint->UpdateProperty("Part0");
|
joint->UpdateProperty("Part0");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto obj : dataModel().value()->GetService<JointsService>()->GetChildren()) {
|
for (auto obj : dataModel().value()->GetService<JointsService>()->GetChildren()) {
|
||||||
if (!obj->IsA<JointInstance>()) continue;
|
if (obj->GetClass()->className != "Snap") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
|
||||||
std::shared_ptr<JointInstance> joint = obj->CastTo<JointInstance>().expect();
|
std::shared_ptr<Snap> joint = std::dynamic_pointer_cast<Snap>(obj);
|
||||||
joint->UpdateProperty("Part0");
|
joint->UpdateProperty("Part0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +94,6 @@ void Workspace::SyncPartPhysics(std::shared_ptr<Part> part) {
|
||||||
part->rigidBody->updateMassFromColliders();
|
part->rigidBody->updateMassFromColliders();
|
||||||
part->rigidBody->updateLocalInertiaTensorFromColliders();
|
part->rigidBody->updateLocalInertiaTensorFromColliders();
|
||||||
|
|
||||||
part->rigidBody->setLinearVelocity(part->velocity);
|
|
||||||
// part->rigidBody->setMass(density * part->size.x * part->size.y * part->size.z);
|
// part->rigidBody->setMass(density * part->size.x * part->size.y * part->size.z);
|
||||||
|
|
||||||
part->rigidBody->setUserData(&*part);
|
part->rigidBody->setUserData(&*part);
|
||||||
|
@ -113,7 +111,6 @@ void Workspace::PhysicsStep(float deltaTime) {
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
|
||||||
const rp::Transform& transform = part->rigidBody->getTransform();
|
const rp::Transform& transform = part->rigidBody->getTransform();
|
||||||
part->cframe = Data::CFrame(transform);
|
part->cframe = Data::CFrame(transform);
|
||||||
part->velocity = part->rigidBody->getLinearVelocity();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ enum FilterResult {
|
||||||
|
|
||||||
class Part;
|
class Part;
|
||||||
class Snap;
|
class Snap;
|
||||||
class Weld;
|
|
||||||
|
|
||||||
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
||||||
|
|
||||||
|
@ -35,7 +34,6 @@ class Workspace : public Service {
|
||||||
|
|
||||||
friend Part;
|
friend Part;
|
||||||
friend Snap;
|
friend Snap;
|
||||||
friend Weld;
|
|
||||||
protected:
|
protected:
|
||||||
void InitService() override;
|
void InitService() override;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <optional>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool operator ==(std::optional<std::weak_ptr<T>> a, std::optional<std::weak_ptr<T>> b) {
|
|
||||||
return (!a.has_value() || a.value().expired()) && (!b.has_value() || b.value().expired())
|
|
||||||
|| (a.has_value() && !a.value().expired()) && (b.has_value() && !b.value().expired()) && a.value().lock() == b.value().lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool operator ==(std::weak_ptr<T> a, std::weak_ptr<T> b) {
|
|
||||||
return a.expired() && b.expired() || (!a.expired() && !b.expired() && a.lock() == b.lock());
|
|
||||||
}
|
|
|
@ -1,14 +1,14 @@
|
||||||
#include "surface.h"
|
#include "surface.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
|
|
||||||
Data::Vector3 FACE_NORMALS[6] = {
|
static std::array<Data::Vector3, 6> FACE_NORMALS = {{
|
||||||
{ 1, 0, 0 },
|
{ 1, 0, 0 },
|
||||||
{ 0, 1, 0 },
|
{ 0, 1, 0 },
|
||||||
{ 0, 0, 1 },
|
{ 0, 0, 1 },
|
||||||
{ -1, 0, 0 },
|
{ -1, 0, 0 },
|
||||||
{ 0, -1, 0 },
|
{ 0, -1, 0 },
|
||||||
{ 0, 0, -1 },
|
{ 0, 0, -1 },
|
||||||
};
|
}};
|
||||||
|
|
||||||
NormalId faceFromNormal(Data::Vector3 normal) {
|
NormalId faceFromNormal(Data::Vector3 normal) {
|
||||||
for (int face = 0; face < 6; face++) {
|
for (int face = 0; face < 6; face++) {
|
||||||
|
|
13
editor/mainglwidget.cpp
Executable file → Normal file
13
editor/mainglwidget.cpp
Executable file → Normal file
|
@ -3,7 +3,6 @@
|
||||||
#include <glm/vector_relational.hpp>
|
#include <glm/vector_relational.hpp>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qsoundeffect.h>
|
#include <qsoundeffect.h>
|
||||||
#include <string>
|
|
||||||
#include "mainglwidget.h"
|
#include "mainglwidget.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "math_helper.h"
|
#include "math_helper.h"
|
||||||
|
@ -12,8 +11,6 @@
|
||||||
#include "rendering/renderer.h"
|
#include "rendering/renderer.h"
|
||||||
#include "rendering/shader.h"
|
#include "rendering/shader.h"
|
||||||
|
|
||||||
#define PI 3.14159
|
|
||||||
|
|
||||||
static Data::CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
static Data::CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
||||||
|
|
||||||
MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) {
|
MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) {
|
||||||
|
@ -158,7 +155,6 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
||||||
|
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(draggingObject.lock());
|
gWorkspace()->SyncPartPhysics(draggingObject.lock());
|
||||||
draggingObject.lock()->UpdateProperty("Position");
|
|
||||||
sendPropertyUpdatedSignal(draggingObject.lock(), "Position", draggingObject.lock()->position());
|
sendPropertyUpdatedSignal(draggingObject.lock(), "Position", draggingObject.lock()->position());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,8 +242,6 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
||||||
playSound("./assets/excluded/switch.wav");
|
playSound("./assets/excluded/switch.wav");
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(part);
|
gWorkspace()->SyncPartPhysics(part);
|
||||||
part->UpdateProperty("Position");
|
|
||||||
part->UpdateProperty("Size");
|
|
||||||
sendPropertyUpdatedSignal(part, "Position", part->position());
|
sendPropertyUpdatedSignal(part, "Position", part->position());
|
||||||
sendPropertyUpdatedSignal(part, "Size", Data::Vector3(part->size));
|
sendPropertyUpdatedSignal(part, "Size", Data::Vector3(part->size));
|
||||||
}
|
}
|
||||||
|
@ -277,7 +271,7 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||||
|
|
||||||
// Snap the angle
|
// Snap the angle
|
||||||
if (snappingFactor() > 0)
|
if (snappingFactor() > 0)
|
||||||
angle = roundf(angle * 4 / PI / snappingFactor()) / 4 * PI * snappingFactor();
|
angle = roundf(angle * 4 / std::numbers::pi / snappingFactor()) / 4 * std::numbers::pi * snappingFactor();
|
||||||
|
|
||||||
// Checks if the rotation axis is facing towards, or away from the camera
|
// Checks if the rotation axis is facing towards, or away from the camera
|
||||||
// If it pointing away from the camera, then we need to invert the angle change
|
// If it pointing away from the camera, then we need to invert the angle change
|
||||||
|
@ -291,7 +285,6 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||||
part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles);
|
part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles);
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(part);
|
gWorkspace()->SyncPartPhysics(part);
|
||||||
part->UpdateProperty("Rotation");
|
|
||||||
sendPropertyUpdatedSignal(part, "Rotation", part->cframe.ToEulerAnglesXYZ());
|
sendPropertyUpdatedSignal(part, "Rotation", part->cframe.ToEulerAnglesXYZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +439,6 @@ void MainGLWidget::updateCycle() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int partId = 1;
|
|
||||||
void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
|
void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
|
||||||
if (evt->key() == Qt::Key_W) moveZ = 1;
|
if (evt->key() == Qt::Key_W) moveZ = 1;
|
||||||
else if (evt->key() == Qt::Key_S) moveZ = -1;
|
else if (evt->key() == Qt::Key_S) moveZ = -1;
|
||||||
|
@ -462,7 +454,6 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
|
||||||
.color = glm::vec3(1.0f, 0.5f, 0.31f),
|
.color = glm::vec3(1.0f, 0.5f, 0.31f),
|
||||||
}));
|
}));
|
||||||
gWorkspace()->SyncPartPhysics(lastPart);
|
gWorkspace()->SyncPartPhysics(lastPart);
|
||||||
lastPart->name = "Part" + std::to_string(partId++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt->key() == Qt::Key_U)
|
if (evt->key() == Qt::Key_U)
|
||||||
|
@ -492,4 +483,4 @@ float MainGLWidget::snappingFactor() {
|
||||||
case GridSnappingMode::SNAP_OFF: return 0;
|
case GridSnappingMode::SNAP_OFF: return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "objects/jointsservice.h"
|
#include "objects/jointsservice.h"
|
||||||
#include "objects/joint/snap.h"
|
#include "objects/snap.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <qclipboard.h>
|
#include <qclipboard.h>
|
||||||
|
@ -178,8 +178,6 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
|
|
||||||
// gWorkspace()->AddChild(snap);
|
// gWorkspace()->AddChild(snap);
|
||||||
gDataModel->GetService<JointsService>()->AddChild(snap);
|
gDataModel->GetService<JointsService>()->AddChild(snap);
|
||||||
snap->UpdateProperty("Part0");
|
|
||||||
snap->UpdateProperty("Part1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent* evt) {
|
void MainWindow::closeEvent(QCloseEvent* evt) {
|
||||||
|
@ -368,18 +366,18 @@ void MainWindow::connectActionHandlers() {
|
||||||
|
|
||||||
connect(ui->actionSave, &QAction::triggered, this, [&]() {
|
connect(ui->actionSave, &QAction::triggered, this, [&]() {
|
||||||
std::optional<std::string> path;
|
std::optional<std::string> path;
|
||||||
if (!editModeDataModel->HasFile())
|
if (!gDataModel->HasFile())
|
||||||
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + editModeDataModel->name));
|
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name));
|
||||||
if (!editModeDataModel->HasFile() && (!path || path == "")) return;
|
if (!gDataModel->HasFile() && (!path || path == "")) return;
|
||||||
|
|
||||||
editModeDataModel->SaveToFile(path);
|
gDataModel->SaveToFile(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionSaveAs, &QAction::triggered, this, [&]() {
|
connect(ui->actionSaveAs, &QAction::triggered, this, [&]() {
|
||||||
std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save as " + editModeDataModel->name));
|
std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save as " + gDataModel->name));
|
||||||
if (!path || path == "") return;
|
if (!path || path == "") return;
|
||||||
|
|
||||||
editModeDataModel->SaveToFile(path);
|
gDataModel->SaveToFile(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionOpen, &QAction::triggered, this, [&]() {
|
connect(ui->actionOpen, &QAction::triggered, this, [&]() {
|
||||||
|
@ -391,16 +389,9 @@ void MainWindow::connectActionHandlers() {
|
||||||
|
|
||||||
// simulationInit();
|
// simulationInit();
|
||||||
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path.value());
|
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path.value());
|
||||||
editModeDataModel = newModel;
|
|
||||||
gDataModel = newModel;
|
gDataModel = newModel;
|
||||||
newModel->Init();
|
newModel->Init();
|
||||||
ui->explorerView->updateRoot(newModel);
|
ui->explorerView->updateRoot(newModel);
|
||||||
|
|
||||||
// Reset running state
|
|
||||||
runState = RUN_STOPPED;
|
|
||||||
ui->actionRunSimulation->setEnabled(true);
|
|
||||||
ui->actionPauseSimulation->setEnabled(false);
|
|
||||||
ui->actionStopSimulation->setEnabled(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionDelete, &QAction::triggered, this, [&]() {
|
connect(ui->actionDelete, &QAction::triggered, this, [&]() {
|
||||||
|
|
3
vcpkg-configuration.json
Executable file → Normal file
3
vcpkg-configuration.json
Executable file → Normal file
|
@ -10,8 +10,5 @@
|
||||||
"location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
|
"location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
|
||||||
"name": "microsoft"
|
"name": "microsoft"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"overlay-ports": [
|
|
||||||
"./vcpkg-overlays"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
vcpkg_check_linkage(ONLY_STATIC_LIBRARY)
|
|
||||||
|
|
||||||
vcpkg_from_github(
|
|
||||||
OUT_SOURCE_PATH SOURCE_PATH
|
|
||||||
REPO DanielChappuis/reactphysics3d
|
|
||||||
REF "cd958bbc0c6e84a869388cba6613f10cc645b3cb"
|
|
||||||
SHA512 9856c0e998473e0bfb97af9ced07952bbd4dfef79f7dc388b1ecf9b6c6406f7669333e441fe6cefdf40b32edc5a1b8e4cb35a8c15fccb64c28785aff5fd77113
|
|
||||||
HEAD_REF master
|
|
||||||
PATCHES "std_chrono.patch"
|
|
||||||
)
|
|
||||||
|
|
||||||
vcpkg_cmake_configure(
|
|
||||||
SOURCE_PATH "${SOURCE_PATH}"
|
|
||||||
)
|
|
||||||
|
|
||||||
vcpkg_cmake_install()
|
|
||||||
|
|
||||||
vcpkg_cmake_config_fixup(PACKAGE_NAME "reactphysics3d" CONFIG_PATH "lib/cmake/ReactPhysics3D")
|
|
||||||
|
|
||||||
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
|
|
||||||
|
|
||||||
# file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
|
|
||||||
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
|
|
|
@ -1,12 +0,0 @@
|
||||||
diff --git a/include/reactphysics3d/utils/DefaultLogger.h b/include/reactphysics3d/utils/DefaultLogger.h
|
|
||||||
index 1088d1e..8360f07 100644
|
|
||||||
--- a/include/reactphysics3d/utils/DefaultLogger.h
|
|
||||||
+++ b/include/reactphysics3d/utils/DefaultLogger.h
|
|
||||||
@@ -37,6 +37,7 @@
|
|
||||||
#include <iomanip>
|
|
||||||
#include <mutex>
|
|
||||||
#include <ctime>
|
|
||||||
+#include <chrono>
|
|
||||||
|
|
||||||
/// ReactPhysics3D namespace
|
|
||||||
namespace reactphysics3d {
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"name": "reactphysics3d",
|
|
||||||
"version": "0.10.2",
|
|
||||||
"homepage": "https://github.com/DanielChappuis/reactphysics3d",
|
|
||||||
"description": "Open source C++ physics engine library in 3D",
|
|
||||||
"license": "zlib",
|
|
||||||
"dependencies": [
|
|
||||||
{
|
|
||||||
"name" : "vcpkg-cmake",
|
|
||||||
"host" : true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name" : "vcpkg-cmake-config",
|
|
||||||
"host" : true
|
|
||||||
},
|
|
||||||
"fmt"
|
|
||||||
]
|
|
||||||
}
|
|
9
vcpkg.json
Executable file → Normal file
9
vcpkg.json
Executable file → Normal file
|
@ -5,13 +5,6 @@
|
||||||
"glm",
|
"glm",
|
||||||
{ "name": "pugixml", "version>=": "1.15" },
|
{ "name": "pugixml", "version>=": "1.15" },
|
||||||
"sdl2",
|
"sdl2",
|
||||||
"stb",
|
"stb"
|
||||||
"reactphysics3d"
|
|
||||||
],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"name": "sdl2",
|
|
||||||
"version": "2.32.4"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue