28 removeDebugDrawables();
39 floorOpts.
position = glm::vec3(0.0f, -0.5f, 0.0f);
40 floorOpts.
scale = glm::vec3(2000.0f, 1.0f, 300000.0f);
47 keysOptions.
base.
position = glm::vec3(0.0f, 20.0f, 0.0f);
87 std::unique_ptr<SceneObject> primitive =
nullptr;
90 primitive = std::make_unique<SceneObject>(
this,
"prim_cube", shader, options);
93 primitive = std::make_unique<SceneObject>(
this,
"prim_sphere", shader, options);
96 assert(primitive !=
nullptr);
100 sceneObjects.push_back(std::move(primitive));
110 std::unique_ptr<SceneObject> primitive = std::make_unique<SceneObject>(
this, meshName, shader, options);
111 assert(primitive !=
nullptr);
116 sceneObjects.push_back(std::move(primitive));
131 if (currentGizmo && currentGizmo->getTarget() == obj) {
142 pickableObjects.erase(
143 std::remove(pickableObjects.begin(), pickableObjects.end(),
static_cast<IPickable*
>(obj)),
144 pickableObjects.end()
148 const std::string& objectName = obj->
getName();
149 auto nameIt = usedNames.find(objectName);
150 if (nameIt != usedNames.end() && nameIt->second == obj) {
151 usedNames.erase(nameIt);
155 auto it = std::find_if(sceneObjects.begin(), sceneObjects.end(),
156 [obj](
const std::unique_ptr<SceneObject>& ptr) {
157 return ptr.get() == obj;
160 if (it != sceneObjects.end()) {
161 sceneObjects.erase(it);
167 while (!sceneObjects.empty()) {
171 sceneObjectsByID.clear();
179 auto it = sceneObjectsByID.find(objectID);
180 return it != sceneObjectsByID.end() ? it->second :
nullptr;
183std::string SceneManager::generateDefaultName(
const CreationOptions& options) {
184 return std::visit([&](
auto&& opt) -> std::string {
185 using T = std::decay_t<
decltype(opt)>;
187 if constexpr (std::is_same_v<T, PointMassOptions>)
190 if constexpr (std::is_same_v<T, RigidBodyOptions>)
193 return "Scene Object";
198 auto it = usedNames.find(name);
200 if (it == usedNames.end())
203 return it->second == self;
209 const std::string& oldName = obj->
getName();
210 if (oldName == newName)
215 if (!oldName.empty()) {
216 auto it = usedNames.find(oldName);
217 if (it != usedNames.end() && it->second == obj) {
223 usedNames[newName] = obj;
229 if (usedNames.find(baseName) == usedNames.end())
234 std::string candidate = baseName +
" (" + std::to_string(index) +
")";
235 if (usedNames.find(candidate) == usedNames.end())
273 mousePos.x(), mousePos.y(),
274 fbSize.width(), fbSize.height(),
298 const bool isPress = (type == QEvent::MouseButtonPress);
299 const bool isRelease = (type == QEvent::MouseButtonRelease);
302 if (button == Qt::RightButton) {
304 rightClickStartDir = camera->
front;
315 if (glm::distance(camera->
front, rightClickStartDir) < 0.05f) {
319 if (
auto* sceneObj =
dynamic_cast<SceneObject*
>(hit)) {
326 if (button == Qt::LeftButton) {
329 IPickable* clickedObject = hit ? hit->object :
nullptr;
330 bool clickedCurrentGizmo = clickedObject && currentGizmo && (clickedObject->
getObjectID() == currentGizmo->getObjectID());
334 if (!clickedObject || (!
selectedIDs.empty() && !clickedCurrentGizmo)) {
343 if (!clickedCurrentGizmo) {
346 }
else if (isRelease && currentGizmo) {
347 currentGizmo->handleRelease();
356 if (heldKeys.contains(Qt::Key_Escape))
359 static const std::unordered_map<int, GizmoType> keyToGizmoType = {
365 for (
auto& [key, type] : keyToGizmoType) {
366 if (heldKeys.contains(key)) {
368 selectedGizmoType = type;
369 if (currentGizmo && oldType != selectedGizmoType)
375 if (currentGizmo && currentGizmo->getIsDragging()) {
376 hoveredIDs.insert(currentGizmo->getObjectID());
378 currentGizmo->handleDrag(ray);
381 const float cameraDt = heldKeys.contains(Qt::Key_Shift) ? dt * 0.1f : dt;
382 if (heldKeys.contains(Qt::Key_A))
384 if (heldKeys.contains(Qt::Key_D))
386 if (heldKeys.contains(Qt::Key_W))
388 if (heldKeys.contains(Qt::Key_S))
393 if (heldKeys.contains(Qt::Key_Z)) {
396 if (heldKeys.contains(Qt::Key_X)) {
399 if (heldKeys.contains(Qt::Key_P)) {
401 std::cout <<
"Save Success!" << std::endl;
403 std::cout <<
"Save Failed!" << std::endl;
405 if (heldKeys.contains(Qt::Key_O)) {
407 std::cout <<
"Load Success!" << std::endl;
409 std::cout <<
"Load Failed!" << std::endl;
415 if (currentGizmo->getTarget() == newTarget && redraw ==
false) {
419 currentGizmo = std::make_unique<Gizmo>(selectedGizmoType,
this, newTarget);
422 currentGizmo = std::make_unique<Gizmo>(selectedGizmoType,
this, newTarget);
446 currentGizmo.reset();
450 pickableObjects.erase(
451 std::remove(pickableObjects.begin(), pickableObjects.end(), obj),
452 pickableObjects.end());
465void SceneManager::initDebugDrawables() {
466 if (!
scene || !window)
return;
468 pathTraces = std::make_unique<PathTraces>(
this, window);
469 forces = std::make_unique<Forces>(
this);
470 colliders = std::make_unique<Colliders>(
this, window);
477void SceneManager::removeDebugDrawables() {
505 pathTraces->setEnabled(dbg.showAllPathTrails);
506 pathTraces->setTimeWindow(dbg.pathTrailTime);
509 forces->setEnabled(dbg.showForces);
512 colliders->setEnabled(dbg.showColliders);
GizmoType
Types of transformation gizmos.
std::variant< ObjectOptions, PointMassOptions, RigidBodyOptions > CreationOptions
static AppSettings & getInstance()
glm::mat4 getProjMatrix() const
glm::mat4 getViewMatrix() const
void processKeyboard(Movement direction, float deltaTime)
void resetView(const glm::vec3 &newPosition=glm::vec3(0.0f, 10.0f, 30.0f))
void focusOn(SceneObject *obj)
void setTarget(SceneObject *obj)
Abstract interface for objects that can be selected and interacted with using the mouse.
virtual uint32_t getObjectID() const =0
Gets the unique identifier for this object.
virtual void handleClick(const Math::Ray &ray, float distance)=0
Handles a mouse click on this object.
QSize getFramebufferSize() const
void resetRenderClock(float simTime=0.0f)
void setMouseCaptured(bool captured)
QPointF getMousePos() const
bool isMouseCaptured() const
void setVelocity(const glm::vec3 &vel, BodyLock lock)
static Shader * getShader(const std::string &name)
void contextMenuRequested(const QPoint &globalPos, SceneObject *object)
bool saveScene(const QString &file)
void removePickable(IPickable *obj)
SceneObject * createObject(const std::string &meshName, Shader *shader=ResourceManager::getShader("basic"), const CreationOptions &=ObjectOptions{})
void objectRemoved(SceneObject *obj)
void stopSimulation() const
void selectedItem(SceneObject *object)
void focusObject(SceneObject *target)
const SceneObject * getCameraTarget() const
void addPickable(IPickable *obj)
void startSimulation() const
void deleteObject(SceneObject *obj)
SceneObject * getObjectByID(uint32_t objectID) const
void setGizmoFor(SceneObject *newTarget, bool redraw=false)
void processHeldKeys(const QSet< int > &heldKeys, float dt)
bool isNameUnique(const std::string &name, SceneObject *self) const
void selectObject(SceneObject *obj)
void setSimSpeed(float newSpeed)
std::unordered_set< uint32_t > selectedIDs
void objectAdded(SceneObject *obj)
void objectRenamed(SceneObject *obj, const QString &newName)
bool loadScene(const QString &file)
void setGlobalAcceleration(const glm::vec3 &newAcceleration) const
SceneObject * createPrimitive(Primitive type, Shader *shader, const CreationOptions &=ObjectOptions{})
void updateHoverState(const Math::Ray &mouseRay)
void deleteCurrentGizmo()
std::unique_ptr< Physics::PhysicsSystem > physicsSystem
SimulationStopCondition stopCondition
SceneManager(OpenGLWindow *win, Scene *scene)
void setObjectName(SceneObject *obj, const std::string &newName)
void setCameraTarget(SceneObject *target)
std::string makeUniqueName(const std::string &baseName) const
void removeDrawable(IDrawable *obj) const
void handleMouseButton(Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods)
void addDrawable(IDrawable *obj) const
std::unordered_set< uint32_t > hoveredIDs
bool loadPreset(const ScenePresets::PresetDescriptor &preset)
void applyDebugSettings()
void setSelectFor(SceneObject *obj, bool flag=true)
const std::vector< std::unique_ptr< SceneObject > > & getObjects() const
std::unordered_map< Physics::PhysicsBody *, glm::vec3 > PosMap
Physics::PhysicsBody * getPhysicsBody() const
const std::string & getName() const
static void setPhysicsPosMap(const PosMap &m)
static void setRenderOrigin(const glm::vec3 &origin)
uint32_t getObjectID() const override
Gets the unique identifier for this object.
void setName(std::string name)
bool loadFromJson(const QString &filename)
bool saveToJson(const QString &filename) const
void removeDrawable(IDrawable *drawable)
void addDrawable(IDrawable *drawable)
constexpr float STANDARD_GRAVITY
glm::vec3 screenToWorldRayDirection(double mouseX, double mouseY, int fbWidth, int fbHeight, const glm::mat4 &view, const glm::mat4 &projection)
Converts screen coordinates to a world-space ray direction.
std::optional< HitResult > findFirstHit(const std::vector< IPickable * > &objects, const Ray &ray, IPickable *priority=nullptr)
Finds the first object intersected by a ray.
Represents a ray in 3D space.
Hash specialization for Vertex to enable use in unordered containers.
static RigidBodyOptions Box(ObjectOptions base, bool isStatic=false, double mass=1.0, glm::vec3 velocity=glm::vec3(0.0f))