Skip to content

Commit 046a4c1

Browse files
committed
optimize physics system, make parser more lenient, and add fallen height
limit
1 parent 0f09115 commit 046a4c1

File tree

16 files changed

+810426
-152
lines changed

16 files changed

+810426
-152
lines changed

resources/Places/AngelOfTruth.rbxl

Lines changed: 646290 additions & 0 deletions
Large diffs are not rendered by default.

resources/Places/HappyHomeInRobloxiaBaseNoCollision.rbxl

Lines changed: 163809 additions & 0 deletions
Large diffs are not rendered by default.

src/Engine/Objects/BasePart.cpp

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
namespace Nova {
1717
BasePart::~BasePart() {
1818
if (!physicsBodyID.IsInvalid()) {
19-
if (auto dm = GetDataModel()) {
19+
if (auto physics = registeredService.lock()) {
20+
physics->UnregisterPart(std::static_pointer_cast<BasePart>(shared_from_this()));
21+
} else if (auto dm = GetDataModel()) {
2022
if (auto physics = dm->GetService<PhysicsService>()) {
2123
physics->UnregisterPart(std::static_pointer_cast<BasePart>(shared_from_this()));
2224
}
@@ -27,8 +29,7 @@ namespace Nova {
2729
void BasePart::InitializePhysics() {
2830
if (!basePartProps) return;
2931
auto cf = basePartProps->CFrame.get().to_nova();
30-
currPosition = prevPosition = cf.position;
31-
currRotation = prevRotation = glm::normalize(glm::quat_cast(cf.rotation));
32+
// Since we removed interpolation state from header, we don't need to sync prev/curr anymore
3233
}
3334

3435
void BasePart::OnAncestorChanged(std::shared_ptr<Instance> instance, std::shared_ptr<Instance> newParent) {
@@ -41,12 +42,21 @@ namespace Nova {
4142
auto physics = dm->GetService<PhysicsService>();
4243
if (physicsBodyID.IsInvalid()) {
4344
physics->BulkRegisterParts({ std::static_pointer_cast<BasePart>(shared_from_this()) });
44-
// BulkRegisterParts initializes physics for the part if it's not deferring
4545
if (!physics->IsDeferring()) InitializePhysics();
4646
}
4747
} else {
4848
if (!physicsBodyID.IsInvalid()) {
49-
auto physics = dm->GetService<PhysicsService>();
49+
if (auto physics = registeredService.lock()) {
50+
physics->UnregisterPart(std::static_pointer_cast<BasePart>(shared_from_this()));
51+
} else if (auto p = dm->GetService<PhysicsService>()) {
52+
p->UnregisterPart(std::static_pointer_cast<BasePart>(shared_from_this()));
53+
}
54+
}
55+
}
56+
} else {
57+
// Detached from tree
58+
if (!physicsBodyID.IsInvalid()) {
59+
if (auto physics = registeredService.lock()) {
5060
physics->UnregisterPart(std::static_pointer_cast<BasePart>(shared_from_this()));
5161
}
5262
}
@@ -56,9 +66,12 @@ namespace Nova {
5666
void BasePart::OnPropertyChanged(const std::string& name) {
5767
if (physicsBodyID.IsInvalid()) return;
5868

59-
auto dm = GetDataModel();
60-
if (!dm) return;
61-
auto physics = dm->GetService<PhysicsService>();
69+
auto physics = registeredService.lock();
70+
if (!physics) {
71+
auto dm = GetDataModel();
72+
if (dm) physics = dm->GetService<PhysicsService>();
73+
}
74+
6275
if (!physics) return;
6376

6477
JPH::BodyInterface &bi = physics->GetPhysicsSystem()->GetBodyInterface();
@@ -70,16 +83,8 @@ namespace Nova {
7083
JPH::RVec3(cf.position.x, cf.position.y, cf.position.z),
7184
JPH::Quat(q.x, q.y, q.z, q.w),
7285
JPH::EActivation::Activate);
73-
74-
// Sync interpolation state
75-
currPosition = prevPosition = cf.position;
76-
currRotation = prevRotation = q;
7786
}
7887
else if (name == "Anchored") {
79-
bool anchored = basePartProps->Anchored;
80-
bi.SetMotionType(physicsBodyID, anchored ? JPH::EMotionType::Static : JPH::EMotionType::Dynamic, JPH::EActivation::Activate);
81-
82-
// For layer changes (static vs moving), we must re-register
8388
physics->UnregisterPart(std::static_pointer_cast<BasePart>(shared_from_this()));
8489
physics->BulkRegisterParts({ std::static_pointer_cast<BasePart>(shared_from_this()) });
8590
}

src/Engine/Objects/BasePart.hpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,7 @@ namespace Nova {
5353

5454
// Jolt Physics linkage
5555
JPH::BodyID physicsBodyID;
56-
57-
// Interpolation state
58-
glm::vec3 prevPosition = glm::vec3(0);
59-
glm::quat prevRotation = glm::quat(1, 0, 0, 0);
60-
glm::vec3 currPosition = glm::vec3(0);
61-
glm::quat currRotation = glm::quat(1, 0, 0, 0);
56+
std::weak_ptr<PhysicsService> registeredService;
6257

6358
void InitializePhysics();
6459

src/Engine/Objects/Instance.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,41 @@ namespace Nova {
3535
return false;
3636
}
3737

38+
void Instance::SetParent(std::shared_ptr<Instance> newParent) {
39+
auto self = shared_from_this();
40+
41+
// Find old workspace for refresh
42+
std::shared_ptr<Workspace> oldWS = nullptr;
43+
if (auto dm = GetDataModel()) oldWS = dm->GetService<Workspace>();
44+
bool wasInWS = oldWS && (this == oldWS.get() || IsDescendantOf(oldWS));
45+
46+
if (auto p = parent.lock()) {
47+
auto& c = p->children;
48+
c.erase(std::remove(c.begin(), c.end(), self), c.end());
49+
}
50+
51+
parent = newParent;
52+
if (newParent) {
53+
newParent->children.push_back(self);
54+
}
55+
56+
OnAncestorChanged(self, newParent);
57+
58+
// Refresh old workspace cache
59+
if (wasInWS && oldWS) oldWS->RefreshCachedParts();
60+
61+
// Refresh new workspace cache
62+
if (newParent) {
63+
if (auto dm = newParent->GetDataModel()) {
64+
if (auto newWS = dm->GetService<Workspace>()) {
65+
if (this == newWS.get() || IsDescendantOf(newWS)) {
66+
newWS->RefreshCachedParts();
67+
}
68+
}
69+
}
70+
}
71+
}
72+
3873
void Instance::OnAncestorChanged(std::shared_ptr<Instance> instance, std::shared_ptr<Instance> newParent) {
3974
for (auto& child : children) {
4075
child->OnAncestorChanged(instance, newParent);
@@ -104,13 +139,6 @@ namespace Nova {
104139
auto res = value.cast<std::shared_ptr<Instance>>();
105140
if (res) {
106141
self.SetParent(res.value());
107-
108-
// Handle cache refresh for rendering
109-
if (auto dm = self.GetDataModel()) {
110-
if (auto ws = dm->GetService<Workspace>()) {
111-
if (self.IsDescendantOf(ws)) ws->RefreshCachedParts();
112-
}
113-
}
114142
}
115143
}
116144
lua_pop(L, 1);

src/Engine/Objects/Instance.hpp

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ namespace Nova {
3535

3636
namespace Props {
3737
struct InstanceProps {
38-
rfl::Rename<"Name", std::string> Name;
39-
rfl::Rename<"archivable", bool> Archivable = true;
38+
std::string Name;
39+
bool Archivable = true;
4040
};
4141
}
4242

@@ -72,12 +72,26 @@ namespace Nova {
7272
} \
7373
\
7474
void ApplyPropertiesGeneric(const rfl::Generic& generic) override { \
75-
auto result = rfl::from_generic<decltype(this->PropsMember), rfl::UnderlyingEnums>(generic); \
76-
if (result) { \
77-
this->PropsMember = result.value(); \
78-
} else { \
79-
std::cout << "[REFLECTION ERROR] " << #ClassName << " failed: " \
80-
<< result.error().what() << std::endl; \
75+
auto current = rfl::to_generic<rfl::UnderlyingEnums>(this->PropsMember); \
76+
if (auto* dest = std::get_if<rfl::Object<rfl::Generic>>(&current.variant())) { \
77+
if (auto* src = std::get_if<rfl::Object<rfl::Generic>>(&generic.variant())) { \
78+
for (const auto& entry : *src) { \
79+
const auto& name = entry.first; \
80+
const auto& value = entry.second; \
81+
/* Update in-place to avoid duplicates */ \
82+
auto it = std::find_if(dest->begin(), dest->end(), [&](const auto& p) { return p.first == name; }); \
83+
if (it != dest->end()) { \
84+
it->second = value; \
85+
} \
86+
} \
87+
auto result = rfl::from_generic<decltype(this->PropsMember), rfl::UnderlyingEnums>(current); \
88+
if (result) { \
89+
this->PropsMember = result.value(); \
90+
} else { \
91+
std::cout << "[REFLECTION ERROR] " << #ClassName << " failed: " \
92+
<< result.error().what() << std::endl; \
93+
} \
94+
} \
8195
} \
8296
} \
8397
\
@@ -92,12 +106,15 @@ namespace Nova {
92106
bool SetProperty(const std::string& name, const rfl::Generic& value) override { \
93107
auto generic = rfl::to_generic<rfl::UnderlyingEnums>(this->PropsMember); \
94108
if (auto* obj = std::get_if<rfl::Object<rfl::Generic>>(&generic.variant())) { \
95-
(*obj)[name] = value; \
96-
auto result = rfl::from_generic<decltype(this->PropsMember), rfl::UnderlyingEnums>(generic); \
97-
if (result) { \
98-
this->PropsMember = result.value(); \
99-
OnPropertyChanged(name); \
100-
return true; \
109+
auto it = std::find_if(obj->begin(), obj->end(), [&](const auto& p) { return p.first == name; }); \
110+
if (it != obj->end()) { \
111+
it->second = value; \
112+
auto result = rfl::from_generic<decltype(this->PropsMember), rfl::UnderlyingEnums>(generic); \
113+
if (result) { \
114+
this->PropsMember = result.value(); \
115+
OnPropertyChanged(name); \
116+
return true; \
117+
} \
101118
} \
102119
} \
103120
return false; \
@@ -106,7 +123,7 @@ namespace Nova {
106123
std::string GetName() const override { \
107124
/* Now we get the whole struct and just pick the Name out of it */ \
108125
auto& instance = ::Nova::Internal::get_instance_props(this->PropsMember); \
109-
return std::string(instance.Name.value()); \
126+
return std::string(instance.Name); \
110127
}
111128

112129
// For classes WITHOUT props (like Folder/DataModel)
@@ -143,19 +160,7 @@ namespace Nova {
143160
std::shared_ptr<Instance> GetParent() const { return parent.lock(); }
144161
const std::vector<std::shared_ptr<Instance>>& GetChildren() const { return children; }
145162

146-
void SetParent(std::shared_ptr<Instance> newParent) {
147-
auto self = shared_from_this();
148-
if (auto p = parent.lock()) {
149-
auto& c = p->children;
150-
c.erase(std::remove(c.begin(), c.end(), self), c.end());
151-
}
152-
parent = newParent;
153-
if (newParent) {
154-
newParent->children.push_back(self);
155-
}
156-
157-
OnAncestorChanged(self, newParent);
158-
}
163+
void SetParent(std::shared_ptr<Instance> newParent);
159164

160165
std::shared_ptr<DataModel> GetDataModel();
161166
bool IsDescendantOf(std::shared_ptr<Instance> other);

src/Engine/Objects/Model.hpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
// (at your option) any later version.
88

99
#pragma once
10-
#include <rfl/Flatten.hpp>
1110
#include "Engine/Objects/Instance.hpp"
1211

1312
namespace Nova {
13+
1414
namespace Props {
1515
struct ModelProps {
1616
rfl::Flatten<InstanceProps> base;
@@ -20,12 +20,11 @@ namespace Nova {
2020
class Model : public Instance {
2121
public:
2222
Props::ModelProps props;
23+
NOVA_OBJECT(Model, props)
24+
2325
Model() : Instance("Model") {}
2426

25-
// unsupported by reflection
26-
// reference to an instance, handled using referents in ROBLOX
2727
std::weak_ptr<Instance> PrimaryPart;
28-
29-
NOVA_OBJECT(Model, props)
3028
};
29+
3130
}

src/Engine/Reflection/LevelLoader.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Engine/Reflection/LevelLoader.hpp"
1111
#include "Common/MathTypes.hpp"
1212
#include "Engine/Services/PhysicsService.hpp"
13+
#include "Engine/Objects/Model.hpp"
1314
#include <SDL3/SDL_log.h>
1415
#include <map>
1516
#include <string>
@@ -180,6 +181,8 @@ namespace Nova {
180181
if (name == "canCollide") name = "CanCollide";
181182
if (name == "CoordinateFrame") name = "CFrame";
182183
if (name == "size") name = "Size";
184+
if (name == "archivable") name = "Archivable";
185+
if (name == "name") name = "Name";
183186

184187
if (type == "string") propMap[name] = rfl::Generic(std::string(prop.text().get()));
185188
else if (type == "bool") propMap[name] = rfl::Generic(prop.text().as_bool());
@@ -273,8 +276,8 @@ namespace Nova {
273276

274277
// Handle specific non-reflected references
275278
if (propName == "PrimaryPart") {
276-
if (auto ws = std::dynamic_pointer_cast<Workspace>(inst)) {
277-
ws->PrimaryPart = target;
279+
if (auto mod = std::dynamic_pointer_cast<Model>(inst)) {
280+
mod->PrimaryPart = target;
278281
}
279282
}
280283
else if (propName == "CurrentCamera") {

src/Engine/Reflection/RegisterClasses.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "Engine/Services/Workspace.hpp"
1313
#include "Engine/Services/DataModel.hpp"
1414
#include "Engine/Services/ScriptContext.hpp"
15+
#include "Engine/Objects/Model.hpp"
1516

1617
namespace Nova {
1718
void RegisterClasses() {
@@ -24,6 +25,7 @@ namespace Nova {
2425
ClassDescriptorBuilder<Workspace>("Workspace", "Instance");
2526
ClassDescriptorBuilder<DataModel>("DataModel", "Instance");
2627
ClassDescriptorBuilder<ScriptContext>("ScriptContext", "Instance");
28+
ClassDescriptorBuilder<Model>("Model", "Instance");
2729

2830
// ... more classes ...
2931
}

src/Engine/Rendering/Renderer_Resources.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include "Renderer.hpp"
1010
#include "Geometry.hpp"
11+
#include "Engine/Services/Lighting.hpp"
12+
#include "Engine/Objects/Sky.hpp"
1113
#include <SDL3/SDL_gpu.h>
1214
#include <SDL3_image/SDL_image.h>
1315
#include <cstring>

0 commit comments

Comments
 (0)