WE GOT TEXTURES BABY
This commit is contained in:
@@ -3,12 +3,14 @@ add_subdirectory(third_party)
|
|||||||
set(SRC_FILES
|
set(SRC_FILES
|
||||||
"src/App.cpp"
|
"src/App.cpp"
|
||||||
|
|
||||||
|
"src/Graphics/BindlessSetManager.cpp"
|
||||||
"src/Graphics/Camera.cpp"
|
"src/Graphics/Camera.cpp"
|
||||||
"src/Graphics/GfxDevice.cpp"
|
"src/Graphics/GfxDevice.cpp"
|
||||||
"src/Graphics/ImageCache.cpp"
|
"src/Graphics/ImageCache.cpp"
|
||||||
"src/Graphics/ImageLoader.cpp"
|
"src/Graphics/ImageLoader.cpp"
|
||||||
"src/Graphics/ImmediateExecuter.cpp"
|
"src/Graphics/ImmediateExecuter.cpp"
|
||||||
"src/Graphics/Init.cpp"
|
"src/Graphics/Init.cpp"
|
||||||
|
"src/Graphics/MaterialCache.cpp"
|
||||||
"src/Graphics/MeshCache.cpp"
|
"src/Graphics/MeshCache.cpp"
|
||||||
"src/Graphics/Pipeline.cpp"
|
"src/Graphics/Pipeline.cpp"
|
||||||
"src/Graphics/Renderer.cpp"
|
"src/Graphics/Renderer.cpp"
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ void main()
|
|||||||
{
|
{
|
||||||
MaterialData material = pcs.sceneData.materials.data[pcs.materialID];
|
MaterialData material = pcs.sceneData.materials.data[pcs.materialID];
|
||||||
|
|
||||||
// vec4 diffuse = sampleTexture2DLinear(material.diffuseTex, inUV);
|
vec4 diffuse = sampleTexture2DLinear(material.diffuseTex, inUV);
|
||||||
|
outFragColor = diffuse;
|
||||||
|
|
||||||
|
|
||||||
// if (diffuse.a < 0.1) {
|
// if (diffuse.a < 0.1) {
|
||||||
// discard;
|
// discard;
|
||||||
// }
|
// }
|
||||||
@@ -121,5 +124,5 @@ void main()
|
|||||||
// // fragColor = normal;
|
// // fragColor = normal;
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
outFragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
|
// outFragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
destrum/assets_src/textures/kobe.png
Normal file
BIN
destrum/assets_src/textures/kobe.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
@@ -34,6 +34,7 @@ protected:
|
|||||||
|
|
||||||
CPUMesh testMesh{};
|
CPUMesh testMesh{};
|
||||||
MeshID testMeshID;
|
MeshID testMeshID;
|
||||||
|
MaterialID testMaterialID;
|
||||||
|
|
||||||
GfxDevice gfxDevice;
|
GfxDevice gfxDevice;
|
||||||
GameRenderer renderer;
|
GameRenderer renderer;
|
||||||
@@ -41,8 +42,7 @@ protected:
|
|||||||
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
||||||
|
|
||||||
MeshCache meshCache;
|
MeshCache meshCache;
|
||||||
|
MaterialCache materialCache;
|
||||||
InputManager inputManager;
|
|
||||||
|
|
||||||
bool isRunning{false};
|
bool isRunning{false};
|
||||||
bool gamePaused{false};
|
bool gamePaused{false};
|
||||||
|
|||||||
33
destrum/include/destrum/Graphics/BindlessSetManager.h
Normal file
33
destrum/include/destrum/Graphics/BindlessSetManager.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef BINDLESSSETMANAGER_H
|
||||||
|
#define BINDLESSSETMANAGER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
struct GPUImage;
|
||||||
|
|
||||||
|
class BindlessSetManager {
|
||||||
|
public:
|
||||||
|
void init(VkDevice device, float maxAnisotropy);
|
||||||
|
void cleanup(VkDevice device);
|
||||||
|
|
||||||
|
VkDescriptorSetLayout getDescSetLayout() const { return descSetLayout; }
|
||||||
|
const VkDescriptorSet& getDescSet() const { return descSet; }
|
||||||
|
|
||||||
|
void addImage(VkDevice device, std::uint32_t id, const VkImageView imageView);
|
||||||
|
void addSampler(VkDevice device, std::uint32_t id, VkSampler sampler);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initDefaultSamplers(VkDevice device, float maxAnisotropy);
|
||||||
|
|
||||||
|
VkDescriptorPool descPool;
|
||||||
|
VkDescriptorSetLayout descSetLayout;
|
||||||
|
VkDescriptorSet descSet;
|
||||||
|
|
||||||
|
VkSampler nearestSampler;
|
||||||
|
VkSampler linearSampler;
|
||||||
|
VkSampler shadowMapSampler;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BINDLESSSETMANAGER_H
|
||||||
@@ -57,6 +57,11 @@ public:
|
|||||||
|
|
||||||
void waitIdle();
|
void waitIdle();
|
||||||
|
|
||||||
|
BindlessSetManager& getBindlessSetManager();
|
||||||
|
VkDescriptorSetLayout getBindlessDescSetLayout() const;
|
||||||
|
const VkDescriptorSet& getBindlessDescSet() const;
|
||||||
|
void bindBindlessDescSet(VkCommandBuffer cmd, VkPipelineLayout layout) const;
|
||||||
|
|
||||||
void immediateSubmit(ImmediateExecuteFunction&& f) const;
|
void immediateSubmit(ImmediateExecuteFunction&& f) const;
|
||||||
|
|
||||||
vkb::Device getDevice() const { return device; }
|
vkb::Device getDevice() const { return device; }
|
||||||
@@ -96,6 +101,8 @@ public:
|
|||||||
GPUImage loadImageFromFileRaw(const std::filesystem::path& path, VkFormat format, VkImageUsageFlags usage, bool mipMap) const;
|
GPUImage loadImageFromFileRaw(const std::filesystem::path& path, VkFormat format, VkImageUsageFlags usage, bool mipMap) const;
|
||||||
void destroyImage(const GPUImage& image) const;
|
void destroyImage(const GPUImage& image) const;
|
||||||
|
|
||||||
|
ImageID getWhiteTextureID() { return whiteImageId; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vkb::Instance instance;
|
vkb::Instance instance;
|
||||||
vkb::PhysicalDevice physicalDevice;
|
vkb::PhysicalDevice physicalDevice;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <destrum/Graphics/ids.h>
|
#include <destrum/Graphics/ids.h>
|
||||||
#include <destrum/Graphics/GPUImage.h>
|
#include <destrum/Graphics/GPUImage.h>
|
||||||
|
#include <destrum/Graphics/BindlessSetManager.h>
|
||||||
|
|
||||||
// #include <destrum/Graphics/Vulkan/BindlessSetManager.h>
|
// #include <destrum/Graphics/Vulkan/BindlessSetManager.h>
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ public:
|
|||||||
|
|
||||||
void destroyImages();
|
void destroyImages();
|
||||||
|
|
||||||
// BindlessSetManager bindlessSetManager;
|
BindlessSetManager bindlessSetManager;
|
||||||
|
|
||||||
void setErrorImageId(ImageID id) { errorImageId = id; }
|
void setErrorImageId(ImageID id) { errorImageId = id; }
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
struct MaterialData {
|
struct MaterialData {
|
||||||
glm::vec3 baseColor;
|
glm::vec4 baseColor;
|
||||||
glm::vec4 metalRoughnessEmissive;
|
glm::vec4 metalRoughnessEmissive;
|
||||||
std::uint32_t diffuseTex;
|
std::uint32_t diffuseTex;
|
||||||
std::uint32_t normalTex;
|
std::uint32_t normalTex;
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ public:
|
|||||||
void init(GfxDevice& gfxDevice);
|
void init(GfxDevice& gfxDevice);
|
||||||
void cleanup(GfxDevice& gfxDevice);
|
void cleanup(GfxDevice& gfxDevice);
|
||||||
|
|
||||||
MaterialId addMaterial(GfxDevice& gfxDevice, Material material);
|
MaterialID addMaterial(GfxDevice& gfxDevice, Material material);
|
||||||
const Material& getMaterial(MaterialId id) const;
|
const Material& getMaterial(MaterialID id) const;
|
||||||
|
|
||||||
MaterialId getFreeMaterialId() const;
|
MaterialID getFreeMaterialId() const;
|
||||||
MaterialId getPlaceholderMaterialId() const;
|
MaterialID getPlaceholderMaterialId() const;
|
||||||
|
|
||||||
const GPUBuffer& getMaterialDataBuffer() const { return materialDataBuffer; }
|
const GPUBuffer& getMaterialDataBuffer() const { return materialDataBuffer; }
|
||||||
VkDeviceAddress getMaterialDataBufferAddress() const { return materialDataBuffer.address; }
|
VkDeviceAddress getMaterialDataBufferAddress() const { return materialDataBuffer.address; }
|
||||||
@@ -33,7 +33,7 @@ private:
|
|||||||
GPUBuffer materialDataBuffer;
|
GPUBuffer materialDataBuffer;
|
||||||
|
|
||||||
// material which is used for meshes without materials
|
// material which is used for meshes without materials
|
||||||
MaterialId placeholderMaterialId{NULL_MATERIAL_ID};
|
MaterialID placeholderMaterialId{NULL_MATERIAL_ID};
|
||||||
|
|
||||||
ImageID defaultNormalMapTextureID{NULL_IMAGE_ID};
|
ImageID defaultNormalMapTextureID{NULL_IMAGE_ID};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ struct MeshDrawCommand {
|
|||||||
|
|
||||||
// If set - mesh will be drawn with overrideMaterialId
|
// If set - mesh will be drawn with overrideMaterialId
|
||||||
// instead of whatever material the mesh has
|
// instead of whatever material the mesh has
|
||||||
MaterialId materialId{NULL_MATERIAL_ID};
|
MaterialID materialId{NULL_MATERIAL_ID};
|
||||||
|
|
||||||
bool castShadow{true};
|
bool castShadow{true};
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public:
|
|||||||
float fogDensity;
|
float fogDensity;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit GameRenderer(MeshCache& meshCache);
|
explicit GameRenderer(MeshCache& meshCache, MaterialCache& matCache);
|
||||||
|
|
||||||
void init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize);
|
void init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize);
|
||||||
void beginDrawing(GfxDevice& gfxDevice);
|
void beginDrawing(GfxDevice& gfxDevice);
|
||||||
@@ -32,7 +32,7 @@ public:
|
|||||||
void draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData);
|
void draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData);
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
void drawMesh(MeshID id, const glm::mat4& transform, MaterialId materialId);
|
void drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId);
|
||||||
const GPUImage& getDrawImage(const GfxDevice& gfx_device) const;
|
const GPUImage& getDrawImage(const GfxDevice& gfx_device) const;
|
||||||
|
|
||||||
void resize(GfxDevice& gfxDevice, const glm::ivec2& newSize) {
|
void resize(GfxDevice& gfxDevice, const glm::ivec2& newSize) {
|
||||||
@@ -43,7 +43,7 @@ private:
|
|||||||
void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate);
|
void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate);
|
||||||
|
|
||||||
MeshCache& meshCache;
|
MeshCache& meshCache;
|
||||||
// MaterialCache& materialCache;
|
MaterialCache& materialCache;
|
||||||
|
|
||||||
std::vector<MeshDrawCommand> meshDrawCommands;
|
std::vector<MeshDrawCommand> meshDrawCommands;
|
||||||
std::vector<std::size_t> sortedMeshDrawCommands;
|
std::vector<std::size_t> sortedMeshDrawCommands;
|
||||||
@@ -77,8 +77,6 @@ private:
|
|||||||
|
|
||||||
NBuffer sceneDataBuffer;
|
NBuffer sceneDataBuffer;
|
||||||
|
|
||||||
MaterialCache materialCache;
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<MeshPipeline> meshPipeline;
|
std::unique_ptr<MeshPipeline> meshPipeline;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -94,12 +94,12 @@ static std::vector<CPUMesh::Vertex> vertices = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<uint32_t> indices = {
|
static std::vector<uint32_t> indices = {
|
||||||
0, 1, 2, 2, 3, 0, // Front
|
0, 1, 2, 2, 3, 0, // Front (+Z)
|
||||||
4, 5, 6, 6, 7, 4, // Back
|
4, 7, 6, 6, 5, 4, // Back (-Z)
|
||||||
8, 9,10, 10,11, 8, // Right
|
8, 9,10, 10,11, 8, // Right (+X)
|
||||||
12,13,14, 14,15,12, // Left
|
12,13,14, 14,15,12, // Left (-X)
|
||||||
16,17,18, 18,19,16, // Top
|
16,17,18, 18,19,16, // Top (+Y)
|
||||||
20,21,22, 22,23,20 // Bottom
|
20,21,22, 22,23,20 // Bottom(-Y)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ constexpr MeshID NULL_MESH_ID = std::numeric_limits<std::size_t>::max();
|
|||||||
using ImageID = std::uint16_t;
|
using ImageID = std::uint16_t;
|
||||||
constexpr ImageID NULL_IMAGE_ID = std::numeric_limits<std::uint16_t>::max();
|
constexpr ImageID NULL_IMAGE_ID = std::numeric_limits<std::uint16_t>::max();
|
||||||
|
|
||||||
using MaterialId = std::uint32_t;
|
using MaterialID = std::uint32_t;
|
||||||
constexpr MaterialId NULL_MATERIAL_ID = std::numeric_limits<std::uint32_t>::max();
|
constexpr MaterialID NULL_MATERIAL_ID = std::numeric_limits<std::uint32_t>::max();
|
||||||
|
|
||||||
using BindlessID = std::uint32_t;
|
using BindlessID = std::uint32_t;
|
||||||
constexpr BindlessID NULL_BINDLESS_ID = std::numeric_limits<std::uint32_t>::max();
|
constexpr BindlessID NULL_BINDLESS_ID = std::numeric_limits<std::uint32_t>::max();
|
||||||
|
|||||||
@@ -6,59 +6,81 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
class InputManager {
|
#include <destrum/Singleton.h>
|
||||||
|
|
||||||
|
class InputManager: public Singleton<InputManager> {
|
||||||
public:
|
public:
|
||||||
// Call once at startup (optional, but convenient)
|
friend class Singleton<InputManager>;
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
// Call at the start of every frame
|
|
||||||
void BeginFrame();
|
void BeginFrame();
|
||||||
|
|
||||||
// Feed SDL events into this (call for each SDL_PollEvent)
|
|
||||||
void ProcessEvent(const SDL_Event& e);
|
void ProcessEvent(const SDL_Event& e);
|
||||||
|
|
||||||
// Call at end of frame if you want (not required)
|
|
||||||
void EndFrame() {}
|
void EndFrame() {}
|
||||||
|
|
||||||
// ---- Queries ----
|
bool IsKeyDown(SDL_Scancode sc) const;
|
||||||
bool IsKeyDown(SDL_Scancode sc) const; // held
|
bool WasKeyPressed(SDL_Scancode sc) const;
|
||||||
bool WasKeyPressed(SDL_Scancode sc) const; // pressed this frame
|
bool WasKeyReleased(SDL_Scancode sc) const;
|
||||||
bool WasKeyReleased(SDL_Scancode sc) const; // released this frame
|
|
||||||
|
|
||||||
bool IsMouseDown(Uint8 button) const; // held (SDL_BUTTON_LEFT etc.)
|
bool IsMouseDown(Uint8 button) const;
|
||||||
bool WasMousePressed(Uint8 button) const; // pressed this frame
|
bool WasMousePressed(Uint8 button) const;
|
||||||
bool WasMouseReleased(Uint8 button) const; // released this frame
|
bool WasMouseReleased(Uint8 button) const;
|
||||||
|
|
||||||
// Mouse position (window space)
|
|
||||||
int MouseX() const { return m_mouseX; }
|
int MouseX() const { return m_mouseX; }
|
||||||
int MouseY() const { return m_mouseY; }
|
int MouseY() const { return m_mouseY; }
|
||||||
int MouseDeltaX() const { return m_mouseDX; }
|
int MouseDeltaX() const { return m_mouseDX; }
|
||||||
int MouseDeltaY() const { return m_mouseDY; }
|
int MouseDeltaY() const { return m_mouseDY; }
|
||||||
|
|
||||||
// Mouse wheel (accumulated per frame)
|
|
||||||
int WheelX() const { return m_wheelX; }
|
int WheelX() const { return m_wheelX; }
|
||||||
int WheelY() const { return m_wheelY; }
|
int WheelY() const { return m_wheelY; }
|
||||||
|
|
||||||
// ---- Text input ----
|
|
||||||
void StartTextInput();
|
void StartTextInput();
|
||||||
void StopTextInput();
|
void StopTextInput();
|
||||||
bool IsTextInputActive() const { return m_textInputActive; }
|
bool IsTextInputActive() const { return m_textInputActive; }
|
||||||
const std::string& GetTextInput() const { return m_textInput; } // captured this frame
|
const std::string& GetTextInput() const { return m_textInput; }
|
||||||
|
|
||||||
// ---- Action mapping ----
|
bool IsPadButtonDown(SDL_JoystickID id, SDL_GameControllerButton btn) const;
|
||||||
enum class Device { Keyboard, MouseButton, MouseWheel };
|
bool WasPadButtonPressed(SDL_JoystickID id, SDL_GameControllerButton btn) const;
|
||||||
|
bool WasPadButtonReleased(SDL_JoystickID id, SDL_GameControllerButton btn) const;
|
||||||
|
|
||||||
|
// axis in [-1..1]
|
||||||
|
float GetPadAxis(SDL_JoystickID id, SDL_GameControllerAxis axis) const;
|
||||||
|
|
||||||
|
enum class Device { Keyboard, MouseButton, MouseWheel, GamepadButton, GamepadAxis };
|
||||||
enum class ButtonState { Down, Pressed, Released };
|
enum class ButtonState { Down, Pressed, Released };
|
||||||
|
|
||||||
struct Binding {
|
struct Binding {
|
||||||
Device device;
|
Device device;
|
||||||
int code; // SDL_Scancode for keyboard, Uint8 for mouse button, wheel axis sign encoding
|
|
||||||
// For MouseWheel: code = +1/-1 for Y, +2/-2 for X (simple encoding)
|
// Keyboard: code = SDL_Scancode
|
||||||
|
// MouseButton: code = Uint8
|
||||||
|
// MouseWheel: code = +1/-1 => Y, +2/-2 => X
|
||||||
|
//
|
||||||
|
// GamepadButton: code = (padIndex<<8) | button (padIndex: 0..255)
|
||||||
|
// GamepadAxis: code = (padIndex<<8) | axis (axis: SDL_GameControllerAxis)
|
||||||
|
int code = 0;
|
||||||
|
|
||||||
|
// For GamepadAxis "button-like" checks:
|
||||||
|
// sign: -1 means negative direction, +1 positive direction
|
||||||
|
// threshold: absolute value in [0..1] that must be exceeded
|
||||||
|
int sign = 0; // only used for GamepadAxis
|
||||||
|
float threshold = 0.5f; // only used for GamepadAxis
|
||||||
};
|
};
|
||||||
|
|
||||||
void BindAction(const std::string& action, const Binding& binding);
|
void BindAction(const std::string& action, const Binding& binding);
|
||||||
bool GetAction(const std::string& action, ButtonState state) const;
|
bool GetAction(const std::string& action, ButtonState state) const;
|
||||||
|
|
||||||
|
// Convenience helpers to build controller bindings
|
||||||
|
static Binding PadButton(uint8_t padIndex, SDL_GameControllerButton btn);
|
||||||
|
static Binding PadAxis(uint8_t padIndex, SDL_GameControllerAxis axis, int sign, float threshold = 0.5f);
|
||||||
|
|
||||||
|
// Which controller is "padIndex 0/1/2..."? (based on connection order)
|
||||||
|
SDL_JoystickID GetPadInstanceId(uint8_t padIndex) const;
|
||||||
|
|
||||||
|
void SetAxisDeadzone(int deadzone) { m_axisDeadzone = deadzone; } // 0..32767
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Key states
|
// Key states
|
||||||
std::unordered_set<SDL_Scancode> m_keysDown;
|
std::unordered_set<SDL_Scancode> m_keysDown;
|
||||||
@@ -75,18 +97,42 @@ private:
|
|||||||
int m_prevMouseX = 0, m_prevMouseY = 0;
|
int m_prevMouseX = 0, m_prevMouseY = 0;
|
||||||
int m_mouseDX = 0, m_mouseDY = 0;
|
int m_mouseDX = 0, m_mouseDY = 0;
|
||||||
|
|
||||||
// Wheel (per-frame)
|
|
||||||
int m_wheelX = 0, m_wheelY = 0;
|
int m_wheelX = 0, m_wheelY = 0;
|
||||||
|
|
||||||
// Text input (per-frame)
|
|
||||||
bool m_textInputActive = false;
|
bool m_textInputActive = false;
|
||||||
std::string m_textInput;
|
std::string m_textInput;
|
||||||
|
|
||||||
// Action bindings
|
|
||||||
std::unordered_map<std::string, std::vector<Binding>> m_bindings;
|
std::unordered_map<std::string, std::vector<Binding>> m_bindings;
|
||||||
|
|
||||||
|
struct PadState {
|
||||||
|
SDL_GameController* controller = nullptr;
|
||||||
|
SDL_JoystickID instanceId = -1;
|
||||||
|
|
||||||
|
std::unordered_set<uint8_t> buttonsDown;
|
||||||
|
std::unordered_set<uint8_t> buttonsPressed;
|
||||||
|
std::unordered_set<uint8_t> buttonsReleased;
|
||||||
|
|
||||||
|
// raw s16 axes (SDL gives Sint16)
|
||||||
|
std::array<Sint16, SDL_CONTROLLER_AXIS_MAX> axes{};
|
||||||
|
std::array<Sint16, SDL_CONTROLLER_AXIS_MAX> prevAxes{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map instanceId -> PadState
|
||||||
|
std::unordered_map<SDL_JoystickID, PadState> m_pads;
|
||||||
|
|
||||||
|
// padIndex -> instanceId (connection order list)
|
||||||
|
std::vector<SDL_JoystickID> m_padOrder;
|
||||||
|
|
||||||
|
int m_axisDeadzone = 8000; // typical deadzone
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool QueryBinding(const Binding& b, ButtonState state) const;
|
bool QueryBinding(const Binding& b, ButtonState state) const;
|
||||||
|
|
||||||
|
void AddController(int deviceIndex);
|
||||||
|
void RemoveController(SDL_JoystickID instanceId);
|
||||||
|
|
||||||
|
uint8_t GetPadIndexFromCode(int code) const { return static_cast<uint8_t>((code >> 8) & 0xFF); }
|
||||||
|
uint8_t GetLow8FromCode(int code) const { return static_cast<uint8_t>(code & 0xFF); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //INPUTMANAGER_H
|
#endif //INPUTMANAGER_H
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "glm/gtx/transform.hpp"
|
#include "glm/gtx/transform.hpp"
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
App::App(): renderer{meshCache} {
|
App::App(): renderer{meshCache, materialCache} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::init(const AppParams& params) {
|
void App::init(const AppParams& params) {
|
||||||
@@ -34,6 +34,7 @@ void App::init(const AppParams& params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gfxDevice.init(window, params.appName, false);
|
gfxDevice.init(window, params.appName, false);
|
||||||
|
materialCache.init(gfxDevice);
|
||||||
renderer.init(gfxDevice, params.renderSize);
|
renderer.init(gfxDevice, params.renderSize);
|
||||||
|
|
||||||
//Read whole file
|
//Read whole file
|
||||||
@@ -47,14 +48,22 @@ void App::init(const AppParams& params) {
|
|||||||
testMeshID = meshCache.addMesh(gfxDevice, testMesh);
|
testMeshID = meshCache.addMesh(gfxDevice, testMesh);
|
||||||
spdlog::info("TestMesh uploaded with id: {}", testMeshID);
|
spdlog::info("TestMesh uploaded with id: {}", testMeshID);
|
||||||
|
|
||||||
|
const auto testimgpath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png");
|
||||||
|
auto testimgID = gfxDevice.loadImageFromFile(testimgpath);
|
||||||
|
spdlog::info("Test image loaded with id: {}", testimgID);
|
||||||
|
testMaterialID = materialCache.addMaterial(gfxDevice, {
|
||||||
|
.baseColor = glm::vec3(1.f),
|
||||||
|
.diffuseTexture = testimgID,
|
||||||
|
});
|
||||||
|
spdlog::info("Test material created with id: {}", testMaterialID);
|
||||||
|
|
||||||
float aspectRatio = static_cast<float>(params.renderSize.x) / static_cast<float>(params.renderSize.y);
|
float aspectRatio = static_cast<float>(params.renderSize.x) / static_cast<float>(params.renderSize.y);
|
||||||
camera.setAspectRatio(aspectRatio);
|
camera.setAspectRatio(aspectRatio);
|
||||||
|
|
||||||
//Look 90 deg to the right
|
//Look 90 deg to the right
|
||||||
camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f)));
|
camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f)));
|
||||||
|
|
||||||
inputManager.Init();
|
InputManager::GetInstance().Init();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::run() {
|
void App::run() {
|
||||||
@@ -90,7 +99,7 @@ void App::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (accumulator >= dt) {
|
while (accumulator >= dt) {
|
||||||
inputManager.BeginFrame();
|
InputManager::GetInstance().BeginFrame();
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
if (event.type == SDL_QUIT) {
|
if (event.type == SDL_QUIT) {
|
||||||
@@ -106,39 +115,12 @@ void App::run() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inputManager.ProcessEvent(event);
|
InputManager::GetInstance().ProcessEvent(event);
|
||||||
|
|
||||||
if (inputManager.IsKeyDown(SDL_SCANCODE_W)) {
|
|
||||||
camera.m_position += camera.GetForward() * dt * 5.f;
|
|
||||||
}
|
|
||||||
if (inputManager.IsKeyDown(SDL_SCANCODE_S)) {
|
|
||||||
camera.m_position -= camera.GetForward() * dt * 5.f;
|
|
||||||
}
|
|
||||||
if (inputManager.IsKeyDown(SDL_SCANCODE_A)) {
|
|
||||||
camera.m_position -= camera.GetRight() * dt * 5.f;
|
|
||||||
}
|
|
||||||
if (inputManager.IsKeyDown(SDL_SCANCODE_D)) {
|
|
||||||
camera.m_position += camera.GetRight() * dt * 5.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// rotation
|
|
||||||
if (inputManager.IsKeyDown(SDL_SCANCODE_LEFT)) {
|
|
||||||
camera.SetRotation(camera.GetYaw() - glm::radians(90.f) * dt, camera.GetPitch());
|
|
||||||
}
|
|
||||||
if (inputManager.IsKeyDown(SDL_SCANCODE_RIGHT)) {
|
|
||||||
camera.SetRotation(camera.GetYaw() + glm::radians(90.f) * dt, camera.GetPitch());
|
|
||||||
}
|
|
||||||
if (inputManager.IsKeyDown(SDL_SCANCODE_UP)) {
|
|
||||||
camera.SetRotation(camera.GetYaw(), camera.GetPitch() + glm::radians(90.f) * dt);
|
|
||||||
}
|
|
||||||
if (inputManager.IsKeyDown(SDL_SCANCODE_DOWN)) {
|
|
||||||
camera.SetRotation(camera.GetYaw(), camera.GetPitch() - glm::radians(90.f) * dt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
camera.Update(dt);
|
camera.Update(dt);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gfxDevice.needsSwapchainRecreate()) {
|
if (gfxDevice.needsSwapchainRecreate()) {
|
||||||
spdlog::info("Recreating swapchain to size: {}x{}", m_params.windowSize.x, m_params.windowSize.y);
|
spdlog::info("Recreating swapchain to size: {}x{}", m_params.windowSize.x, m_params.windowSize.y);
|
||||||
gfxDevice.recreateSwapchain(m_params.windowSize.x, m_params.windowSize.y);
|
gfxDevice.recreateSwapchain(m_params.windowSize.x, m_params.windowSize.y);
|
||||||
@@ -154,7 +136,7 @@ void App::run() {
|
|||||||
glm::mat4 objMatrix = glm::mat4(1.f);
|
glm::mat4 objMatrix = glm::mat4(1.f);
|
||||||
objMatrix = glm::translate(objMatrix, glm::vec3(0.f, -3.0f, 0.f));
|
objMatrix = glm::translate(objMatrix, glm::vec3(0.f, -3.0f, 0.f));
|
||||||
renderer.beginDrawing(gfxDevice);
|
renderer.beginDrawing(gfxDevice);
|
||||||
renderer.drawMesh(testMeshID, glm::mat4(1.f), 0);
|
renderer.drawMesh(testMeshID, glm::mat4(1.f), testMaterialID);
|
||||||
renderer.drawMesh(testMeshID, objMatrix, 0);
|
renderer.drawMesh(testMeshID, objMatrix, 0);
|
||||||
renderer.endDrawing();
|
renderer.endDrawing();
|
||||||
|
|
||||||
|
|||||||
184
destrum/src/Graphics/BindlessSetManager.cpp
Normal file
184
destrum/src/Graphics/BindlessSetManager.cpp
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
#include <destrum/Graphics/BindlessSetManager.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <volk.h>
|
||||||
|
|
||||||
|
#include <destrum/Graphics/Util.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static const std::uint32_t maxBindlessResources = 16536;
|
||||||
|
static const std::uint32_t maxSamplers = 32;
|
||||||
|
|
||||||
|
static const std::uint32_t texturesBinding = 0;
|
||||||
|
static const std::uint32_t samplersBinding = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BindlessSetManager::init(VkDevice device, float maxAnisotropy)
|
||||||
|
{
|
||||||
|
{ // create pool
|
||||||
|
const auto poolSizesBindless = std::array<VkDescriptorPoolSize, 2>{{
|
||||||
|
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, maxBindlessResources},
|
||||||
|
{VK_DESCRIPTOR_TYPE_SAMPLER, maxSamplers},
|
||||||
|
}};
|
||||||
|
|
||||||
|
const auto poolInfo = VkDescriptorPoolCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||||
|
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT,
|
||||||
|
.maxSets = 10,
|
||||||
|
.poolSizeCount = (std::uint32_t)poolSizesBindless.size(),
|
||||||
|
.pPoolSizes = poolSizesBindless.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
VK_CHECK(vkCreateDescriptorPool(device, &poolInfo, nullptr, &descPool));
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // build desc set layout
|
||||||
|
const auto bindings = std::array<VkDescriptorSetLayoutBinding, 2>{{
|
||||||
|
{
|
||||||
|
.binding = texturesBinding,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
||||||
|
.descriptorCount = maxBindlessResources,
|
||||||
|
.stageFlags = VK_SHADER_STAGE_ALL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.binding = samplersBinding,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
|
||||||
|
.descriptorCount = maxSamplers,
|
||||||
|
.stageFlags = VK_SHADER_STAGE_ALL,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
const VkDescriptorBindingFlags bindlessFlags =
|
||||||
|
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT;
|
||||||
|
const auto bindingFlags = std::array{bindlessFlags, bindlessFlags};
|
||||||
|
|
||||||
|
const auto flagInfo = VkDescriptorSetLayoutBindingFlagsCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
||||||
|
.bindingCount = (std::uint32_t)bindingFlags.size(),
|
||||||
|
.pBindingFlags = bindingFlags.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto info = VkDescriptorSetLayoutCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||||
|
.pNext = &flagInfo,
|
||||||
|
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT,
|
||||||
|
.bindingCount = (std::uint32_t)bindings.size(),
|
||||||
|
.pBindings = bindings.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &descSetLayout));
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // alloc desc set
|
||||||
|
const auto allocInfo = VkDescriptorSetAllocateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||||
|
.descriptorPool = descPool,
|
||||||
|
.descriptorSetCount = 1,
|
||||||
|
.pSetLayouts = &descSetLayout,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::uint32_t maxBinding = maxBindlessResources - 1;
|
||||||
|
const auto countInfo = VkDescriptorSetVariableDescriptorCountAllocateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
|
||||||
|
.descriptorSetCount = 1,
|
||||||
|
.pDescriptorCounts = &maxBinding,
|
||||||
|
};
|
||||||
|
|
||||||
|
VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &descSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
initDefaultSamplers(device, maxAnisotropy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BindlessSetManager::initDefaultSamplers(VkDevice device, float maxAnisotropy)
|
||||||
|
{
|
||||||
|
// Keep in sync with bindless.glsl
|
||||||
|
static const std::uint32_t nearestSamplerId = 0;
|
||||||
|
static const std::uint32_t linearSamplerId = 1;
|
||||||
|
static const std::uint32_t shadowSamplerId = 2;
|
||||||
|
|
||||||
|
{ // init nearest sampler
|
||||||
|
const auto samplerCreateInfo = VkSamplerCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
|
.magFilter = VK_FILTER_NEAREST,
|
||||||
|
.minFilter = VK_FILTER_NEAREST,
|
||||||
|
};
|
||||||
|
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &nearestSampler));
|
||||||
|
vkutil::addDebugLabel(device, nearestSampler, "nearest");
|
||||||
|
addSampler(device, nearestSamplerId, nearestSampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // init linear sampler
|
||||||
|
const auto samplerCreateInfo = VkSamplerCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
|
.magFilter = VK_FILTER_LINEAR,
|
||||||
|
.minFilter = VK_FILTER_LINEAR,
|
||||||
|
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
||||||
|
// TODO: make possible to disable anisotropy or set other values?
|
||||||
|
.anisotropyEnable = VK_TRUE,
|
||||||
|
.maxAnisotropy = maxAnisotropy,
|
||||||
|
};
|
||||||
|
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &linearSampler));
|
||||||
|
vkutil::addDebugLabel(device, linearSampler, "linear");
|
||||||
|
addSampler(device, linearSamplerId, linearSampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // init shadow map sampler
|
||||||
|
const auto samplerCreateInfo = VkSamplerCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
|
.magFilter = VK_FILTER_LINEAR,
|
||||||
|
.minFilter = VK_FILTER_LINEAR,
|
||||||
|
.compareEnable = VK_TRUE,
|
||||||
|
.compareOp = VK_COMPARE_OP_GREATER_OR_EQUAL,
|
||||||
|
};
|
||||||
|
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &shadowMapSampler));
|
||||||
|
vkutil::addDebugLabel(device, shadowMapSampler, "shadow");
|
||||||
|
addSampler(device, shadowSamplerId, shadowMapSampler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BindlessSetManager::cleanup(VkDevice device)
|
||||||
|
{
|
||||||
|
vkDestroySampler(device, nearestSampler, nullptr);
|
||||||
|
vkDestroySampler(device, linearSampler, nullptr);
|
||||||
|
vkDestroySampler(device, shadowMapSampler, nullptr);
|
||||||
|
|
||||||
|
vkDestroyDescriptorSetLayout(device, descSetLayout, nullptr);
|
||||||
|
vkDestroyDescriptorPool(device, descPool, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BindlessSetManager::addImage(
|
||||||
|
const VkDevice device,
|
||||||
|
std::uint32_t id,
|
||||||
|
const VkImageView imageView)
|
||||||
|
{
|
||||||
|
const auto imageInfo = VkDescriptorImageInfo{
|
||||||
|
.imageView = imageView, .imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL};
|
||||||
|
const auto writeSet = VkWriteDescriptorSet{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
.dstSet = descSet,
|
||||||
|
.dstBinding = texturesBinding,
|
||||||
|
.dstArrayElement = id,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
||||||
|
.pImageInfo = &imageInfo,
|
||||||
|
};
|
||||||
|
vkUpdateDescriptorSets(device, 1, &writeSet, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BindlessSetManager::addSampler(const VkDevice device, std::uint32_t id, VkSampler sampler)
|
||||||
|
{
|
||||||
|
const auto imageInfo =
|
||||||
|
VkDescriptorImageInfo{.sampler = sampler, .imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL};
|
||||||
|
const auto writeSet = VkWriteDescriptorSet{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
.dstSet = descSet,
|
||||||
|
.dstBinding = samplersBinding,
|
||||||
|
.dstArrayElement = id,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
|
||||||
|
.pImageInfo = &imageInfo,
|
||||||
|
};
|
||||||
|
vkUpdateDescriptorSets(device, 1, &writeSet, 0, nullptr);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <destrum/Graphics/Camera.h>
|
#include <destrum/Graphics/Camera.h>
|
||||||
|
#include <destrum/Input/InputManager.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <glm/gtc/matrix_inverse.hpp>
|
#include <glm/gtc/matrix_inverse.hpp>
|
||||||
@@ -6,146 +7,112 @@
|
|||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
#include <glm/gtx/string_cast.hpp>
|
#include <glm/gtx/string_cast.hpp>
|
||||||
|
|
||||||
|
#include "glm/gtx/norm.hpp"
|
||||||
|
|
||||||
|
|
||||||
Camera::Camera(const glm::vec3& position, const glm::vec3& up): m_position{position}, m_up{up} {
|
Camera::Camera(const glm::vec3& position, const glm::vec3& up): m_position{position}, m_up{up} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::Update(float deltaTime) {
|
void Camera::Update(float deltaTime) {
|
||||||
// const auto MyWindow = static_cast<Window*>(glfwGetWindowUserPointer(Window::gWindow));
|
auto& input = InputManager::GetInstance();
|
||||||
//
|
|
||||||
// #pragma region Keyboard Movement
|
// --- tuning ---
|
||||||
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
|
float moveSpeed = m_movementSpeed;
|
||||||
//
|
if (input.IsKeyDown(SDL_SCANCODE_LSHIFT) || input.IsKeyDown(SDL_SCANCODE_RSHIFT)) {
|
||||||
// float MovementSpeed = m_movementSpeed;
|
moveSpeed *= 2.0f;
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) {
|
}
|
||||||
// MovementSpeed *= 2.0f;
|
|
||||||
// }
|
// Controller speed boost (pad0 LB)
|
||||||
//
|
const SDL_JoystickID pad0 = input.GetPadInstanceId(0);
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_W) == GLFW_PRESS) {
|
if (pad0 >= 0 && input.IsPadButtonDown(pad0, SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) {
|
||||||
// m_position += m_forward * deltaTime * MovementSpeed;
|
moveSpeed *= 3.0f;
|
||||||
// }
|
}
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_A) == GLFW_PRESS) {
|
|
||||||
// m_position -= m_right * deltaTime * MovementSpeed;
|
// Clamp pitch like your old code
|
||||||
// }
|
m_pitch = glm::clamp(m_pitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_S) == GLFW_PRESS) {
|
|
||||||
// m_position -= m_forward * deltaTime * MovementSpeed;
|
// =========================
|
||||||
// }
|
// Movement (Keyboard)
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_D) == GLFW_PRESS) {
|
// =========================
|
||||||
// m_position += m_right * deltaTime * MovementSpeed;
|
glm::vec3 move(0.0f);
|
||||||
// }
|
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_E) == GLFW_PRESS) {
|
if (input.IsKeyDown(SDL_SCANCODE_W)) move += m_forward;
|
||||||
// m_position += m_up * deltaTime * MovementSpeed;
|
if (input.IsKeyDown(SDL_SCANCODE_S)) move -= m_forward;
|
||||||
// }
|
if (input.IsKeyDown(SDL_SCANCODE_D)) move += m_right;
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_Q) == GLFW_PRESS) {
|
if (input.IsKeyDown(SDL_SCANCODE_A)) move -= m_right;
|
||||||
// m_position -= m_up * deltaTime * MovementSpeed;
|
|
||||||
// }
|
if (input.IsKeyDown(SDL_SCANCODE_Q)) move += m_up;
|
||||||
//
|
if (input.IsKeyDown(SDL_SCANCODE_E)) move -= m_up;
|
||||||
// // Looking around with arrow keys
|
|
||||||
// constexpr float lookSpeed = 1.0f * glm::radians(1.f);
|
if (glm::length2(move) > 0.0f) {
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_UP) == GLFW_PRESS) {
|
move = glm::normalize(move);
|
||||||
// totalPitch += lookSpeed;
|
m_position += move * (moveSpeed * deltaTime);
|
||||||
// }
|
}
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_DOWN) == GLFW_PRESS) {
|
|
||||||
// totalPitch -= lookSpeed;
|
// =========================
|
||||||
// }
|
// Movement (Controller)
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_LEFT) == GLFW_PRESS) {
|
// =========================
|
||||||
// totalYaw -= lookSpeed;
|
if (pad0 >= 0) {
|
||||||
// }
|
const float lx = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_LEFTX); // [-1..1]
|
||||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_RIGHT) == GLFW_PRESS) {
|
const float ly = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_LEFTY); // [-1..1]
|
||||||
// totalYaw += lookSpeed;
|
|
||||||
// }
|
// SDL Y is typically +down, so invert for "forward"
|
||||||
//
|
glm::vec3 padMove(0.0f);
|
||||||
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>(), glm::half_pi<float>());
|
padMove += m_forward * (-ly);
|
||||||
//
|
padMove += m_right * ( lx);
|
||||||
// const glm::mat4 yawMatrix = glm::rotate(glm::mat4(1.0f), -totalYaw, glm::vec3(0, 1, 0));
|
|
||||||
// const glm::mat4 pitchMatrix = glm::rotate(glm::mat4(1.0f), totalPitch, glm::vec3(0, 0, 1));
|
// Triggers for vertical movement (optional)
|
||||||
//
|
// SDL controller triggers are axes too: 0..1-ish after normalization in our helper, but signless.
|
||||||
// const glm::mat4 rotationMatrix = yawMatrix * pitchMatrix;
|
// With our NormalizeAxis, triggers will sit near 0 until pressed (depending on mapping).
|
||||||
//
|
// If your NormalizeAxis maps triggers weirdly, swap to raw event value approach.
|
||||||
// m_forward = glm::vec3(rotationMatrix * glm::vec4(1, 0, 0, 0));
|
const float lt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||||
// m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
|
const float rt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||||
// m_up = glm::normalize(glm::cross(m_right, m_forward));
|
const float vertical = (rt - lt);
|
||||||
// #pragma endregion
|
padMove += m_up * vertical;
|
||||||
//
|
|
||||||
// #pragma region Mouse Looking
|
if (glm::length2(padMove) > 0.0001f) {
|
||||||
// static bool wasCursorLockedLastFrame = false;
|
// do NOT normalize: preserve analog magnitude for smooth movement
|
||||||
// static double lastMouseX = 0.0;
|
m_position += padMove * (moveSpeed * deltaTime);
|
||||||
// static double lastMouseY = 0.0;
|
}
|
||||||
// static bool firstMouse = true;
|
}
|
||||||
//
|
|
||||||
// bool isLocked = MyWindow->isCursorLocked();
|
// =========================
|
||||||
// if (isLocked) {
|
// Look (Keyboard arrows only)
|
||||||
// double mouseX, mouseY;
|
// =========================
|
||||||
// glfwGetCursorPos(Window::gWindow, &mouseX, &mouseY);
|
// Use radians/sec so framerate-independent
|
||||||
//
|
const float keyLookSpeed = glm::radians(120.0f); // degrees per second
|
||||||
// // Reset mouse reference when locking the cursor (prevents jump)
|
|
||||||
// if (!wasCursorLockedLastFrame) {
|
if (input.IsKeyDown(SDL_SCANCODE_UP)) m_pitch += keyLookSpeed * deltaTime;
|
||||||
// lastMouseX = mouseX;
|
if (input.IsKeyDown(SDL_SCANCODE_DOWN)) m_pitch -= keyLookSpeed * deltaTime;
|
||||||
// lastMouseY = mouseY;
|
if (input.IsKeyDown(SDL_SCANCODE_LEFT)) m_yaw -= keyLookSpeed * deltaTime;
|
||||||
// firstMouse = false;
|
if (input.IsKeyDown(SDL_SCANCODE_RIGHT)) m_yaw += keyLookSpeed * deltaTime;
|
||||||
// }
|
|
||||||
//
|
// =========================
|
||||||
// float xOffset = static_cast<float>(mouseX - lastMouseX);
|
// Look (Controller right stick)
|
||||||
// float yOffset = static_cast<float>(lastMouseY - mouseY); // Reversed: y goes up
|
// =========================
|
||||||
//
|
if (pad0 >= 0) {
|
||||||
// lastMouseX = mouseX;
|
const float rx = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||||
// lastMouseY = mouseY;
|
const float ry = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||||
//
|
|
||||||
// constexpr float sensitivity = 0.002f;
|
const float padLookSpeed = 2.2f; // radians/sec at full deflection
|
||||||
// xOffset *= sensitivity;
|
m_yaw += rx * padLookSpeed * deltaTime;
|
||||||
// yOffset *= sensitivity;
|
m_pitch -= ry * padLookSpeed * deltaTime;
|
||||||
//
|
}
|
||||||
// totalYaw += xOffset;
|
|
||||||
// totalPitch += yOffset;
|
// Clamp pitch again after modifications
|
||||||
//
|
m_pitch = glm::clamp(m_pitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
|
||||||
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
|
|
||||||
//
|
// Recompute basis from yaw/pitch (same convention you used)
|
||||||
// m_frustum.update(m_projectionMatrix * m_viewMatrix);
|
const glm::mat4 yawMatrix = glm::rotate(glm::mat4(1.0f), -m_yaw, glm::vec3(0, 1, 0));
|
||||||
// }
|
const glm::mat4 pitchMatrix = glm::rotate(glm::mat4(1.0f), m_pitch, glm::vec3(0, 0, 1));
|
||||||
//
|
const glm::mat4 rotation = yawMatrix * pitchMatrix;
|
||||||
// wasCursorLockedLastFrame = isLocked;
|
|
||||||
// #pragma endregion
|
m_forward = glm::normalize(glm::vec3(rotation * glm::vec4(1, 0, 0, 0))); // +X forward
|
||||||
//
|
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
|
||||||
// #pragma region Controller Movement
|
m_up = glm::normalize(glm::cross(m_right, m_forward));
|
||||||
//
|
|
||||||
// if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1)) {
|
// keep target mode off when manually controlled
|
||||||
// GLFWgamepadstate state;
|
m_useTarget = false;
|
||||||
// if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1) && glfwGetGamepadState(GLFW_JOYSTICK_1, &state)) {
|
|
||||||
// float moveSpeed = MovementSpeed * deltaTime;
|
|
||||||
// const float rotSpeed = 1.5f * deltaTime;
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// //TODO: god knows what this is
|
|
||||||
// auto deadzone = [] (float value, float threshold = 0.1f) {
|
|
||||||
// if (fabs(value) < threshold)
|
|
||||||
// return 0.0f;
|
|
||||||
// const float sign = (value > 0) ? 1.0f : -1.0f;
|
|
||||||
// return sign * (fabs(value) - threshold) / (1.0f - threshold);
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// //LT is x2 speed
|
|
||||||
// const bool isLTPressed = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS;
|
|
||||||
// if (isLTPressed) {
|
|
||||||
// moveSpeed *= 3.0f;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const float lx = deadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]);
|
|
||||||
// const float ly = deadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
|
|
||||||
// const float rx = deadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
|
|
||||||
// const float ry = deadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
|
|
||||||
//
|
|
||||||
// m_position += m_forward * (-ly * moveSpeed);
|
|
||||||
// m_position += m_right * (lx * moveSpeed);
|
|
||||||
//
|
|
||||||
// const float lTrigger = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) / 2.0f;
|
|
||||||
// const float rTrigger = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) / 2.0f;
|
|
||||||
// const float vertical = (rTrigger - lTrigger);
|
|
||||||
// m_position += m_up * vertical * moveSpeed;
|
|
||||||
//
|
|
||||||
// totalYaw += rx * rotSpeed;
|
|
||||||
// totalPitch -= ry * rotSpeed;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// #pragma endregion
|
|
||||||
|
|
||||||
CalculateProjectionMatrix();
|
CalculateProjectionMatrix();
|
||||||
CalculateViewMatrix();
|
CalculateViewMatrix();
|
||||||
|
|||||||
@@ -99,6 +99,23 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync)
|
|||||||
swapchainFormat = VK_FORMAT_B8G8R8A8_SRGB;
|
swapchainFormat = VK_FORMAT_B8G8R8A8_SRGB;
|
||||||
swapchain.createSwapchain(this, swapchainFormat, w, h, vSync);
|
swapchain.createSwapchain(this, swapchainFormat, w, h, vSync);
|
||||||
|
|
||||||
|
VkPhysicalDeviceProperties props{};
|
||||||
|
vkGetPhysicalDeviceProperties(physicalDevice, &props);
|
||||||
|
|
||||||
|
imageCache.bindlessSetManager.init(device, props.limits.maxSamplerAnisotropy);
|
||||||
|
|
||||||
|
{ // create white texture
|
||||||
|
std::uint32_t pixel = 0xFFFFFFFF;
|
||||||
|
whiteImageId = createImage(
|
||||||
|
{
|
||||||
|
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||||
|
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||||
|
.extent = VkExtent3D{1, 1, 1},
|
||||||
|
},
|
||||||
|
"white texture",
|
||||||
|
&pixel);
|
||||||
|
}
|
||||||
|
|
||||||
swapchain.initSync(device);
|
swapchain.initSync(device);
|
||||||
|
|
||||||
const auto poolCreateInfo = vkinit::commandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamily);
|
const auto poolCreateInfo = vkinit::commandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamily);
|
||||||
@@ -223,6 +240,30 @@ void GfxDevice::waitIdle() {
|
|||||||
VK_CHECK(vkDeviceWaitIdle(device));
|
VK_CHECK(vkDeviceWaitIdle(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BindlessSetManager& GfxDevice::getBindlessSetManager() {
|
||||||
|
return imageCache.bindlessSetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDescriptorSetLayout GfxDevice::getBindlessDescSetLayout() const {
|
||||||
|
return imageCache.bindlessSetManager.getDescSetLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkDescriptorSet& GfxDevice::getBindlessDescSet() const {
|
||||||
|
return imageCache.bindlessSetManager.getDescSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GfxDevice::bindBindlessDescSet(VkCommandBuffer cmd, VkPipelineLayout layout) const {
|
||||||
|
vkCmdBindDescriptorSets(
|
||||||
|
cmd,
|
||||||
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
|
layout,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
&imageCache.bindlessSetManager.getDescSet(),
|
||||||
|
0,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const {
|
void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const {
|
||||||
executor.immediateSubmit(std::move(f));
|
executor.immediateSubmit(std::move(f));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ ImageID ImageCache::addImage(ImageID id, GPUImage image)
|
|||||||
} else {
|
} else {
|
||||||
images.push_back(std::move(image));
|
images.push_back(std::move(image));
|
||||||
}
|
}
|
||||||
// bindlessSetManager.addImage(gfxDevice.getDevice(), id, image.imageView);
|
bindlessSetManager.addImage(gfxDevice.getDevice(), id, image.imageView);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|||||||
77
destrum/src/Graphics/MaterialCache.cpp
Normal file
77
destrum/src/Graphics/MaterialCache.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include <destrum/Graphics/MaterialCache.h>
|
||||||
|
|
||||||
|
#include <destrum/Graphics/GfxDevice.h>
|
||||||
|
#include <destrum/Graphics/Util.h>
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
void MaterialCache::init(GfxDevice& gfxDevice)
|
||||||
|
{
|
||||||
|
materialDataBuffer = gfxDevice.createBuffer(
|
||||||
|
MAX_MATERIALS * sizeof(MaterialData),
|
||||||
|
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
|
||||||
|
vkutil::addDebugLabel(gfxDevice.getDevice(), materialDataBuffer.buffer, "material data");
|
||||||
|
|
||||||
|
{ // create default normal map texture
|
||||||
|
std::uint32_t normal = 0xFFFF8080; // (0.5, 0.5, 1.0, 1.0)
|
||||||
|
defaultNormalMapTextureID = gfxDevice.createImage(
|
||||||
|
{
|
||||||
|
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||||
|
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||||
|
.extent = VkExtent3D{1, 1, 1},
|
||||||
|
},
|
||||||
|
"normal map placeholder texture",
|
||||||
|
&normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
Material placeholderMaterial{.name = "PLACEHOLDER_MATERIAL"};
|
||||||
|
placeholderMaterialId = addMaterial(gfxDevice, placeholderMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialCache::cleanup(GfxDevice& gfxDevice)
|
||||||
|
{
|
||||||
|
gfxDevice.destroyBuffer(materialDataBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialID MaterialCache::addMaterial(GfxDevice& gfxDevice, Material material)
|
||||||
|
{
|
||||||
|
const auto getTextureOrElse = [](ImageID imageId, ImageID placeholder) {
|
||||||
|
spdlog::warn("Using placeholder texture for material given texture ID: {}", imageId);
|
||||||
|
return imageId != NULL_IMAGE_ID ? imageId : placeholder;
|
||||||
|
};
|
||||||
|
|
||||||
|
// store on GPU
|
||||||
|
MaterialData* data = (MaterialData*)materialDataBuffer.info.pMappedData;
|
||||||
|
auto whiteTextureID = gfxDevice.getWhiteTextureID();
|
||||||
|
auto id = getFreeMaterialId();
|
||||||
|
assert(id < MAX_MATERIALS);
|
||||||
|
data[id] = MaterialData{
|
||||||
|
.baseColor = glm::vec4(material.baseColor, 1.0f),
|
||||||
|
.metalRoughnessEmissive = glm::vec4{material.metallicFactor, material.roughnessFactor, material.emissiveFactor, 0.f},
|
||||||
|
.diffuseTex = getTextureOrElse(material.diffuseTexture, whiteTextureID),
|
||||||
|
.normalTex = whiteTextureID,
|
||||||
|
.metallicRoughnessTex = whiteTextureID,
|
||||||
|
.emissiveTex = whiteTextureID,
|
||||||
|
};
|
||||||
|
|
||||||
|
// store on CPU
|
||||||
|
materials.push_back(std::move(material));
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Material& MaterialCache::getMaterial(MaterialID id) const
|
||||||
|
{
|
||||||
|
return materials.at(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialID MaterialCache::getFreeMaterialId() const
|
||||||
|
{
|
||||||
|
return materials.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialID MaterialCache::getPlaceholderMaterialId() const
|
||||||
|
{
|
||||||
|
assert(placeholderMaterialId != NULL_MATERIAL_ID && "MaterialCache::init not called");
|
||||||
|
return placeholderMaterialId;
|
||||||
|
}
|
||||||
@@ -28,14 +28,14 @@ void MeshPipeline::init(GfxDevice& gfxDevice, VkFormat drawImageFormat, VkFormat
|
|||||||
};
|
};
|
||||||
|
|
||||||
const auto pushConstantRanges = std::array{bufferRange};
|
const auto pushConstantRanges = std::array{bufferRange};
|
||||||
// const auto layouts = std::array{gfxDevice.getBindlessDescSetLayout()};
|
const auto layouts = std::array{gfxDevice.getBindlessDescSetLayout()};
|
||||||
// m_pipelineLayout = vkutil::createPipelineLayout(device, layouts, pushConstantRanges);
|
// m_pipelineLayout = vkutil::createPipelineLayout(device, layouts, pushConstantRanges);
|
||||||
// vkutil::addDebugLabel(device, pipelineLayout, "mesh pipeline layout");
|
// vkutil::addDebugLabel(device, pipelineLayout, "mesh pipeline layout");
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||||
// pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
|
pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(layouts.size());
|
||||||
// pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data();
|
pipelineLayoutInfo.pSetLayouts = layouts.data();
|
||||||
|
|
||||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||||
pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data();
|
pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data();
|
||||||
@@ -72,9 +72,8 @@ void MeshPipeline::draw(VkCommandBuffer cmd,
|
|||||||
const GPUBuffer& sceneDataBuffer,
|
const GPUBuffer& sceneDataBuffer,
|
||||||
const std::vector<MeshDrawCommand>& drawCommands,
|
const std::vector<MeshDrawCommand>& drawCommands,
|
||||||
const std::vector<std::size_t>& sortedDrawCommands) {
|
const std::vector<std::size_t>& sortedDrawCommands) {
|
||||||
// vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
||||||
m_pipeline->bind(cmd);
|
m_pipeline->bind(cmd);
|
||||||
// gfxDevice.bindBindlessDescSet(cmd, pipelineLayout);
|
gfxDevice.bindBindlessDescSet(cmd, m_pipelineLayout);
|
||||||
|
|
||||||
const auto viewport = VkViewport{
|
const auto viewport = VkViewport{
|
||||||
.x = 0,
|
.x = 0,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
GameRenderer::GameRenderer(MeshCache& meshCache): meshCache{meshCache} {
|
GameRenderer::GameRenderer(MeshCache& meshCache, MaterialCache& matCache): meshCache{meshCache}, materialCache{matCache} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameRenderer::init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize) {
|
void GameRenderer::init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize) {
|
||||||
@@ -89,7 +89,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
void GameRenderer::cleanup() {
|
void GameRenderer::cleanup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialId materialId) {
|
void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId) {
|
||||||
const auto& mesh = meshCache.getMesh(id);
|
const auto& mesh = meshCache.getMesh(id);
|
||||||
// const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, false);
|
// const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, false);
|
||||||
assert(materialId != NULL_MATERIAL_ID);
|
assert(materialId != NULL_MATERIAL_ID);
|
||||||
|
|||||||
@@ -101,6 +101,16 @@ void vkutil::addDebugLabel(VkDevice device, VkBuffer buffer, const char* label)
|
|||||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vkutil::addDebugLabel(VkDevice device, VkSampler sampler, const char* label) {
|
||||||
|
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||||
|
.objectType = VK_OBJECT_TYPE_SAMPLER,
|
||||||
|
.objectHandle = (std::uint64_t)sampler,
|
||||||
|
.pObjectName = label,
|
||||||
|
};
|
||||||
|
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||||
|
}
|
||||||
|
|
||||||
vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params) {
|
vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params) {
|
||||||
assert(
|
assert(
|
||||||
(params.colorImageView || params.depthImageView != nullptr) &&
|
(params.colorImageView || params.depthImageView != nullptr) &&
|
||||||
|
|||||||
@@ -1,8 +1,38 @@
|
|||||||
#include <destrum/Input/InputManager.h>
|
#include <destrum/Input/InputManager.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
static float NormalizeAxis(Sint16 v, int deadzone) {
|
||||||
|
// Deadzone in raw units. Return [-1..1]
|
||||||
|
const int iv = (int)v;
|
||||||
|
|
||||||
|
if (std::abs(iv) <= deadzone) return 0.0f;
|
||||||
|
|
||||||
|
// Normalize to [-1..1] while preserving sign.
|
||||||
|
// SDL axis range is roughly [-32768..32767]
|
||||||
|
const float denom = (iv < 0) ? 32768.0f : 32767.0f;
|
||||||
|
float f = (float)iv / denom;
|
||||||
|
|
||||||
|
// Optional: re-scale so that value starts at 0 outside deadzone
|
||||||
|
// (nice for sticks; comment out if you prefer raw normalize)
|
||||||
|
const float dz = (float)deadzone / 32767.0f;
|
||||||
|
const float sign = (f < 0.0f) ? -1.0f : 1.0f;
|
||||||
|
const float af = std::abs(f);
|
||||||
|
const float scaled = (af - dz) / (1.0f - dz);
|
||||||
|
return sign * std::clamp(scaled, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
void InputManager::Init() {
|
void InputManager::Init() {
|
||||||
// Nothing mandatory here. You can also enable relative mouse mode, etc.
|
// Make sure gamecontroller subsystem is active (safe even if already)
|
||||||
// SDL_SetRelativeMouseMode(SDL_TRUE);
|
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||||
|
|
||||||
|
// Open any controllers already connected
|
||||||
|
const int num = SDL_NumJoysticks();
|
||||||
|
for (int i = 0; i < num; ++i) {
|
||||||
|
if (SDL_IsGameController(i)) {
|
||||||
|
AddController(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::BeginFrame() {
|
void InputManager::BeginFrame() {
|
||||||
@@ -21,74 +51,160 @@ void InputManager::BeginFrame() {
|
|||||||
m_mouseDY = m_mouseY - m_prevMouseY;
|
m_mouseDY = m_mouseY - m_prevMouseY;
|
||||||
m_prevMouseX = m_mouseX;
|
m_prevMouseX = m_mouseX;
|
||||||
m_prevMouseY = m_mouseY;
|
m_prevMouseY = m_mouseY;
|
||||||
|
|
||||||
|
// Clear controller "edge" sets + snapshot axes
|
||||||
|
for (auto& [id, pad]: m_pads) {
|
||||||
|
pad.buttonsPressed.clear();
|
||||||
|
pad.buttonsReleased.clear();
|
||||||
|
pad.prevAxes = pad.axes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputManager::AddController(int deviceIndex) {
|
||||||
|
if (!SDL_IsGameController(deviceIndex)) return;
|
||||||
|
|
||||||
|
SDL_GameController* gc = SDL_GameControllerOpen(deviceIndex);
|
||||||
|
if (!gc) return;
|
||||||
|
|
||||||
|
SDL_Joystick* joy = SDL_GameControllerGetJoystick(gc);
|
||||||
|
SDL_JoystickID instanceId = SDL_JoystickInstanceID(joy);
|
||||||
|
|
||||||
|
PadState ps;
|
||||||
|
ps.controller = gc;
|
||||||
|
ps.instanceId = instanceId;
|
||||||
|
ps.axes.fill(0);
|
||||||
|
ps.prevAxes.fill(0);
|
||||||
|
|
||||||
|
m_pads[instanceId] = ps;
|
||||||
|
|
||||||
|
// Track order (padIndex mapping)
|
||||||
|
m_padOrder.push_back(instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputManager::RemoveController(SDL_JoystickID instanceId) {
|
||||||
|
auto it = m_pads.find(instanceId);
|
||||||
|
if (it != m_pads.end()) {
|
||||||
|
if (it->second.controller) {
|
||||||
|
SDL_GameControllerClose(it->second.controller);
|
||||||
|
}
|
||||||
|
m_pads.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from order list
|
||||||
|
m_padOrder.erase(
|
||||||
|
std::remove(m_padOrder.begin(), m_padOrder.end(), instanceId),
|
||||||
|
m_padOrder.end()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::ProcessEvent(const SDL_Event& e) {
|
void InputManager::ProcessEvent(const SDL_Event& e) {
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case SDL_KEYDOWN: {
|
case SDL_KEYDOWN: {
|
||||||
if (e.key.repeat) break; // avoid repeat spam for "Pressed"
|
if (e.key.repeat) break;
|
||||||
SDL_Scancode sc = e.key.keysym.scancode;
|
SDL_Scancode sc = e.key.keysym.scancode;
|
||||||
m_keysDown.insert(sc);
|
m_keysDown.insert(sc);
|
||||||
m_keysPressed.insert(sc);
|
m_keysPressed.insert(sc);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SDL_KEYUP: {
|
case SDL_KEYUP: {
|
||||||
SDL_Scancode sc = e.key.keysym.scancode;
|
SDL_Scancode sc = e.key.keysym.scancode;
|
||||||
m_keysDown.erase(sc);
|
m_keysDown.erase(sc);
|
||||||
m_keysReleased.insert(sc);
|
m_keysReleased.insert(sc);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SDL_MOUSEBUTTONDOWN: {
|
case SDL_MOUSEBUTTONDOWN: {
|
||||||
Uint8 b = e.button.button;
|
Uint8 b = e.button.button;
|
||||||
m_mouseDown.insert(b);
|
m_mouseDown.insert(b);
|
||||||
m_mousePressed.insert(b);
|
m_mousePressed.insert(b);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SDL_MOUSEBUTTONUP: {
|
case SDL_MOUSEBUTTONUP: {
|
||||||
Uint8 b = e.button.button;
|
Uint8 b = e.button.button;
|
||||||
m_mouseDown.erase(b);
|
m_mouseDown.erase(b);
|
||||||
m_mouseReleased.insert(b);
|
m_mouseReleased.insert(b);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SDL_MOUSEMOTION: {
|
case SDL_MOUSEMOTION: {
|
||||||
m_mouseX = e.motion.x;
|
m_mouseX = e.motion.x;
|
||||||
m_mouseY = e.motion.y;
|
m_mouseY = e.motion.y;
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SDL_MOUSEWHEEL: {
|
case SDL_MOUSEWHEEL: {
|
||||||
// Accumulate wheel per frame
|
|
||||||
m_wheelX += e.wheel.x;
|
m_wheelX += e.wheel.x;
|
||||||
m_wheelY += e.wheel.y;
|
m_wheelY += e.wheel.y;
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SDL_TEXTINPUT: {
|
case SDL_TEXTINPUT: {
|
||||||
// text typed this frame
|
|
||||||
m_textInput += e.text.text;
|
m_textInput += e.text.text;
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ---- Controller hotplug ----
|
||||||
|
case SDL_CONTROLLERDEVICEADDED: {
|
||||||
|
// e.cdevice.which is the device index here
|
||||||
|
AddController(e.cdevice.which);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_CONTROLLERDEVICEREMOVED: {
|
||||||
|
// e.cdevice.which is instanceId here
|
||||||
|
RemoveController((SDL_JoystickID)e.cdevice.which);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ---- Controller buttons ----
|
||||||
|
case SDL_CONTROLLERBUTTONDOWN: {
|
||||||
|
SDL_JoystickID id = (SDL_JoystickID)e.cbutton.which;
|
||||||
|
auto it = m_pads.find(id);
|
||||||
|
if (it == m_pads.end()) break;
|
||||||
|
|
||||||
|
uint8_t btn = (uint8_t)e.cbutton.button;
|
||||||
|
it->second.buttonsDown.insert(btn);
|
||||||
|
it->second.buttonsPressed.insert(btn);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_CONTROLLERBUTTONUP: {
|
||||||
|
SDL_JoystickID id = (SDL_JoystickID)e.cbutton.which;
|
||||||
|
auto it = m_pads.find(id);
|
||||||
|
if (it == m_pads.end()) break;
|
||||||
|
|
||||||
|
uint8_t btn = (uint8_t)e.cbutton.button;
|
||||||
|
it->second.buttonsDown.erase(btn);
|
||||||
|
it->second.buttonsReleased.insert(btn);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ---- Controller axes ----
|
||||||
|
case SDL_CONTROLLERAXISMOTION: {
|
||||||
|
SDL_JoystickID id = (SDL_JoystickID)e.caxis.which;
|
||||||
|
auto it = m_pads.find(id);
|
||||||
|
if (it == m_pads.end()) break;
|
||||||
|
|
||||||
|
const int axis = (int)e.caxis.axis;
|
||||||
|
if (axis >= 0 && axis < SDL_CONTROLLER_AXIS_MAX) {
|
||||||
|
it->second.axes[(size_t)axis] = e.caxis.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputManager::IsKeyDown(SDL_Scancode sc) const {
|
bool InputManager::IsKeyDown(SDL_Scancode sc) const { return m_keysDown.count(sc) != 0; }
|
||||||
return m_keysDown.find(sc) != m_keysDown.end();
|
bool InputManager::WasKeyPressed(SDL_Scancode sc) const { return m_keysPressed.count(sc) != 0; }
|
||||||
}
|
bool InputManager::WasKeyReleased(SDL_Scancode sc) const { return m_keysReleased.count(sc) != 0; }
|
||||||
bool InputManager::WasKeyPressed(SDL_Scancode sc) const {
|
|
||||||
return m_keysPressed.find(sc) != m_keysPressed.end();
|
|
||||||
}
|
|
||||||
bool InputManager::WasKeyReleased(SDL_Scancode sc) const {
|
|
||||||
return m_keysReleased.find(sc) != m_keysReleased.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InputManager::IsMouseDown(Uint8 button) const {
|
bool InputManager::IsMouseDown(Uint8 button) const { return m_mouseDown.count(button) != 0; }
|
||||||
return m_mouseDown.find(button) != m_mouseDown.end();
|
bool InputManager::WasMousePressed(Uint8 button) const { return m_mousePressed.count(button) != 0; }
|
||||||
}
|
bool InputManager::WasMouseReleased(Uint8 button) const { return m_mouseReleased.count(button) != 0; }
|
||||||
bool InputManager::WasMousePressed(Uint8 button) const {
|
|
||||||
return m_mousePressed.find(button) != m_mousePressed.end();
|
|
||||||
}
|
|
||||||
bool InputManager::WasMouseReleased(Uint8 button) const {
|
|
||||||
return m_mouseReleased.find(button) != m_mouseReleased.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputManager::StartTextInput() {
|
void InputManager::StartTextInput() {
|
||||||
if (!m_textInputActive) {
|
if (!m_textInputActive) {
|
||||||
@@ -96,6 +212,7 @@ void InputManager::StartTextInput() {
|
|||||||
m_textInputActive = true;
|
m_textInputActive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::StopTextInput() {
|
void InputManager::StopTextInput() {
|
||||||
if (m_textInputActive) {
|
if (m_textInputActive) {
|
||||||
SDL_StopTextInput();
|
SDL_StopTextInput();
|
||||||
@@ -103,6 +220,56 @@ void InputManager::StopTextInput() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Controller direct queries ----
|
||||||
|
SDL_JoystickID InputManager::GetPadInstanceId(uint8_t padIndex) const {
|
||||||
|
if (padIndex >= m_padOrder.size()) return -1;
|
||||||
|
return m_padOrder[padIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputManager::IsPadButtonDown(SDL_JoystickID id, SDL_GameControllerButton btn) const {
|
||||||
|
auto it = m_pads.find(id);
|
||||||
|
if (it == m_pads.end()) return false;
|
||||||
|
return it->second.buttonsDown.count((uint8_t)btn) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputManager::WasPadButtonPressed(SDL_JoystickID id, SDL_GameControllerButton btn) const {
|
||||||
|
auto it = m_pads.find(id);
|
||||||
|
if (it == m_pads.end()) return false;
|
||||||
|
return it->second.buttonsPressed.count((uint8_t)btn) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputManager::WasPadButtonReleased(SDL_JoystickID id, SDL_GameControllerButton btn) const {
|
||||||
|
auto it = m_pads.find(id);
|
||||||
|
if (it == m_pads.end()) return false;
|
||||||
|
return it->second.buttonsReleased.count((uint8_t)btn) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float InputManager::GetPadAxis(SDL_JoystickID id, SDL_GameControllerAxis axis) const {
|
||||||
|
auto it = m_pads.find(id);
|
||||||
|
if (it == m_pads.end()) return 0.0f;
|
||||||
|
const int a = (int)axis;
|
||||||
|
if (a < 0 || a >= SDL_CONTROLLER_AXIS_MAX) return 0.0f;
|
||||||
|
return NormalizeAxis(it->second.axes[(size_t)a], m_axisDeadzone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Binding helpers ----
|
||||||
|
InputManager::Binding InputManager::PadButton(uint8_t padIndex, SDL_GameControllerButton btn) {
|
||||||
|
Binding b;
|
||||||
|
b.device = Device::GamepadButton;
|
||||||
|
b.code = ((int)padIndex << 8) | ((int)btn & 0xFF);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputManager::Binding InputManager::PadAxis(uint8_t padIndex, SDL_GameControllerAxis axis, int sign, float threshold) {
|
||||||
|
Binding b;
|
||||||
|
b.device = Device::GamepadAxis;
|
||||||
|
b.code = ((int)padIndex << 8) | ((int)axis & 0xFF);
|
||||||
|
b.sign = (sign < 0) ? -1 : +1;
|
||||||
|
b.threshold = threshold;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Action mapping ----
|
||||||
void InputManager::BindAction(const std::string& action, const Binding& binding) {
|
void InputManager::BindAction(const std::string& action, const Binding& binding) {
|
||||||
m_bindings[action].push_back(binding);
|
m_bindings[action].push_back(binding);
|
||||||
}
|
}
|
||||||
@@ -114,25 +281,64 @@ bool InputManager::QueryBinding(const Binding& b, ButtonState state) const {
|
|||||||
if (state == ButtonState::Down) return IsKeyDown(sc);
|
if (state == ButtonState::Down) return IsKeyDown(sc);
|
||||||
if (state == ButtonState::Pressed) return WasKeyPressed(sc);
|
if (state == ButtonState::Pressed) return WasKeyPressed(sc);
|
||||||
if (state == ButtonState::Released) return WasKeyReleased(sc);
|
if (state == ButtonState::Released) return WasKeyReleased(sc);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Device::MouseButton: {
|
case Device::MouseButton: {
|
||||||
auto mb = static_cast<Uint8>(b.code);
|
auto mb = static_cast<Uint8>(b.code);
|
||||||
if (state == ButtonState::Down) return IsMouseDown(mb);
|
if (state == ButtonState::Down) return IsMouseDown(mb);
|
||||||
if (state == ButtonState::Pressed) return WasMousePressed(mb);
|
if (state == ButtonState::Pressed) return WasMousePressed(mb);
|
||||||
if (state == ButtonState::Released) return WasMouseReleased(mb);
|
if (state == ButtonState::Released) return WasMouseReleased(mb);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Device::MouseWheel: {
|
case Device::MouseWheel: {
|
||||||
// Encoding:
|
|
||||||
// +1/-1 => wheel Y (up/down), +2/-2 => wheel X (right/left)
|
|
||||||
const int c = b.code;
|
const int c = b.code;
|
||||||
if (state != ButtonState::Pressed) return false; // wheel is "impulse"
|
if (state != ButtonState::Pressed) return false;
|
||||||
if (c == 1) return m_wheelY > 0;
|
if (c == 1) return m_wheelY > 0;
|
||||||
if (c == -1) return m_wheelY < 0;
|
if (c == -1) return m_wheelY < 0;
|
||||||
if (c == 2) return m_wheelX > 0;
|
if (c == 2) return m_wheelX > 0;
|
||||||
if (c == -2) return m_wheelX < 0;
|
if (c == -2) return m_wheelX < 0;
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Device::GamepadButton: {
|
||||||
|
const uint8_t padIndex = GetPadIndexFromCode(b.code);
|
||||||
|
const uint8_t btn = GetLow8FromCode(b.code);
|
||||||
|
|
||||||
|
const SDL_JoystickID id = GetPadInstanceId(padIndex);
|
||||||
|
if (id < 0) return false;
|
||||||
|
|
||||||
|
if (state == ButtonState::Down) return IsPadButtonDown(id, (SDL_GameControllerButton)btn);
|
||||||
|
if (state == ButtonState::Pressed) return WasPadButtonPressed(id, (SDL_GameControllerButton)btn);
|
||||||
|
if (state == ButtonState::Released) return WasPadButtonReleased(id, (SDL_GameControllerButton)btn);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Device::GamepadAxis: {
|
||||||
|
// Axis bindings are treated as "digital" by thresholding
|
||||||
|
const uint8_t padIndex = GetPadIndexFromCode(b.code);
|
||||||
|
const uint8_t axis = GetLow8FromCode(b.code);
|
||||||
|
|
||||||
|
const SDL_JoystickID id = GetPadInstanceId(padIndex);
|
||||||
|
if (id < 0) return false;
|
||||||
|
|
||||||
|
auto it = m_pads.find(id);
|
||||||
|
if (it == m_pads.end()) return false;
|
||||||
|
|
||||||
|
if (axis >= SDL_CONTROLLER_AXIS_MAX) return false;
|
||||||
|
|
||||||
|
const float cur = NormalizeAxis(it->second.axes[axis], m_axisDeadzone) * (float)b.sign;
|
||||||
|
const float prev = NormalizeAxis(it->second.prevAxes[axis], m_axisDeadzone) * (float)b.sign;
|
||||||
|
|
||||||
|
const bool curOn = cur >= b.threshold;
|
||||||
|
const bool prevOn = prev >= b.threshold;
|
||||||
|
|
||||||
|
if (state == ButtonState::Down) return curOn;
|
||||||
|
if (state == ButtonState::Pressed) return (curOn && !prevOn);
|
||||||
|
if (state == ButtonState::Released) return (!curOn && prevOn);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user