Files
QuaternionEngine/src/scene/vk_scene.h

264 lines
11 KiB
C++

#pragma once
#include <core/types.h>
#include <core/world.h>
#include <scene/camera.h>
#include <scene/camera/camera_rig.h>
#include <unordered_map>
#include <memory>
#include <optional>
#include <chrono>
#include <glm/vec2.hpp>
#include <string>
#include "scene/vk_loader.h"
class EngineContext;
class PlanetSystem;
struct RenderObject
{
// Geometry and material binding
uint32_t indexCount;
uint32_t firstIndex;
VkBuffer indexBuffer;
VkBuffer vertexBuffer; // for RG buffer tracking (device-address path still used in shader)
MaterialInstance *material;
Bounds bounds;
glm::mat4 transform;
VkDeviceAddress vertexBufferAddress;
// Optional debug/source information (may be null/unused for some objects).
MeshAsset *sourceMesh = nullptr;
uint32_t surfaceIndex = 0;
// Unique per-draw identifier for ID-buffer picking (0 = none).
uint32_t objectID = 0;
// Optional logical owner for editor/picking (instance name etc.).
enum class OwnerType : uint8_t
{
None = 0,
StaticGLTF, // loaded scene
GLTFInstance, // runtime glTF instance with transform
MeshInstance // dynamic primitive/mesh instance
};
OwnerType ownerType = OwnerType::None;
std::string ownerName;
// Optional owning glTF scene and node for this draw (null for procedural/dynamic meshes).
LoadedGLTF *sourceScene = nullptr;
Node *sourceNode = nullptr;
};
struct DrawContext
{
std::vector<RenderObject> OpaqueSurfaces;
std::vector<RenderObject> TransparentSurfaces;
// Monotonic counter used to assign stable per-frame object IDs.
uint32_t nextID = 1;
// Optional per-instance glTF node local overrides (additive layer in local space).
// When non-null, MeshNode::Draw will rebuild world transforms using these offsets.
const std::unordered_map<const Node*, glm::mat4> *gltfNodeLocalOverrides = nullptr;
};
class SceneManager
{
public:
SceneManager();
~SceneManager();
void init(EngineContext *context);
void cleanup();
void update_scene();
Camera &getMainCamera() { return mainCamera; }
const Camera &getMainCamera() const { return mainCamera; }
CameraRig &getCameraRig() { return cameraRig; }
const CameraRig &getCameraRig() const { return cameraRig; }
WorldVec3 get_world_origin() const { return _origin_world; }
glm::vec3 get_camera_local_position() const { return _camera_position_local; }
// Ray-pick against current DrawContext using per-surface Bounds.
// mousePosPixels is in window coordinates (SDL), origin at top-left.
// Returns true if any object was hit, filling outObject and outWorldPos.
bool pick(const glm::vec2 &mousePosPixels, RenderObject &outObject, WorldVec3 &outWorldPos);
// Resolve an object ID (from ID buffer) back to the RenderObject for
// the most recently built DrawContext. Returns false if not found or id==0.
bool resolveObjectID(uint32_t id, RenderObject &outObject) const;
// Select all objects whose projected bounds intersect the given screen-space
// rectangle (window coordinates, origin top-left). Results are appended to outObjects.
void selectRect(const glm::vec2 &p0, const glm::vec2 &p1, std::vector<RenderObject> &outObjects) const;
const GPUSceneData &getSceneData() const { return sceneData; }
// Sunlight (directional light) access
void setSunlightDirection(const glm::vec3& dir);
glm::vec3 getSunlightDirection() const;
void setSunlightColor(const glm::vec3& color, float intensity);
glm::vec3 getSunlightColor() const;
float getSunlightIntensity() const;
// Delta time (seconds) for the current frame
float getDeltaTime() const { return _deltaTime; }
DrawContext &getMainDrawContext() { return mainDrawContext; }
void loadScene(const std::string &name, std::shared_ptr<LoadedGLTF> scene);
std::shared_ptr<LoadedGLTF> getScene(const std::string &name);
// Dynamic renderables API
struct MeshInstance
{
std::shared_ptr<MeshAsset> mesh;
WorldVec3 translation_world{0.0, 0.0, 0.0};
glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
glm::vec3 scale{1.0f, 1.0f, 1.0f};
std::optional<BoundsType> boundsTypeOverride;
};
void addMeshInstance(const std::string &name, std::shared_ptr<MeshAsset> mesh,
const glm::mat4 &transform = glm::mat4(1.f),
std::optional<BoundsType> boundsType = {});
bool getMeshInstanceTransform(const std::string &name, glm::mat4 &outTransform);
bool setMeshInstanceTransform(const std::string &name, const glm::mat4 &transform);
bool getMeshInstanceTransformLocal(const std::string &name, glm::mat4 &outTransformLocal) const;
bool setMeshInstanceTransformLocal(const std::string &name, const glm::mat4 &transformLocal);
bool getMeshInstanceTRSWorld(const std::string &name, WorldVec3 &outTranslationWorld, glm::quat &outRotation, glm::vec3 &outScale) const;
bool setMeshInstanceTRSWorld(const std::string &name, const WorldVec3 &translationWorld, const glm::quat &rotation, const glm::vec3 &scale);
bool removeMeshInstance(const std::string &name);
void clearMeshInstances();
// GLTF instances (runtime-spawned scenes with transforms)
struct GLTFInstance
{
std::shared_ptr<LoadedGLTF> scene;
WorldVec3 translation_world{0.0, 0.0, 0.0};
glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
glm::vec3 scale{1.0f, 1.0f, 1.0f};
LoadedGLTF::AnimationState animation;
// Per-instance local-space pose offsets for nodes in this glTF scene.
// The offset matrix is post-multiplied onto the node's localTransform.
std::unordered_map<const Node*, glm::mat4> nodeLocalOverrides;
};
void addGLTFInstance(const std::string &name, std::shared_ptr<LoadedGLTF> scene,
const glm::mat4 &transform = glm::mat4(1.f));
bool removeGLTFInstance(const std::string &name);
bool getGLTFInstanceTransform(const std::string &name, glm::mat4 &outTransform);
bool setGLTFInstanceTransform(const std::string &name, const glm::mat4 &transform);
bool getGLTFInstanceTransformLocal(const std::string &name, glm::mat4 &outTransformLocal) const;
bool setGLTFInstanceTransformLocal(const std::string &name, const glm::mat4 &transformLocal);
bool getGLTFInstanceTRSWorld(const std::string &name, WorldVec3 &outTranslationWorld, glm::quat &outRotation, glm::vec3 &outScale) const;
bool setGLTFInstanceTRSWorld(const std::string &name, const WorldVec3 &translationWorld, const glm::quat &rotation, const glm::vec3 &scale);
void clearGLTFInstances();
// Per-instance glTF node pose overrides (local-space, layered on top of animation/base TRS).
// 'offset' is post-multiplied onto the node's localTransform for this instance only.
bool setGLTFInstanceNodeOffset(const std::string &instanceName,
const std::string &nodeName,
const glm::mat4 &offset);
bool clearGLTFInstanceNodeOffset(const std::string &instanceName,
const std::string &nodeName);
void clearGLTFInstanceNodeOffsets(const std::string &instanceName);
// Animation control helpers (glTF)
// Note: a LoadedGLTF may be shared by multiple instances; changing
// the active animation on a scene or instance affects all users
// of that shared LoadedGLTF.
bool setSceneAnimation(const std::string &sceneName, int animationIndex, bool resetTime = true);
bool setSceneAnimation(const std::string &sceneName, const std::string &animationName, bool resetTime = true);
bool setSceneAnimationLoop(const std::string &sceneName, bool loop);
bool setGLTFInstanceAnimation(const std::string &instanceName, int animationIndex, bool resetTime = true);
bool setGLTFInstanceAnimation(const std::string &instanceName, const std::string &animationName, bool resetTime = true);
bool setGLTFInstanceAnimationLoop(const std::string &instanceName, bool loop);
struct PointLight
{
WorldVec3 position_world;
float radius;
glm::vec3 color;
float intensity;
};
void addPointLight(const PointLight &light);
void clearPointLights();
size_t getPointLightCount() const { return pointLights.size(); }
bool getPointLight(size_t index, PointLight &outLight) const;
bool setPointLight(size_t index, const PointLight &light);
bool removePointLight(size_t index);
const std::vector<PointLight> &getPointLights() const { return pointLights; }
struct SpotLight
{
WorldVec3 position_world;
glm::vec3 direction{0.0f, -1.0f, 0.0f}; // world-space unit vector
float radius = 10.0f;
glm::vec3 color{1.0f, 1.0f, 1.0f};
float intensity = 1.0f;
// Cone half-angles in degrees (inner <= outer).
float inner_angle_deg = 15.0f;
float outer_angle_deg = 25.0f;
};
void addSpotLight(const SpotLight &light);
void clearSpotLights();
size_t getSpotLightCount() const { return spotLights.size(); }
bool getSpotLight(size_t index, SpotLight &outLight) const;
bool setSpotLight(size_t index, const SpotLight &light);
bool removeSpotLight(size_t index);
const std::vector<SpotLight> &getSpotLights() const { return spotLights; }
struct SceneStats
{
float scene_update_time = 0.f;
} stats;
struct PickingDebug
{
bool usedMeshBVH = false;
bool meshBVHHit = false;
bool meshBVHFallbackBox = false;
uint32_t meshBVHPrimCount = 0;
uint32_t meshBVHNodeCount = 0;
};
const PickingDebug &getPickingDebug() const { return pickingDebug; }
PlanetSystem *get_planet_system() const { return _planetSystem.get(); }
// Returns the LoadedGLTF scene for a named GLTF instance, or nullptr if not found.
std::shared_ptr<LoadedGLTF> getGLTFInstanceScene(const std::string &instanceName) const;
private:
EngineContext *_context = nullptr;
Camera mainCamera = {};
CameraRig cameraRig{};
GPUSceneData sceneData = {};
DrawContext mainDrawContext;
std::vector<PointLight> pointLights;
std::vector<SpotLight> spotLights;
WorldVec3 _origin_world{0.0, 0.0, 0.0};
glm::vec3 _camera_position_local{0.0f, 0.0f, 0.0f};
double _floating_origin_recenter_threshold = 1000.0;
double _floating_origin_snap_size = 100.0;
float _deltaTime = 0.0f;
std::chrono::steady_clock::time_point _lastFrameTime{};
std::unordered_map<std::string, std::shared_ptr<LoadedGLTF> > loadedScenes;
// Per-named static glTF scene animation state (independent of instances).
std::unordered_map<std::string, LoadedGLTF::AnimationState> sceneAnimations;
std::unordered_map<std::string, std::shared_ptr<Node> > loadedNodes;
std::unordered_map<std::string, MeshInstance> dynamicMeshInstances;
std::unordered_map<std::string, GLTFInstance> dynamicGLTFInstances;
// Keep GLTF assets alive until after the next frame fence to avoid destroying
// GPU resources that might still be in-flight.
std::vector<std::shared_ptr<LoadedGLTF>> pendingGLTFRelease;
std::unique_ptr<PlanetSystem> _planetSystem;
PickingDebug pickingDebug{};
};