Add skybox and crude IBL approximation

This commit is contained in:
bailehuni 2024-03-19 11:32:51 +00:00
parent 1b0af6cd8b
commit 11641c6179
30 changed files with 1191 additions and 724 deletions

View File

@ -3,15 +3,13 @@
vulkan/device.cpp: report what is missing when a device fails to be found. vulkan/device.cpp: report what is missing when a device fails to be found.
Only select physical device 0 to simplify code. Only select physical device 0 to simplify code.
Supported different event 'kinds' (postPhysics, preRender, postRender etc) Add support for multiple lights.
TODO now: the collision system doesn't use the "isTrigger" bool properly. Add support for simple shadow mapping.
+ cascaded shadow mapping
Add support for shadows and other complex lighting. Also add post-processing. Support animations.
+ skinned meshes / morph targets
Add AABB colliders, sphere colliders, and mesh colliders.
Support animations and skinned meshes.
At some point, add game controller support. Make sure it works well with the At some point, add game controller support. Make sure it works well with the
InputManager class. InputManager class.
@ -38,3 +36,11 @@ Place all instances of a particular component in contiguous memory: I.e., a
scene holds many std::vectors, one for each type of component. These vectors are scene holds many std::vectors, one for each type of component. These vectors are
looped through every frame. This should optimise things by improving the memory looped through every frame. This should optimise things by improving the memory
layout of the program, significantly reducing cache misses. layout of the program, significantly reducing cache misses.
Implemented glTF file loader
Added a PBR shader with albedo, normal, metallic, roughness, and AO textures
Added the BVH AABB tree made in Summer to start a much better Collision system.
The CameraControllerSystem now uses raycasting to enable FPS-style player movement.

View File

@ -31,7 +31,8 @@ enum class MSAALevel {
}; };
struct GraphicsSettings { struct GraphicsSettings {
GraphicsSettings() { GraphicsSettings()
{
// sane defaults // sane defaults
enable_validation = true; enable_validation = true;
vsync = true; vsync = true;
@ -74,12 +75,7 @@ enum class Primitive {
kTriangleStrip, kTriangleStrip,
}; };
enum class CullMode { enum class CullMode { kCullNone, kCullFront, kCullBack, kCullFrontAndBack };
kCullNone,
kCullFront,
kCullBack,
kCullFrontAndBack
};
enum class VertexAttribFormat { kFloat2, kFloat3, kFloat4 }; enum class VertexAttribFormat { kFloat2, kFloat3, kFloat4 };
@ -88,6 +84,12 @@ enum class Filter : int {
kNearest, kNearest,
}; };
enum class WrapMode : int {
kRepeat,
kMirroredRepeat,
kClampToEdge,
};
enum class DescriptorType { enum class DescriptorType {
kUniformBuffer, kUniformBuffer,
kCombinedImageSampler, kCombinedImageSampler,
@ -102,9 +104,7 @@ typedef std::underlying_type<Bits>::type Flags;
} // namespace ShaderStageFlags } // namespace ShaderStageFlags
struct VertexAttribDescription { struct VertexAttribDescription {
VertexAttribDescription(uint32_t location, VertexAttribFormat format, VertexAttribDescription(uint32_t location, VertexAttribFormat format, uint32_t offset) : location(location), format(format), offset(offset) {}
uint32_t offset)
: location(location), format(format), offset(offset) {}
uint32_t location; // the index to use in the shader uint32_t location; // the index to use in the shader
VertexAttribFormat format; VertexAttribFormat format;
uint32_t offset; uint32_t offset;
@ -135,6 +135,9 @@ struct SamplerInfo {
Filter minify = gfx::Filter::kLinear; Filter minify = gfx::Filter::kLinear;
Filter magnify = gfx::Filter::kLinear; Filter magnify = gfx::Filter::kLinear;
Filter mipmap = gfx::Filter::kLinear; Filter mipmap = gfx::Filter::kLinear;
WrapMode wrap_u = gfx::WrapMode::kRepeat;
WrapMode wrap_v = gfx::WrapMode::kRepeat;
WrapMode wrap_w = gfx::WrapMode::kRepeat; // only useful for cubemaps AFAIK
bool anisotropic_filtering = true; // this can be force disabled by a global setting bool anisotropic_filtering = true; // this can be force disabled by a global setting
bool operator==(const SamplerInfo&) const = default; bool operator==(const SamplerInfo&) const = default;
@ -146,16 +149,20 @@ struct SamplerInfo {
namespace std { namespace std {
template <> template <>
struct hash<engine::gfx::SamplerInfo> { struct hash<engine::gfx::SamplerInfo> {
std::size_t operator()(const engine::gfx::SamplerInfo& k) const { std::size_t operator()(const engine::gfx::SamplerInfo& k) const
{
using std::hash; using std::hash;
size_t h1 = hash<int>()(static_cast<int>(k.minify)); size_t h1 = hash<int>()(static_cast<int>(k.minify));
size_t h2 = hash<int>()(static_cast<int>(k.magnify)); size_t h2 = hash<int>()(static_cast<int>(k.magnify));
size_t h3 = hash<int>()(static_cast<int>(k.mipmap)); size_t h3 = hash<int>()(static_cast<int>(k.mipmap));
size_t h4 = hash<bool>()(k.anisotropic_filtering); size_t h4 = hash<int>()(static_cast<int>(k.wrap_u));
size_t h5 = hash<int>()(static_cast<int>(k.wrap_v));
size_t h6 = hash<int>()(static_cast<int>(k.wrap_w));
size_t h7 = hash<bool>()(k.anisotropic_filtering);
return ((h1 & 0xFF) << 24) | ((h2 & 0xFF) << 16) | ((h3 & 0xFF) << 8) | return ((h1 & 0xFF) << 48) | ((h2 & 0xFF) << 40) | ((h3 & 0xFF) << 32) | ((h4 & 0xFF) << 24) | ((h5 & 0xFF) << 16) | ((h6 & 0xFF) << 8) |
((h4 & 0xFF) << 0); ((h7 & 0xFF) << 0);
} }
}; };

View File

@ -104,6 +104,8 @@ class GFXDevice {
gfx::Image* CreateImage(uint32_t w, uint32_t h, gfx::ImageFormat input_format, const void* image_data); gfx::Image* CreateImage(uint32_t w, uint32_t h, gfx::ImageFormat input_format, const void* image_data);
gfx::Image* CreateImageCubemap(uint32_t w, uint32_t h, gfx::ImageFormat input_format, const std::array<const void*, 6>& image_data);
void DestroyImage(const gfx::Image* image); void DestroyImage(const gfx::Image* image);
const gfx::Sampler* CreateSampler(const gfx::SamplerInfo& info); const gfx::Sampler* CreateSampler(const gfx::SamplerInfo& info);

View File

@ -58,7 +58,7 @@ class Renderer : private ApplicationComponent {
struct CameraSettings { struct CameraSettings {
float vertical_fov_radians = glm::radians(70.0f); float vertical_fov_radians = glm::radians(70.0f);
float clip_near = 0.5f; float clip_near = 0.1f;
float clip_far = 1000.0f; float clip_far = 1000.0f;
} camera_settings_; } camera_settings_;
@ -101,6 +101,11 @@ class Renderer : private ApplicationComponent {
// shader will take 2 clip space xyzw coords as push constants to define the line // shader will take 2 clip space xyzw coords as push constants to define the line
} debug_rendering_things_{}; } debug_rendering_things_{};
gfx::Image* skybox_cubemap = nullptr;
const gfx::Sampler* skybox_sampler = nullptr;
const gfx::Pipeline* skybox_pipeline = nullptr;
const gfx::Buffer* skybox_buffer = nullptr;
void DrawRenderList(gfx::DrawBuffer* draw_buffer, const RenderList& render_list); void DrawRenderList(gfx::DrawBuffer* draw_buffer, const RenderList& render_list);
}; };

View File

@ -19,8 +19,7 @@ class Material {
void SetAlbedoTexture(std::shared_ptr<Texture> texture); void SetAlbedoTexture(std::shared_ptr<Texture> texture);
void SetNormalTexture(std::shared_ptr<Texture> texture); void SetNormalTexture(std::shared_ptr<Texture> texture);
void SetOcclusionTexture(std::shared_ptr<Texture> texture); void SetOcclusionRoughnessMetallicTexture(std::shared_ptr<Texture> texture);
void SetMetallicRoughnessTexture(std::shared_ptr<Texture> texture);
const gfx::DescriptorSet* GetDescriptorSet() { return material_set_; } const gfx::DescriptorSet* GetDescriptorSet() { return material_set_; }
Shader* GetShader() { return shader_.get(); } Shader* GetShader() { return shader_.get(); }
@ -29,8 +28,7 @@ class Material {
const std::shared_ptr<Shader> shader_; const std::shared_ptr<Shader> shader_;
std::shared_ptr<Texture> texture_albedo_; std::shared_ptr<Texture> texture_albedo_;
std::shared_ptr<Texture> texture_normal_; std::shared_ptr<Texture> texture_normal_;
std::shared_ptr<Texture> texture_occlusion_; std::shared_ptr<Texture> texture_occlusion_roughness_metallic_;
std::shared_ptr<Texture> texture_metallic_roughness_;
const gfx::DescriptorSet* material_set_ = nullptr; const gfx::DescriptorSet* material_set_ = nullptr;

View File

@ -3,15 +3,19 @@
#define PI 3.1415926535897932384626433832795 #define PI 3.1415926535897932384626433832795
#define PI_INV 0.31830988618379067153776752674503 #define PI_INV 0.31830988618379067153776752674503
layout(set = 0, binding = 1) uniform samplerCube globalSetSkybox;
layout(set = 2, binding = 0) uniform sampler2D materialSetAlbedoSampler; layout(set = 2, binding = 0) uniform sampler2D materialSetAlbedoSampler;
layout(set = 2, binding = 1) uniform sampler2D materialSetNormalSampler; layout(set = 2, binding = 1) uniform sampler2D materialSetNormalSampler;
layout(set = 2, binding = 2) uniform sampler2D materialSetOcclusionSampler; layout(set = 2, binding = 2) uniform sampler2D materialSetOcclusionRoughnessMetallic;
layout(set = 2, binding = 3) uniform sampler2D materialSetMetallicRoughnessSampler;
layout(location = 0) in vec2 fragUV; layout(location = 0) in vec2 fragUV;
layout(location = 1) in vec3 fragPosTangentSpace; layout(location = 1) in vec3 fragPosTangentSpace;
layout(location = 2) in vec3 fragViewPosTangentSpace; layout(location = 2) in vec3 fragViewPosTangentSpace;
layout(location = 3) in vec3 fragLightPosTangentSpace; layout(location = 3) in vec3 fragLightPosTangentSpace;
layout(location = 4) in vec3 fragNormWorldSpace;
layout(location = 5) in vec3 fragViewPosWorldSpace;
layout(location = 6) in vec3 fragPosWorldSpace;
layout(location = 0) out vec4 outColor; layout(location = 0) out vec4 outColor;
@ -28,21 +32,24 @@ float GGXDist(float alpha_2, float N_dot_H) {
void main() { void main() {
const vec3 metallic_roughness = vec3(texture(materialSetMetallicRoughnessSampler, fragUV)); const vec3 occlusion_roughness_metallic = vec3(texture(materialSetOcclusionRoughnessMetallic, fragUV));
const float metallic = metallic_roughness.b; const float ao = occlusion_roughness_metallic.r;
const float roughness = metallic_roughness.g; // roughness of zero is completely black? const float roughness = occlusion_roughness_metallic.g;
const float metallic = occlusion_roughness_metallic.b;
const vec3 I = normalize(fragPosWorldSpace - fragViewPosWorldSpace);
const vec3 R = reflect(I, fragNormWorldSpace);
const float roughness_2 = roughness * roughness; const float roughness_2 = roughness * roughness;
const vec3 light_colour = vec3(1.0, 1.0, 1.0) * 2.4; const vec3 light_colour = vec3(1.0, 1.0, 1.0) * 2.4;
const vec3 emission = vec3(0.0, 0.0, 0.0); const vec3 emission = vec3(0.0, 0.0, 0.0);
const float ao = texture(materialSetOcclusionSampler, fragUV).r;
const vec3 albedo = vec3(texture(materialSetAlbedoSampler, fragUV)); const vec3 albedo = vec3(texture(materialSetAlbedoSampler, fragUV));
const vec3 N = GetNormal(); const vec3 N = GetNormal();
const vec3 V = normalize(fragViewPosTangentSpace - fragPosTangentSpace); const vec3 V = normalize(fragViewPosTangentSpace - fragPosTangentSpace);
const vec3 L = normalize(fragLightPosTangentSpace - fragPosTangentSpace); const vec3 L = normalize(fragLightPosTangentSpace);
//const vec3 L = normalize(vec3(5.0, 0.0, 3.0)); //const vec3 L = normalize(vec3(5.0, 0.0, 3.0));
const vec3 H = normalize(V + L); const vec3 H = normalize(V + L);
@ -69,7 +76,10 @@ void main() {
const vec3 brdf = mix(dielectric_brdf, metal_brdf, metallic); const vec3 brdf = mix(dielectric_brdf, metal_brdf, metallic);
const vec3 lighting = brdf * light_colour * L_dot_N; vec3 lighting = brdf * light_colour * L_dot_N;
vec3 ambient_light = vec3(0.09082, 0.13281, 0.18164);
lighting += mix(ambient_light, texture(globalSetSkybox, R).rgb, metallic) * ao * diffuse_brdf; // this is NOT physically-based, it just looks cool
outColor = vec4(min(emission + lighting, 1.0), 1.0); outColor = vec4(min(emission + lighting, 1.0), 1.0);
} }

View File

@ -21,6 +21,9 @@ layout(location = 0) out vec2 fragUV;
layout(location = 1) out vec3 fragPosTangentSpace; layout(location = 1) out vec3 fragPosTangentSpace;
layout(location = 2) out vec3 fragViewPosTangentSpace; layout(location = 2) out vec3 fragViewPosTangentSpace;
layout(location = 3) out vec3 fragLightPosTangentSpace; layout(location = 3) out vec3 fragLightPosTangentSpace;
layout(location = 4) out vec3 fragNormWorldSpace;
layout(location = 5) out vec3 fragViewPosWorldSpace;
layout(location = 6) out vec3 fragPosWorldSpace;
void main() { void main() {
vec4 worldPosition = constants.model * vec4(inPosition, 1.0); vec4 worldPosition = constants.model * vec4(inPosition, 1.0);
@ -34,7 +37,11 @@ void main() {
fragUV = inUV; fragUV = inUV;
fragPosTangentSpace = worldToTangentSpace * vec3(worldPosition); fragPosTangentSpace = worldToTangentSpace * vec3(worldPosition);
fragViewPosTangentSpace = worldToTangentSpace * vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0)); fragViewPosTangentSpace = worldToTangentSpace * vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0));
fragLightPosTangentSpace = worldToTangentSpace * vec3(10000.0, 0000.0, 20000.0); fragLightPosTangentSpace = worldToTangentSpace * vec3(-0.4278,0.7923,0.43502);
fragNormWorldSpace = N;
fragViewPosWorldSpace = vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0));
fragPosWorldSpace = worldPosition.xyz;
gl_Position.y *= -1.0; gl_Position.y *= -1.0;
} }

View File

@ -1,14 +1,11 @@
#version 450 #version 450
layout(set = 2, binding = 0) uniform sampler2D materialSetAlbedoSampler; layout(set = 0, binding = 1) uniform samplerCube cubeSampler;
layout(set = 2, binding = 1) uniform sampler2D materialSetNormalSampler;
layout(set = 2, binding = 2) uniform sampler2D materialSetOcclusionSampler;
layout(set = 2, binding = 3) uniform sampler2D materialSetMetallicRoughnessSampler;
layout(location = 0) in vec2 fragUV; layout(location = 0) in vec3 fragPosition;
layout(location = 0) out vec4 outColor; layout(location = 0) out vec4 outColor;
void main() { void main() {
outColor = texture(materialSetAlbedoSampler, fragUV); outColor = texture(cubeSampler, fragPosition);
} }

View File

@ -8,20 +8,13 @@ layout(set = 1, binding = 0) uniform FrameSetUniformBuffer {
mat4 view; mat4 view;
} frameSetUniformBuffer; } frameSetUniformBuffer;
layout( push_constant ) uniform Constants {
mat4 model;
} constants;
layout(location = 0) in vec3 inPosition; layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNorm;
layout(location = 2) in vec4 inTangent;
layout(location = 3) in vec2 inUV;
layout(location = 0) out vec2 fragUV; layout(location = 0) out vec3 fragPosition;
void main() { void main() {
vec3 position = mat3(frameSetUniformBuffer.view) * vec3(constants.model * vec4(inPosition, 1.0)); fragPosition = inPosition;
gl_Position = (globalSetUniformBuffer.proj * vec4(position, 0.0)).xyzz; gl_Position = (globalSetUniformBuffer.proj * vec4(mat3(frameSetUniformBuffer.view) * inPosition, 0.0)).xyzz;
fragUV = inUV;
gl_Position.y *= -1.0; gl_Position.y *= -1.0;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 B

View File

@ -120,21 +120,6 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph
std::make_unique<Shader>(renderer(), GetResourcePath("engine/shaders/fancy.vert"), GetResourcePath("engine/shaders/fancy.frag"), shaderSettings); std::make_unique<Shader>(renderer(), GetResourcePath("engine/shaders/fancy.vert"), GetResourcePath("engine/shaders/fancy.frag"), shaderSettings);
GetResourceManager<Shader>()->AddPersistent("builtin.fancy", std::move(fancyShader)); GetResourceManager<Shader>()->AddPersistent("builtin.fancy", std::move(fancyShader));
} }
{
Shader::VertexParams vertParams{};
vertParams.has_normal = true;
vertParams.has_tangent = true;
vertParams.has_uv0 = true;
Shader::ShaderSettings shaderSettings{};
shaderSettings.vertexParams = vertParams;
shaderSettings.alpha_blending = false;
shaderSettings.cull_backface = true;
shaderSettings.write_z = false;
shaderSettings.render_order = 1;
auto skyboxShader =
std::make_unique<Shader>(renderer(), GetResourcePath("engine/shaders/skybox.vert"), GetResourcePath("engine/shaders/skybox.frag"), shaderSettings);
GetResourceManager<Shader>()->AddPersistent("builtin.skybox", std::move(skyboxShader));
}
/* default textures */ /* default textures */
{ {
@ -208,7 +193,8 @@ void Application::GameLoop()
struct DebugMenuState { struct DebugMenuState {
bool menu_active = false; bool menu_active = false;
bool show_aabbs = false; bool show_entity_boxes = false;
bool show_bounding_volumes = false;
bool show_info_window = false; bool show_info_window = false;
} debug_menu_state; } debug_menu_state;
@ -246,7 +232,8 @@ void Application::GameLoop()
if (ImGui::Begin("debugMenu", 0)) { if (ImGui::Begin("debugMenu", 0)) {
ImGui::Text("Test!"); ImGui::Text("Test!");
ImGui::Text("FPS: %.3f", std::roundf(avg_fps)); ImGui::Text("FPS: %.3f", std::roundf(avg_fps));
ImGui::Checkbox("Show AABBs?", &debug_menu_state.show_aabbs); ImGui::Checkbox("Show entity hitboxes?", &debug_menu_state.show_entity_boxes);
ImGui::Checkbox("Show bounding volumes?", &debug_menu_state.show_bounding_volumes);
} }
ImGui::End(); ImGui::End();
} }
@ -288,7 +275,7 @@ void Application::GameLoop()
const RenderList* dynamic_list = nullptr; const RenderList* dynamic_list = nullptr;
glm::mat4 camera_transform{1.0f}; glm::mat4 camera_transform{1.0f};
if (scene) { if (scene) {
if (debug_menu_state.show_aabbs) { if (debug_menu_state.show_entity_boxes) {
if (CollisionSystem* colsys = scene->GetSystem<CollisionSystem>()) { if (CollisionSystem* colsys = scene->GetSystem<CollisionSystem>()) {
for (const auto& node : colsys->bvh_) { for (const auto& node : colsys->bvh_) {
if (node.type1 == CollisionSystem::BiTreeNode::Type::Entity) { if (node.type1 == CollisionSystem::BiTreeNode::Type::Entity) {
@ -354,6 +341,72 @@ void Application::GameLoop()
} }
} }
} }
if (debug_menu_state.show_bounding_volumes) {
if (CollisionSystem* colsys = scene->GetSystem<CollisionSystem>()) {
for (const auto& node : colsys->bvh_) {
if (node.type1 == CollisionSystem::BiTreeNode::Type::BoundingVolume) {
const glm::vec3 col =
(node.type1 == CollisionSystem::BiTreeNode::Type::BoundingVolume) ? glm::vec3{ 1.0f, 0.0f, 0.0f } : glm::vec3{ 0.0f, 1.0f, 0.0f };
Line line1{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, col };
debug_lines.push_back(line1);
Line line2{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, col };
debug_lines.push_back(line2);
Line line3{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, col };
debug_lines.push_back(line3);
Line line4{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, col };
debug_lines.push_back(line4);
Line line5{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, col };
debug_lines.push_back(line5);
Line line6{ glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col };
debug_lines.push_back(line6);
Line line7{ glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col };
debug_lines.push_back(line7);
Line line8{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, col };
debug_lines.push_back(line8);
Line line9{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col };
debug_lines.push_back(line9);
Line line10{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col };
debug_lines.push_back(line10);
Line line11{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col };
debug_lines.push_back(line11);
Line line12{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col };
debug_lines.push_back(line12);
}
if (node.type2 == CollisionSystem::BiTreeNode::Type::BoundingVolume) {
const glm::vec3 col =
(node.type2 == CollisionSystem::BiTreeNode::Type::BoundingVolume) ? glm::vec3{ 1.0f, 0.0f, 0.0f } : glm::vec3{ 0.0f, 1.0f, 0.0f };
Line line1{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, col };
debug_lines.push_back(line1);
Line line2{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, col };
debug_lines.push_back(line2);
Line line3{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, col };
debug_lines.push_back(line3);
Line line4{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, col };
debug_lines.push_back(line4);
Line line5{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, col };
debug_lines.push_back(line5);
Line line6{ glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col };
debug_lines.push_back(line6);
Line line7{ glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col };
debug_lines.push_back(line7);
Line line8{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, col };
debug_lines.push_back(line8);
Line line9{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col };
debug_lines.push_back(line9);
Line line10{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col };
debug_lines.push_back(line10);
Line line11{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col };
debug_lines.push_back(line11);
Line line12{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col };
debug_lines.push_back(line12);
}
}
}
}
camera_transform = scene->GetComponent<TransformComponent>(scene->GetEntity("camera"))->world_matrix; camera_transform = scene->GetComponent<TransformComponent>(scene->GetEntity("camera"))->world_matrix;
auto mesh_render_system = scene->GetSystem<MeshRenderSystem>(); auto mesh_render_system = scene->GetSystem<MeshRenderSystem>();
static_list = mesh_render_system->GetStaticRenderList(); static_list = mesh_render_system->GetStaticRenderList();

View File

@ -173,6 +173,19 @@ static VkBufferUsageFlagBits getBufferUsageFlag(gfx::BufferType type)
throw std::runtime_error("Unknown filter"); throw std::runtime_error("Unknown filter");
} }
[[maybe_unused]] static VkSamplerAddressMode getSamplerAddressMode(gfx::WrapMode mode)
{
switch (mode) {
case gfx::WrapMode::kRepeat:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case gfx::WrapMode::kMirroredRepeat:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case gfx::WrapMode::kClampToEdge:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
}
throw std::runtime_error("Unknown wrap mode");
}
[[maybe_unused]] static VkSamplerMipmapMode getSamplerMipmapMode(gfx::Filter filter) [[maybe_unused]] static VkSamplerMipmapMode getSamplerMipmapMode(gfx::Filter filter)
{ {
switch (filter) { switch (filter) {
@ -1377,7 +1390,7 @@ void GFXDevice::DestroyBuffer(const gfx::Buffer* buffer)
delete buffer; delete buffer;
} }
// imageData must have pixel format R8G8B8A8_SRGB // imageData must have pixel format R8G8B8A8
gfx::Image* GFXDevice::CreateImage(uint32_t w, uint32_t h, gfx::ImageFormat input_format, const void* imageData) gfx::Image* GFXDevice::CreateImage(uint32_t w, uint32_t h, gfx::ImageFormat input_format, const void* imageData)
{ {
assert(imageData != nullptr); assert(imageData != nullptr);
@ -1624,6 +1637,263 @@ gfx::Image* GFXDevice::CreateImage(uint32_t w, uint32_t h, gfx::ImageFormat inpu
return out; return out;
} }
gfx::Image* GFXDevice::CreateImageCubemap(uint32_t w, uint32_t h, gfx::ImageFormat input_format, const std::array<const void*, 6>& image_data)
{
assert(image_data[0] != nullptr);
assert(image_data[1] != nullptr);
assert(image_data[2] != nullptr);
assert(image_data[3] != nullptr);
assert(image_data[4] != nullptr);
assert(image_data[5] != nullptr);
if (pimpl->FRAMECOUNT != 0) abort(); // TODO. This is annoying
gfx::Image* out = new gfx::Image{};
uint32_t mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(w, h)))) + 1;
VkFormat imageFormat = converters::getImageFormat(input_format);
VkBuffer stagingBuffer = VK_NULL_HANDLE;
VmaAllocation stagingAllocation = VK_NULL_HANDLE;
const VkDeviceSize stagingBufferSizePerSide = (VkDeviceSize)w * (VkDeviceSize)h * 4;
/* create staging buffer */
{
VkBufferCreateInfo stagingBufferInfo{};
stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
stagingBufferInfo.size = stagingBufferSizePerSide * 6;
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
stagingBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
stagingBufferInfo.flags = 0;
VmaAllocationCreateInfo stagingAllocInfo{};
stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO;
stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
stagingAllocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VKCHECK(vmaCreateBuffer(pimpl->allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr));
void* dataDest;
VKCHECK(vmaMapMemory(pimpl->allocator, stagingAllocation, &dataDest));
for (size_t i = 0; i < 6; ++i) {
memcpy(reinterpret_cast<std::byte*>(dataDest) + stagingBufferSizePerSide * i, image_data[i], stagingBufferSizePerSide);
}
vmaUnmapMemory(pimpl->allocator, stagingAllocation);
}
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.format = imageFormat;
imageInfo.extent.width = w;
imageInfo.extent.height = h;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = mipLevels;
imageInfo.arrayLayers = 6;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.flags = 0;
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
allocCreateInfo.priority = 0.5f;
VKCHECK(vmaCreateImage(pimpl->allocator, &imageInfo, &allocCreateInfo, &out->image, &out->allocation, nullptr));
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = out->image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
viewInfo.format = imageFormat;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = mipLevels;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 6;
VKCHECK(vkCreateImageView(pimpl->device.device, &viewInfo, nullptr, &out->view));
/* begin command buffer */
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = pimpl->graphicsCommandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
VKCHECK(vkAllocateCommandBuffers(pimpl->device.device, &allocInfo, &commandBuffer));
{ // record the command buffer
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VKCHECK(vkBeginCommandBuffer(commandBuffer, &beginInfo));
// barrier: (all mip levels): UNDEFINED -> TRANSFER_DST_OPTIMAL
// Used for copying staging buffer AND blitting mipmaps
// Must happen before vkCmdCopyBufferToImage performs a TRANSFER_WRITE in
// the COPY stage.
VkImageMemoryBarrier2 beforeCopyBarrier{};
beforeCopyBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
beforeCopyBarrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE;
beforeCopyBarrier.srcAccessMask = VK_ACCESS_2_NONE;
beforeCopyBarrier.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT;
beforeCopyBarrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
beforeCopyBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
beforeCopyBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
beforeCopyBarrier.srcQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
beforeCopyBarrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
beforeCopyBarrier.image = out->image;
beforeCopyBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
beforeCopyBarrier.subresourceRange.baseMipLevel = 0;
beforeCopyBarrier.subresourceRange.levelCount = mipLevels;
beforeCopyBarrier.subresourceRange.baseArrayLayer = 0;
beforeCopyBarrier.subresourceRange.layerCount = 6;
VkDependencyInfo beforeCopyDependency{};
beforeCopyDependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
beforeCopyDependency.imageMemoryBarrierCount = 1;
beforeCopyDependency.pImageMemoryBarriers = &beforeCopyBarrier;
vkCmdPipelineBarrier2(commandBuffer, &beforeCopyDependency);
for (int face = 0; face < 6; ++face) {
// copy staging buffer to mipLevel 0 (full res image)
VkBufferImageCopy region{};
region.bufferOffset = stagingBufferSizePerSide * face;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = face;
region.imageSubresource.layerCount = 1;
region.imageOffset.x = 0;
region.imageOffset.y = 0;
region.imageOffset.z = 0;
region.imageExtent = imageInfo.extent;
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, out->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
int32_t mipWidth = w;
int32_t mipHeight = h;
for (uint32_t i = 1; i < mipLevels; i++) {
// barrier: (i - 1) TRANSFER_DST_OPTIMAL -> TRANSFER_SRC_OPTIMAL
// Must happen after TRANSFER_WRITE in the COPY stage and BLIT stage.
VkImageMemoryBarrier2 beforeBlitBarrier{};
beforeBlitBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
beforeBlitBarrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT | VK_PIPELINE_STAGE_2_BLIT_BIT;
beforeBlitBarrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
beforeBlitBarrier.dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT;
beforeBlitBarrier.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT;
beforeBlitBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
beforeBlitBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
beforeBlitBarrier.srcQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
beforeBlitBarrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
beforeBlitBarrier.image = out->image;
beforeBlitBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
beforeBlitBarrier.subresourceRange.baseMipLevel = (i - 1);
beforeBlitBarrier.subresourceRange.levelCount = 1;
beforeBlitBarrier.subresourceRange.baseArrayLayer = face;
beforeBlitBarrier.subresourceRange.layerCount = 1;
VkDependencyInfo beforeBlitDependency{};
beforeBlitDependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
beforeBlitDependency.imageMemoryBarrierCount = 1;
beforeBlitDependency.pImageMemoryBarriers = &beforeBlitBarrier;
vkCmdPipelineBarrier2(commandBuffer, &beforeBlitDependency);
VkImageBlit blit{};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = i - 1;
blit.srcSubresource.baseArrayLayer = face;
blit.srcSubresource.layerCount = 1;
blit.srcOffsets[0] = { 0, 0, 0 };
blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = i;
blit.dstSubresource.baseArrayLayer = face;
blit.dstSubresource.layerCount = 1;
blit.dstOffsets[0] = { 0, 0, 0 };
blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
vkCmdBlitImage(commandBuffer, out->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, out->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
VK_FILTER_LINEAR);
// barrier: (i - 1) TRANSFER_SRC_OPTIMAL -> SHADER_READ_ONLY_OPTIMALs
// Must happen after usage in the BLIT stage.
// Must happen before SHADER_SAMPLED_READ in the FRAGMENT_SHADER stage
VkImageMemoryBarrier2 afterBlitBarrier{};
afterBlitBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
afterBlitBarrier.srcStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT;
afterBlitBarrier.srcAccessMask = 0;
afterBlitBarrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
afterBlitBarrier.dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_2_SHADER_READ_BIT;
afterBlitBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
afterBlitBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
afterBlitBarrier.srcQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
afterBlitBarrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
afterBlitBarrier.image = out->image;
afterBlitBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
afterBlitBarrier.subresourceRange.baseMipLevel = (i - 1);
afterBlitBarrier.subresourceRange.levelCount = 1;
afterBlitBarrier.subresourceRange.baseArrayLayer = face;
afterBlitBarrier.subresourceRange.layerCount = 1;
VkDependencyInfo afterBlitDependency{};
afterBlitDependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
afterBlitDependency.imageMemoryBarrierCount = 1;
afterBlitDependency.pImageMemoryBarriers = &afterBlitBarrier;
vkCmdPipelineBarrier2(commandBuffer, &afterBlitDependency);
if (mipWidth > 1) mipWidth /= 2;
if (mipHeight > 2) mipHeight /= 2;
}
// Final mipLevel is never transitioned from TRANSFER_DST_OPTIMAL
// barrier: (mipLevels - 1) TRANSFER_DST_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL
VkImageMemoryBarrier2 finalBlitBarrier{};
finalBlitBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
finalBlitBarrier.srcStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT | VK_PIPELINE_STAGE_2_COPY_BIT;
finalBlitBarrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
finalBlitBarrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
finalBlitBarrier.dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_2_SHADER_READ_BIT;
finalBlitBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
finalBlitBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
finalBlitBarrier.srcQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
finalBlitBarrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
finalBlitBarrier.image = out->image;
finalBlitBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
finalBlitBarrier.subresourceRange.baseMipLevel = (mipLevels - 1);
finalBlitBarrier.subresourceRange.levelCount = 1;
finalBlitBarrier.subresourceRange.baseArrayLayer = face;
finalBlitBarrier.subresourceRange.layerCount = 1;
VkDependencyInfo afterBlitDependency{};
afterBlitDependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
afterBlitDependency.imageMemoryBarrierCount = 1;
afterBlitDependency.pImageMemoryBarriers = &finalBlitBarrier;
vkCmdPipelineBarrier2(commandBuffer, &afterBlitDependency);
}
VKCHECK(vkEndCommandBuffer(commandBuffer));
}
// submit
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
VKCHECK(vkQueueSubmit(pimpl->device.queues.drawQueues[0], 1, &submitInfo, VK_NULL_HANDLE));
VKCHECK(vkQueueWaitIdle(pimpl->device.queues.drawQueues[0]));
vkFreeCommandBuffers(pimpl->device.device, pimpl->graphicsCommandPool, 1, &commandBuffer);
vmaDestroyBuffer(pimpl->allocator, stagingBuffer, stagingAllocation);
return out;
}
void GFXDevice::DestroyImage(const gfx::Image* image) void GFXDevice::DestroyImage(const gfx::Image* image)
{ {
assert(image != nullptr); assert(image != nullptr);
@ -1641,9 +1911,9 @@ const gfx::Sampler* GFXDevice::CreateSampler(const gfx::SamplerInfo& info)
samplerInfo.magFilter = converters::getFilter(info.magnify); samplerInfo.magFilter = converters::getFilter(info.magnify);
samplerInfo.minFilter = converters::getFilter(info.minify); samplerInfo.minFilter = converters::getFilter(info.minify);
samplerInfo.mipmapMode = converters::getSamplerMipmapMode(info.mipmap); samplerInfo.mipmapMode = converters::getSamplerMipmapMode(info.mipmap);
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeU = converters::getSamplerAddressMode(info.wrap_u);
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = converters::getSamplerAddressMode(info.wrap_v);
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = converters::getSamplerAddressMode(info.wrap_w);
samplerInfo.mipLodBias = 0.0f; samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = (info.anisotropic_filtering && pimpl->graphicsSettings.enable_anisotropy) ? VK_TRUE : VK_FALSE; samplerInfo.anisotropyEnable = (info.anisotropic_filtering && pimpl->graphicsSettings.enable_anisotropy) ? VK_TRUE : VK_FALSE;
samplerInfo.maxAnisotropy = pimpl->device.properties.limits.maxSamplerAnisotropy; samplerInfo.maxAnisotropy = pimpl->device.properties.limits.maxSamplerAnisotropy;

View File

@ -1,6 +1,9 @@
#include "renderer.h" #include "renderer.h"
#include <array>
#include "application_component.h" #include "application_component.h"
#include "util/files.h"
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <glm/trigonometric.hpp> #include <glm/trigonometric.hpp>
@ -20,12 +23,16 @@ Renderer::Renderer(Application& app, gfx::GraphicsSettings settings) : Applicati
auto& binding0 = globalSetBindings.emplace_back(); auto& binding0 = globalSetBindings.emplace_back();
binding0.descriptor_type = gfx::DescriptorType::kUniformBuffer; binding0.descriptor_type = gfx::DescriptorType::kUniformBuffer;
binding0.stage_flags = gfx::ShaderStageFlags::kVertex; binding0.stage_flags = gfx::ShaderStageFlags::kVertex;
auto& binding1 = globalSetBindings.emplace_back();
binding1.descriptor_type = gfx::DescriptorType::kCombinedImageSampler;
binding1.stage_flags = gfx::ShaderStageFlags::kFragment;
} }
global_uniform.layout = device_->CreateDescriptorSetLayout(globalSetBindings); global_uniform.layout = device_->CreateDescriptorSetLayout(globalSetBindings);
global_uniform.set = device_->AllocateDescriptorSet(global_uniform.layout); global_uniform.set = device_->AllocateDescriptorSet(global_uniform.layout);
global_uniform.uniform_buffer_data.data = glm::mat4{1.0f}; global_uniform.uniform_buffer_data.data = glm::mat4{1.0f};
global_uniform.uniform_buffer = device_->CreateUniformBuffer(sizeof(global_uniform.uniform_buffer_data), &global_uniform.uniform_buffer_data); global_uniform.uniform_buffer = device_->CreateUniformBuffer(sizeof(global_uniform.uniform_buffer_data), &global_uniform.uniform_buffer_data);
device_->UpdateDescriptorUniformBuffer(global_uniform.set, 0, global_uniform.uniform_buffer, 0, sizeof(global_uniform.uniform_buffer_data)); device_->UpdateDescriptorUniformBuffer(global_uniform.set, 0, global_uniform.uniform_buffer, 0, sizeof(global_uniform.uniform_buffer_data));
// binding1 is updated towards the end of the constructor once the skybox texture is loaded
std::vector<gfx::DescriptorSetLayoutBinding> frameSetBindings; std::vector<gfx::DescriptorSetLayoutBinding> frameSetBindings;
{ {
@ -55,6 +62,7 @@ Renderer::Renderer(Application& app, gfx::GraphicsSettings settings) : Applicati
debug_vertex_format.stride = 0; debug_vertex_format.stride = 0;
// debug_vertex_format.vertex_attrib_descriptions = empty // debug_vertex_format.vertex_attrib_descriptions = empty
{
gfx::PipelineInfo debug_pipeline_info{}; gfx::PipelineInfo debug_pipeline_info{};
debug_pipeline_info.vert_shader_path = GetResourcePath("engine/shaders/debug.vert"); debug_pipeline_info.vert_shader_path = GetResourcePath("engine/shaders/debug.vert");
debug_pipeline_info.frag_shader_path = GetResourcePath("engine/shaders/debug.frag"); debug_pipeline_info.frag_shader_path = GetResourcePath("engine/shaders/debug.frag");
@ -66,10 +74,122 @@ Renderer::Renderer(Application& app, gfx::GraphicsSettings settings) : Applicati
debug_pipeline_info.line_primitives = true; debug_pipeline_info.line_primitives = true;
debug_rendering_things_.pipeline = device_->CreatePipeline(debug_pipeline_info); debug_rendering_things_.pipeline = device_->CreatePipeline(debug_pipeline_info);
}
// create the skybox cubemap
{
constexpr uint32_t cubemap_w = 2048;
constexpr uint32_t cubemap_h = 2048;
int w{}, h{};
std::array<std::unique_ptr<std::vector<uint8_t>>, 6> face_unq_ptrs{};
std::array<const void*, 6> face_unsafe_ptrs{};
for (int face = 0; face < 6; ++face) {
std::string path = std::string("engine/textures/skybox") + std::to_string(face) + std::string(".jpg");
face_unq_ptrs[face] = util::ReadImageFile(GetResourcePath(path), w, h);
if (cubemap_w != w || cubemap_h != h) throw std::runtime_error("Skybox textures must be 512x512!");
face_unsafe_ptrs[face] = face_unq_ptrs[face]->data();
}
skybox_cubemap = device_->CreateImageCubemap(cubemap_w, cubemap_h, gfx::ImageFormat::kSRGB, face_unsafe_ptrs);
gfx::SamplerInfo sampler_info{};
sampler_info.magnify = gfx::Filter::kLinear;
sampler_info.minify = gfx::Filter::kLinear;
sampler_info.mipmap = gfx::Filter::kLinear;
sampler_info.wrap_u = gfx::WrapMode::kClampToEdge;
sampler_info.wrap_v = gfx::WrapMode::kClampToEdge;
sampler_info.wrap_w = gfx::WrapMode::kClampToEdge;
sampler_info.anisotropic_filtering = true;
skybox_sampler = device_->CreateSampler(sampler_info);
}
// create skybox shader
{
gfx::VertexFormat vertex_format{};
vertex_format.attribute_descriptions.emplace_back(0, gfx::VertexAttribFormat::kFloat3, 0);
vertex_format.stride = 3 * sizeof(float);
gfx::PipelineInfo pipeline_info{};
pipeline_info.vert_shader_path = GetResourcePath("engine/shaders/skybox.vert");
pipeline_info.frag_shader_path = GetResourcePath("engine/shaders/skybox.frag");
pipeline_info.vertex_format = vertex_format;
pipeline_info.alpha_blending = false;
pipeline_info.backface_culling = true;
pipeline_info.write_z = false;
pipeline_info.line_primitives = false;
pipeline_info.descriptor_set_layouts.push_back(GetGlobalSetLayout());
pipeline_info.descriptor_set_layouts.push_back(GetFrameSetLayout());
skybox_pipeline = device_->CreatePipeline(pipeline_info);
device_->UpdateDescriptorCombinedImageSampler(global_uniform.set, 1, skybox_cubemap, skybox_sampler);
}
// create skybox vertex buffer
{
std::vector<glm::vec3> v{};
v.push_back({0.0f, 0.0f, 2.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({2.0f, 0.0f, 0.0f});
v.push_back({2.0f, 0.0f, 0.0f});
v.push_back({2.0f, 0.0f, 2.0f});
v.push_back({0.0f, 0.0f, 2.0f});
// back
v.push_back({2.0f, 2.0f, 2.0f });
v.push_back({ 2.0f, 2.0f, 0.0f});
v.push_back({0.0f, 2.0f, 0.0f});
v.push_back({0.0f, 2.0f, 0.0f});
v.push_back({0.0f, 2.0f, 2.0f });
v.push_back({ 2.0f, 2.0f, 2.0f });
// left
v.push_back({0.0f, 2.0f, 2.0f });
v.push_back({0.0f, 2.0f, 0.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({0.0f, 0.0f, 2.0f });
v.push_back({0.0f, 2.0f, 2.0f });
// right
v.push_back({ 2.0f, 0.0f, 2.0f });
v.push_back({ 2.0f, 0.0f, 0.0f});
v.push_back({ 2.0f, 2.0f, 0.0f});
v.push_back({ 2.0f, 2.0f, 0.0f});
v.push_back({ 2.0f, 2.0f, 2.0f });
v.push_back({ 2.0f, 0.0f, 2.0f });
// top
v.push_back({0.0f, 2.0f, 2.0f });
v.push_back({0.0f, 0.0f, 2.0f });
v.push_back({ 2.0f, 0.0f, 2.0f });
v.push_back({ 2.0f, 0.0f, 2.0f });
v.push_back({ 2.0f, 2.0f, 2.0f });
v.push_back({0.0f, 2.0f, 2.0f });
// bottom
v.push_back({ 2.0f, 2.0f, 0.0f});
v.push_back({ 2.0f, 0.0f, 0.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({0.0f, 2.0f, 0.0f});
v.push_back({ 2.0f, 2.0f, 0.0f});
for (glm::vec3& pos : v) {
pos.x -= 1.0f;
pos.y -= 1.0f;
pos.z -= 1.0f;
}
for (size_t i = 0; i < v.size(); i += 3) {
std::swap(v[i], v[i + 2]);
}
skybox_buffer = device_->CreateBuffer(gfx::BufferType::kVertex, v.size() * sizeof(glm::vec3), v.data());
}
}; };
Renderer::~Renderer() Renderer::~Renderer()
{ {
device_->DestroyBuffer(skybox_buffer);
device_->DestroyPipeline(skybox_pipeline);
device_->DestroySampler(skybox_sampler);
device_->DestroyImage(skybox_cubemap);
device_->DestroyPipeline(debug_rendering_things_.pipeline); device_->DestroyPipeline(debug_rendering_things_.pipeline);
for (const auto& [info, sampler] : samplers) { for (const auto& [info, sampler] : samplers) {
@ -127,6 +247,15 @@ void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_l
glm::vec3 color; glm::vec3 color;
}; };
// draw skybox
{
device_->CmdBindPipeline(draw_buffer, skybox_pipeline);
device_->CmdBindDescriptorSet(draw_buffer, skybox_pipeline, global_uniform.set, 0);
device_->CmdBindDescriptorSet(draw_buffer, skybox_pipeline, frame_uniform.set, 1);
device_->CmdBindVertexBuffer(draw_buffer, 0, skybox_buffer);
device_->CmdDraw(draw_buffer, 36, 1, 0, 0);
}
// draw debug shit here // draw debug shit here
device_->CmdBindPipeline(draw_buffer, debug_rendering_things_.pipeline); device_->CmdBindPipeline(draw_buffer, debug_rendering_things_.pipeline);
DebugPush push{}; DebugPush push{};

View File

@ -23,16 +23,10 @@ void Material::SetNormalTexture(std::shared_ptr<Texture> texture)
texture_normal_ = texture; texture_normal_ = texture;
} }
void Material::SetOcclusionTexture(std::shared_ptr<Texture> texture) void Material::SetOcclusionRoughnessMetallicTexture(std::shared_ptr<Texture> texture)
{ {
renderer_->GetDevice()->UpdateDescriptorCombinedImageSampler(material_set_, 2, texture->GetImage(), texture->GetSampler()); renderer_->GetDevice()->UpdateDescriptorCombinedImageSampler(material_set_, 2, texture->GetImage(), texture->GetSampler());
texture_occlusion_ = texture; texture_occlusion_roughness_metallic_ = texture;
}
void Material::SetMetallicRoughnessTexture(std::shared_ptr<Texture> texture)
{
renderer_->GetDevice()->UpdateDescriptorCombinedImageSampler(material_set_, 3, texture->GetImage(), texture->GetSampler());
texture_metallic_roughness_ = texture;
} }
} // namespace engine } // namespace engine

View File

@ -109,6 +109,14 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
throw std::runtime_error("Failed to load glTF file!"); throw std::runtime_error("Failed to load glTF file!");
} }
// check for required extensions
if (model.extensionsRequired.empty() == false) { // this loader doesn't support any extensions
for (const auto& ext : model.extensionsRequired) {
LOG_ERROR("Unsupported required extension: {}", ext);
}
throw std::runtime_error("One or more required extensions are unsupported. File: " + path);
}
// test model loading // test model loading
if (model.scenes.size() < 1) { if (model.scenes.size() < 1) {
@ -142,7 +150,10 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
// use missing texture image by default // use missing texture image by default
textures.emplace_back(scene.app()->GetResource<Texture>("builtin.white")); textures.emplace_back(scene.app()->GetResource<Texture>("builtin.white"));
if (texture.source == -1) continue; if (texture.source == -1) {
LOG_ERROR("A gltf file specifies a texture with no source.");
continue;
}
gfx::SamplerInfo samplerInfo{}; gfx::SamplerInfo samplerInfo{};
// default to trilinear filtering even if mipmaps are not specified // default to trilinear filtering even if mipmaps are not specified
@ -193,6 +204,9 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
textures.back() = std::make_shared<Texture>(scene.app()->renderer(), image.image.data(), image.width, image.height, samplerInfo, textures.back() = std::make_shared<Texture>(scene.app()->renderer(), image.image.data(), image.width, image.height, samplerInfo,
tex_index_is_base_color[texture_idx]); tex_index_is_base_color[texture_idx]);
} }
else {
throw std::runtime_error("Texture found in gltf file is unsupported! Make sure texture is 8 bpp. File: " + path);
}
++texture_idx; ++texture_idx;
} }
@ -256,20 +270,20 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
materials.back()->SetAlbedoTexture(colour_textures.at(c)); materials.back()->SetAlbedoTexture(colour_textures.at(c));
} }
// metallic roughness // occlusion roughness metallic
materials.back()->SetMetallicRoughnessTexture(scene.app()->GetResource<Texture>("builtin.white")); // default metal = 1.0, rough = 1.0 materials.back()->SetOcclusionRoughnessMetallicTexture(scene.app()->GetResource<Texture>("builtin.white")); // default ao = 1.0, rough = 1.0, metal = 1.0
if (material.pbrMetallicRoughness.metallicRoughnessTexture.index != -1) { if (material.pbrMetallicRoughness.metallicRoughnessTexture.index != -1) {
if (material.pbrMetallicRoughness.metallicRoughnessTexture.texCoord == 0) { if (material.pbrMetallicRoughness.metallicRoughnessTexture.texCoord == 0) {
LOG_INFO("Setting metallic roughness texture!"); LOG_INFO("Setting occlusion roughness metallic texture!");
materials.back()->SetMetallicRoughnessTexture(textures.at(material.pbrMetallicRoughness.metallicRoughnessTexture.index)); materials.back()->SetOcclusionRoughnessMetallicTexture(textures.at(material.pbrMetallicRoughness.metallicRoughnessTexture.index));
} }
else { else {
LOG_WARN("Material {} metallic roughness texture specifies a UV channel other than zero which is unsupported.", material.name); LOG_WARN("Material {} metallic roughness texture specifies a UV channel other than zero which is unsupported.", material.name);
} }
} }
else { else {
LOG_INFO("Creating a metallic-roughness texture..."); LOG_INFO("Creating occlusion roughness metallic texture...");
const std::vector<double> mr_values{1.0f, material.pbrMetallicRoughness.roughnessFactor, material.pbrMetallicRoughness.metallicFactor, 1.0f}; const std::vector<double> mr_values{1.0f /* no AO */, material.pbrMetallicRoughness.roughnessFactor, material.pbrMetallicRoughness.metallicFactor, 1.0f};
Color mr(mr_values); Color mr(mr_values);
if (metal_rough_textures.contains(mr) == false) { if (metal_rough_textures.contains(mr) == false) {
const uint8_t pixel[4] = {mr.r, mr.g, mr.b, mr.a}; const uint8_t pixel[4] = {mr.r, mr.g, mr.b, mr.a};
@ -280,15 +294,16 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
samplerInfo.anisotropic_filtering = false; samplerInfo.anisotropic_filtering = false;
metal_rough_textures.emplace(std::make_pair(mr, std::make_shared<Texture>(scene.app()->renderer(), pixel, 1, 1, samplerInfo, false))); metal_rough_textures.emplace(std::make_pair(mr, std::make_shared<Texture>(scene.app()->renderer(), pixel, 1, 1, samplerInfo, false)));
} }
materials.back()->SetMetallicRoughnessTexture(metal_rough_textures.at(mr)); materials.back()->SetOcclusionRoughnessMetallicTexture(metal_rough_textures.at(mr));
} }
// occlusion texture // occlusion texture
materials.back()->SetOcclusionTexture(scene.app()->GetResource<Texture>("builtin.white")); // R=255 means no AO so white will work // if occlusion texture was same as metallic-roughness texture, this will already have been applied
if (material.occlusionTexture.index != -1) { if (material.occlusionTexture.index != -1) {
if (material.occlusionTexture.texCoord == 0) { if (material.occlusionTexture.texCoord == 0) {
LOG_INFO("Setting occlusion texture!"); if (material.occlusionTexture.index != material.pbrMetallicRoughness.metallicRoughnessTexture.index) {
materials.back()->SetOcclusionTexture(textures.at(material.occlusionTexture.index)); throw std::runtime_error(std::string("Material ") + material.name + std::string(" has an ambient occlusion texture different to the metal-rough texture."));
}
} }
else { else {
LOG_WARN("Material {} occlusion texture specifies a UV channel other than zero which is unsupported.", material.name); LOG_WARN("Material {} occlusion texture specifies a UV channel other than zero which is unsupported.", material.name);

View File

@ -210,10 +210,12 @@ namespace engine {
if (memoryPriorityRequired) { if (memoryPriorityRequired) {
if (memoryPriorityFeatures.memoryPriority == VK_FALSE) { if (memoryPriorityFeatures.memoryPriority == VK_FALSE) {
throw std::runtime_error("Required device feature 'memoryPriority' not found, but extension was"); throw std::runtime_error("Required device feature 'memoryPriority' not found, but extension was");
} else { }
else {
d.memoryPriorityFeature = true; d.memoryPriorityFeature = true;
} }
} else { }
else {
// see if memoryPriority was optionally requested */ // see if memoryPriority was optionally requested */
for (const char* optExt : requirements.optionalExtensions) { for (const char* optExt : requirements.optionalExtensions) {
if (strcmp(optExt, VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) == 0) { if (strcmp(optExt, VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) == 0) {
@ -221,17 +223,18 @@ namespace engine {
if (strcmp(extAvail.extensionName, VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) == 0) { if (strcmp(extAvail.extensionName, VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) == 0) {
if (memoryPriorityFeatures.memoryPriority == VK_TRUE) { if (memoryPriorityFeatures.memoryPriority == VK_TRUE) {
d.memoryPriorityFeature = true; d.memoryPriorityFeature = true;
} else { }
else {
throw std::runtime_error("Optional device extension 'VK_EXT_memory_priority' found, but feature wasn't"); throw std::runtime_error("Optional device extension 'VK_EXT_memory_priority' found, but feature wasn't");
} }
break; // | break; // |
} // | } // |
} // V } // V
// <-------------------- // <---------------
break; // | break; // |
} // | } // |
} // V } // V
// <-------------------- // <---------------
} }
} }
@ -304,9 +307,7 @@ namespace engine {
// prefer a dedicated transfer queue family // prefer a dedicated transfer queue family
for (size_t i = 0; i < queueFamilies.size(); i++) { for (size_t i = 0; i < queueFamilies.size(); i++) {
VkQueueFamilyProperties p = queueFamilies[i]; VkQueueFamilyProperties p = queueFamilies[i];
if (((p.queueFlags & VK_QUEUE_TRANSFER_BIT) != 0) && if (((p.queueFlags & VK_QUEUE_TRANSFER_BIT) != 0) && ((p.queueFlags & VK_QUEUE_COMPUTE_BIT) == 0) && ((p.queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) {
((p.queueFlags & VK_QUEUE_COMPUTE_BIT) == 0) &&
((p.queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) {
transferFamily = static_cast<uint32_t>(i); transferFamily = static_cast<uint32_t>(i);
break; break;
} }
@ -323,24 +324,20 @@ namespace engine {
std::fill(transferQueuePriorities.begin(), transferQueuePriorities.end(), 1.0f); std::fill(transferQueuePriorities.begin(), transferQueuePriorities.end(), 1.0f);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{}; std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{};
queueCreateInfos.push_back(VkDeviceQueueCreateInfo{ queueCreateInfos.push_back(VkDeviceQueueCreateInfo{.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.queueFamilyIndex = graphicsFamily, .queueFamilyIndex = graphicsFamily,
.queueCount = queueFamilies[graphicsFamily].queueCount, .queueCount = queueFamilies[graphicsFamily].queueCount,
.pQueuePriorities = graphicsQueuePriorities.data() .pQueuePriorities = graphicsQueuePriorities.data()});
});
if (transferFamily != graphicsFamily) { if (transferFamily != graphicsFamily) {
queueCreateInfos.push_back(VkDeviceQueueCreateInfo{ queueCreateInfos.push_back(VkDeviceQueueCreateInfo{.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.queueFamilyIndex = transferFamily, .queueFamilyIndex = transferFamily,
.queueCount = queueFamilies[transferFamily].queueCount, .queueCount = queueFamilies[transferFamily].queueCount,
.pQueuePriorities = transferQueuePriorities.data() .pQueuePriorities = transferQueuePriorities.data()});
});
} }
/* set enabled features */ /* set enabled features */
@ -359,8 +356,8 @@ namespace engine {
/* get list of extensions to enable */ /* get list of extensions to enable */
std::vector<const char*> extensionsToEnable{}; std::vector<const char*> extensionsToEnable{};
for (const VkExtensionProperties& availableExt : availableExtensions) { for (const VkExtensionProperties& availableExt : availableExtensions) {
if ( std::find(requirements.optionalExtensions.begin(), requirements.optionalExtensions.end(), if (std::find(requirements.optionalExtensions.begin(), requirements.optionalExtensions.end(), std::string(availableExt.extensionName)) !=
std::string(availableExt.extensionName)) != requirements.optionalExtensions.end()) { requirements.optionalExtensions.end()) {
extensionsToEnable.push_back(availableExt.extensionName); extensionsToEnable.push_back(availableExt.extensionName);
} }
} }
@ -401,7 +398,8 @@ namespace engine {
for (uint32_t i = 0; i < d.queues.drawQueues.size(); i++) { for (uint32_t i = 0; i < d.queues.drawQueues.size(); i++) {
vkGetDeviceQueue(d.device, graphicsFamily, i + 1, &d.queues.drawQueues[i]); vkGetDeviceQueue(d.device, graphicsFamily, i + 1, &d.queues.drawQueues[i]);
} }
} else { }
else {
d.queues.drawQueues.resize(1); d.queues.drawQueues.resize(1);
d.queues.drawQueues[0] = d.queues.presentQueue; d.queues.drawQueues[0] = d.queues.presentQueue;
} }
@ -409,7 +407,8 @@ namespace engine {
for (uint32_t i = 0; i < d.queues.transferQueues.size(); i++) { for (uint32_t i = 0; i < d.queues.transferQueues.size(); i++) {
vkGetDeviceQueue(d.device, transferFamily, i, &d.queues.transferQueues[i]); vkGetDeviceQueue(d.device, transferFamily, i, &d.queues.transferQueues[i]);
} }
} else { }
else {
// same graphics family for graphics/present and transfer // same graphics family for graphics/present and transfer
uint32_t queueCount = queueFamilies[graphicsFamily].queueCount; uint32_t queueCount = queueFamilies[graphicsFamily].queueCount;
vkGetDeviceQueue(d.device, graphicsFamily, 0, &d.queues.presentQueue); vkGetDeviceQueue(d.device, graphicsFamily, 0, &d.queues.presentQueue);
@ -422,14 +421,16 @@ namespace engine {
for (uint32_t i = 0; i < queueCount - 2; i++) { for (uint32_t i = 0; i < queueCount - 2; i++) {
vkGetDeviceQueue(d.device, graphicsFamily, i + 2, &d.queues.drawQueues[i]); vkGetDeviceQueue(d.device, graphicsFamily, i + 2, &d.queues.drawQueues[i]);
} }
} else { }
else {
// 2 queues available // 2 queues available
// present and drawing share a queue // present and drawing share a queue
// transfer gets its own queue // transfer gets its own queue
d.queues.drawQueues.resize(1); d.queues.drawQueues.resize(1);
d.queues.drawQueues[0] = d.queues.presentQueue; d.queues.drawQueues[0] = d.queues.presentQueue;
} }
} else { }
else {
// only 1 queue available :( // only 1 queue available :(
d.queues.transferQueues.resize(1); d.queues.transferQueues.resize(1);
d.queues.transferQueues[0] = d.queues.presentQueue; d.queues.transferQueues[0] = d.queues.presentQueue;
@ -442,12 +443,8 @@ namespace engine {
d.queues.transferQueueFamily = transferFamily; d.queues.transferQueueFamily = transferFamily;
return d; return d;
} }
void destroyDevice(Device device) void destroyDevice(Device device) { vkDestroyDevice(device.device, nullptr); }
{
vkDestroyDevice(device.device, nullptr);
}
} } // namespace engine

Binary file not shown.

Binary file not shown.

BIN
test/res/models/cube.glb Normal file

Binary file not shown.

BIN
test/res/models/floor.glb Normal file

Binary file not shown.

View File

@ -37,6 +37,10 @@ void CameraControllerSystem::OnUpdate(float ts)
float dx = scene_->app()->input_manager()->GetAxis("movex") * CameraControllerComponent::kSpeedStrafe; float dx = scene_->app()->input_manager()->GetAxis("movex") * CameraControllerComponent::kSpeedStrafe;
float dy = scene_->app()->input_manager()->GetAxis("movey") * CameraControllerComponent::kSpeedForwardBack; float dy = scene_->app()->input_manager()->GetAxis("movey") * CameraControllerComponent::kSpeedForwardBack;
if (scene_->app()->input_manager()->GetButton("sprint")) {
dy *= CameraControllerComponent::kSprintMultiplier;
}
// calculate new pitch and yaw // calculate new pitch and yaw
float d_pitch = scene_->app()->input_manager()->GetAxis("looky") * -1.0f * CameraControllerComponent::kCameraSensitivity; float d_pitch = scene_->app()->input_manager()->GetAxis("looky") * -1.0f * CameraControllerComponent::kCameraSensitivity;
@ -66,25 +70,41 @@ void CameraControllerSystem::OnUpdate(float ts)
// check horizontal collisions first as otherwise the player may be teleported above a wall instead of colliding against it // check horizontal collisions first as otherwise the player may be teleported above a wall instead of colliding against it
if (c->vel.x != 0.0f || c->vel.y != 0.0f) { // just in case, to avoid a ray with direction = (0,0,0) if (c->vel.x != 0.0f || c->vel.y != 0.0f) { // just in case, to avoid a ray with direction = (0,0,0)
engine::Ray horiz_ray{};
horiz_ray.origin = t->position; // set origin to 'MaxStairHeight' units above player's feet std::array<engine::Raycast, CameraControllerComponent::kNumHorizontalRays> raycasts;
horiz_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight; engine::Raycast* chosen_cast = nullptr; // nullptr means no hit at all
horiz_ray.direction.x = c->vel.x;
horiz_ray.direction.y = c->vel.y; // this is normalized by GetRayCast() float smallest_distance = std::numeric_limits<float>::infinity();
horiz_ray.direction.z = 0.0f; for (size_t i = 0; i < raycasts.size(); ++i) {
const engine::Raycast horiz_raycast = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(horiz_ray);
if (horiz_raycast.hit) { const float lerp_value = static_cast<float>(i) / (static_cast<float>(CameraControllerComponent::kNumHorizontalRays) - 1);
const glm::vec2 norm_xy = glm::normalize(glm::vec2{horiz_raycast.normal.x, horiz_raycast.normal.y}) * -1.0f; // make it point towards object
engine::Ray ray{};
ray.origin = t->position;
ray.origin.z -= (CameraControllerComponent::kPlayerHeight - CameraControllerComponent::kMaxStairHeight) * lerp_value;
ray.direction.x = c->vel.x;
ray.direction.y = c->vel.y; // this is normalized by GetRayCast()
ray.direction.z = 0.0f;
raycasts[i] = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(ray);
if (raycasts[i].hit && raycasts[i].distance < smallest_distance) {
smallest_distance = raycasts[i].distance;
chosen_cast = &raycasts[i];
}
}
if (chosen_cast != nullptr) {
const glm::vec2 norm_xy = glm::normalize(glm::vec2{chosen_cast->normal.x, chosen_cast->normal.y}) * -1.0f; // make it point towards object
const glm::vec2 vel_xy = glm::vec2{ c->vel.x, c->vel.y }; const glm::vec2 vel_xy = glm::vec2{ c->vel.x, c->vel.y };
// find the extent of the player's velocity in the direction of the wall's normal vector // find the extent of the player's velocity in the direction of the wall's normal vector
const glm::vec2 partial_vel = norm_xy * glm::dot(norm_xy, vel_xy); const glm::vec2 partial_vel = norm_xy * glm::dot(norm_xy, vel_xy);
const glm::vec2 partial_dX = partial_vel * dt; const glm::vec2 partial_dX = partial_vel * dt;
if (glm::length(partial_dX) > horiz_raycast.distance - CameraControllerComponent::kPlayerCollisionRadius) { if (glm::length(partial_dX) > chosen_cast->distance - CameraControllerComponent::kPlayerCollisionRadius) {
// player will collide with wall // player will collide with wall
// push player out of collision zone // push player out of collision zone
const glm::vec2 push_vector = glm::normalize(vel_xy) * fmaxf(CameraControllerComponent::kPlayerCollisionRadius, horiz_raycast.distance); const glm::vec2 push_vector = glm::normalize(vel_xy) * fmaxf(CameraControllerComponent::kPlayerCollisionRadius, chosen_cast->distance);
t->position.x = horiz_raycast.location.x - push_vector.x; t->position.x = chosen_cast->location.x - push_vector.x;
t->position.y = horiz_raycast.location.y - push_vector.y; t->position.y = chosen_cast->location.y - push_vector.y;
c->vel.x -= partial_vel.x; c->vel.x -= partial_vel.x;
c->vel.y -= partial_vel.y; c->vel.y -= partial_vel.y;
} }
@ -103,7 +123,6 @@ void CameraControllerSystem::OnUpdate(float ts)
const float mag_dz = fabsf(c->vel.z * dt); const float mag_dz = fabsf(c->vel.z * dt);
// check if the player will be less than 'height' units above the collided ground // check if the player will be less than 'height' units above the collided ground
if (mag_dz > fall_raycast.distance - CameraControllerComponent::kMaxStairHeight) { if (mag_dz > fall_raycast.distance - CameraControllerComponent::kMaxStairHeight) {
LOG_INFO("HIT");
// push player up to ground level and set as grounded // push player up to ground level and set as grounded
t->position.z = fall_raycast.location.z + CameraControllerComponent::kPlayerHeight; t->position.z = fall_raycast.location.z + CameraControllerComponent::kPlayerHeight;
c->vel.z = 0.0f; c->vel.z = 0.0f;
@ -119,19 +138,25 @@ void CameraControllerSystem::OnUpdate(float ts)
} }
else if (c->vel.z > 0.0f) { else if (c->vel.z > 0.0f) {
c->grounded = false; // they are jumping c->grounded = false; // they are jumping
// check if intersection with ceiling
engine::Ray jump_ray{};
jump_ray.origin = t->position;
jump_ray.direction = { 0.0f, 0.0f, 1.0f };
const engine::Raycast jump_raycast = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(jump_ray);
if (jump_raycast.hit) {
// find how far the player will move (upwards) if velocity is applied without collision
const float mag_dz = fabsf(c->vel.z * dt);
// check if the player will be higher than the collided ground
if (mag_dz > jump_raycast.distance - CameraControllerComponent::kPlayerCollisionRadius) {
// push player below ceiling
t->position.z = jump_raycast.location.z - CameraControllerComponent::kPlayerCollisionRadius;
c->vel.z = 0.0f;
}
} }
if (c->was_grounded != c->grounded) {
LOG_INFO("GROUNDED? {}", c->grounded);
c->was_grounded = c->grounded;
} }
t->position += c->vel * dt; t->position += c->vel * dt;
if (glm::length(t->position) > CameraControllerComponent::kMaxDistanceFromOrigin) {
t->position = {0.0f, 0.0f, 100.0f};
}
/* ROTATION STUFF */ /* ROTATION STUFF */
// pitch quaternion // pitch quaternion
@ -155,15 +180,8 @@ void CameraControllerSystem::OnUpdate(float ts)
/* user interface inputs */ /* user interface inputs */
if (scene_->app()->window()->GetKeyPress(engine::inputs::Key::K_P)) { if (scene_->app()->window()->GetKeyPress(engine::inputs::Key::K_R) || glm::length(t->position) > CameraControllerComponent::kMaxDistanceFromOrigin) {
std::string pos_string{"x: " + std::to_string(t->world_matrix[3][0]) + " y: " + std::to_string(t->world_matrix[3][1]) + t->position = {0.0f, 0.0f, 100.0f};
" z: " + std::to_string(t->world_matrix[3][2])};
LOG_INFO("position {}", pos_string);
LOG_INFO("rotation w: {} x: {} y: {} z: {}", t->rotation.w, t->rotation.x, t->rotation.y, t->rotation.z);
}
if (scene_->app()->window()->GetKeyPress(engine::inputs::Key::K_R)) {
t->position = {0.0f, 0.0f, 10.0f};
c->vel = {0.0f, 0.0f, 0.0f}; c->vel = {0.0f, 0.0f, 0.0f};
c->pitch = glm::half_pi<float>(); c->pitch = glm::half_pi<float>();
c->yaw = 0.0f; c->yaw = 0.0f;
@ -181,9 +199,7 @@ void CameraControllerSystem::OnUpdate(float ts)
scene_->app()->scene_manager()->SetActiveScene(next_scene_); scene_->app()->scene_manager()->SetActiveScene(next_scene_);
} }
static std::vector<engine::Line> perm_lines{}; if (scene_->app()->window()->GetButtonPress(engine::inputs::MouseButton::M_LEFT)) {
if (scene_->app()->window()->GetButton(engine::inputs::MouseButton::M_LEFT)) {
engine::Ray ray{}; engine::Ray ray{};
ray.origin = t->position; ray.origin = t->position;
ray.direction = glm::vec3(glm::mat4_cast(t->rotation) * glm::vec4{0.0f, 0.0f, -1.0f, 1.0f}); ray.direction = glm::vec3(glm::mat4_cast(t->rotation) * glm::vec4{0.0f, 0.0f, -1.0f, 1.0f});
@ -195,24 +211,9 @@ void CameraControllerSystem::OnUpdate(float ts)
LOG_INFO("Normal: {} {} {}", cast.normal.x, cast.normal.y, cast.normal.z); LOG_INFO("Normal: {} {} {}", cast.normal.x, cast.normal.y, cast.normal.z);
LOG_INFO("Ray direction: {} {} {}", ray.direction.x, ray.direction.y, ray.direction.z); LOG_INFO("Ray direction: {} {} {}", ray.direction.x, ray.direction.y, ray.direction.z);
LOG_INFO("Hit Entity: {}", scene_->GetComponent<engine::TransformComponent>(cast.hit_entity)->tag); LOG_INFO("Hit Entity: {}", scene_->GetComponent<engine::TransformComponent>(cast.hit_entity)->tag);
perm_lines.emplace_back(ray.origin, cast.location, glm::vec3{0.0f, 0.0f, 1.0f}); c->perm_lines.emplace_back(ray.origin, cast.location, glm::vec3{0.0f, 0.0f, 1.0f});
}
}
if (scene_->app()->window()->GetButtonPress(engine::inputs::MouseButton::M_RIGHT)) {
engine::Ray horiz_ray{};
horiz_ray.origin = t->position; // set origin to 'MaxStairHeight' units above player's feet
horiz_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight;
horiz_ray.direction.x = c->vel.x;
horiz_ray.direction.y = c->vel.y; // this is normalized by GetRayCast()
horiz_ray.direction.z = 0.0f;
const engine::Raycast cast = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(horiz_ray);
if (cast.hit) {
LOG_INFO("Distance: {} m", cast.distance);
LOG_INFO("Location: {} {} {}", cast.location.x, cast.location.y, cast.location.z);
LOG_INFO("Normal: {} {} {}", cast.normal.x, cast.normal.y, cast.normal.z);
perm_lines.emplace_back(horiz_ray.origin, cast.location, glm::vec3{ 0.0f, 0.0f, 1.0f });
} }
} }
scene_->app()->debug_lines.insert(scene_->app()->debug_lines.end(), perm_lines.begin(), perm_lines.end()); scene_->app()->debug_lines.insert(scene_->app()->debug_lines.end(), c->perm_lines.begin(), c->perm_lines.end());
} }

View File

@ -4,28 +4,38 @@
#include <glm/vec3.hpp> #include <glm/vec3.hpp>
#include "components/transform.h" #include "components/transform.h"
#include "application.h"
#include "ecs.h" #include "ecs.h"
struct CameraControllerComponent { struct CameraControllerComponent {
static constexpr float kSpeedForwardBack = 4.0f; // looking
static constexpr float kSpeedStrafe = 4.0f;
static constexpr float kCameraSensitivity = 0.007f; static constexpr float kCameraSensitivity = 0.007f;
static constexpr float kMaxDistanceFromOrigin = 10000.0f;
static constexpr float kGravAccel = -9.81f;
static constexpr float kPlayerHeight = 71.0f * 25.4f / 1000.0f;
static constexpr float kMaxStairHeight = 0.2f;
static constexpr float kPlayerCollisionRadius = 0.2f;
static constexpr float kMaxPitch = glm::pi<float>(); static constexpr float kMaxPitch = glm::pi<float>();
static constexpr float kMinPitch = 0.0f; static constexpr float kMinPitch = 0.0f;
// moving
static constexpr float kSpeedForwardBack = 4.0f;
static constexpr float kSpeedStrafe = 4.0f;
static constexpr float kSprintMultiplier = 2.0f;
// collision
static constexpr float kPlayerHeight = 2.0f; // 71.0f * 25.4f / 1000.0f;
static constexpr float kPlayerCollisionRadius = 0.2f; // this should be greater than z_near
static constexpr float kMaxStairHeight = 0.2f;
static constexpr size_t kNumHorizontalRays = 20;
static constexpr float kGravAccel = -9.81f;
static constexpr float kMaxDistanceFromOrigin = 1000.0f;
float yaw = 0.0f; float yaw = 0.0f;
float pitch = glm::half_pi<float>(); float pitch = glm::half_pi<float>();
glm::vec3 vel{0.0f, 0.0f, 0.0f}; glm::vec3 vel{0.0f, 0.0f, 0.0f};
bool grounded = false; bool grounded = false;
bool was_grounded = false;
std::vector<engine::Line> perm_lines{}; // raycasting lines
}; };
class CameraControllerSystem class CameraControllerSystem : public engine::System {
: public engine::System {
public: public:
CameraControllerSystem(engine::Scene* scene); CameraControllerSystem(engine::Scene* scene);

View File

@ -81,32 +81,15 @@ void PlayGame(GameSettings settings)
* matrix */ * matrix */
auto camera_transform = main_scene->GetComponent<engine::TransformComponent>(camera); auto camera_transform = main_scene->GetComponent<engine::TransformComponent>(camera);
camera_transform->position = {-5.0f, -10.0f, 4.0f}; camera_transform->position = {0.0f, 0.0f, 100.0f};
main_scene->RegisterComponent<CameraControllerComponent>(); main_scene->RegisterComponent<CameraControllerComponent>();
main_scene->RegisterSystem<CameraControllerSystem>(); main_scene->RegisterSystem<CameraControllerSystem>();
main_scene->AddComponent<CameraControllerComponent>(camera); main_scene->AddComponent<CameraControllerComponent>(camera);
/* floor */ /* floor */
engine::Entity floor = main_scene->CreateEntity("floor", 0, glm::vec3{-50.0f, -50.0f, 0.0f}); engine::Entity floor = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/floor.glb"));
auto floor_renderable = main_scene->AddComponent<engine::MeshRenderableComponent>(floor); main_scene->GetComponent<engine::MeshRenderableComponent>(main_scene->GetEntity("Cube", floor))->visible = false;
floor_renderable->mesh = GenCuboidMesh(app.renderer()->GetDevice(), 100.0f, 100.0f, 0.1f, 50.0f);
floor_renderable->material = std::make_unique<engine::Material>(app.renderer(), app.GetResource<engine::Shader>("builtin.fancy"));
std::shared_ptr<engine::Texture> floor_albedo =
engine::LoadTextureFromFile(app.GetResourcePath("textures/bricks-mortar-albedo.png"), engine::gfx::SamplerInfo{}, app.renderer());
std::shared_ptr<engine::Texture> floor_normal =
engine::LoadTextureFromFile(app.GetResourcePath("textures/bricks-mortar-normal.png"), engine::gfx::SamplerInfo{}, app.renderer(), false);
std::shared_ptr<engine::Texture> floor_mr =
engine::LoadTextureFromFile(app.GetResourcePath("textures/bricks-mortar-roughness.png"), engine::gfx::SamplerInfo{}, app.renderer(), false);
floor_renderable->material->SetAlbedoTexture(floor_albedo);
floor_renderable->material->SetNormalTexture(floor_normal);
floor_renderable->material->SetMetallicRoughnessTexture(floor_mr);
floor_renderable->material->SetOcclusionTexture(app.GetResource<engine::Texture>("builtin.white"));
floor_renderable->visible = true ;
auto floor_col = main_scene->AddComponent<engine::ColliderComponent>(floor);
floor_col->aabb.min = glm::vec3{0.0f, 0.0f, 0.0f};
floor_col->aabb.max = glm::vec3{100.0f, 100.0f, 0.1f };
engine::Entity monke = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/monke.glb")); engine::Entity monke = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/monke.glb"));
main_scene->GetComponent<engine::TransformComponent>(monke)->position.y += 10.0f; main_scene->GetComponent<engine::TransformComponent>(monke)->position.y += 10.0f;
@ -116,23 +99,13 @@ void PlayGame(GameSettings settings)
//main_scene->GetComponent<engine::TransformComponent>(bottle)->position.x += 25.0f; //main_scene->GetComponent<engine::TransformComponent>(bottle)->position.x += 25.0f;
//main_scene->GetComponent<engine::TransformComponent>(bottle)->position.z += 5.0f; //main_scene->GetComponent<engine::TransformComponent>(bottle)->position.z += 5.0f;
/* skybox */
engine::Entity skybox = main_scene->CreateEntity("skybox");
auto skybox_transform = main_scene->GetComponent<engine::TransformComponent>(skybox);
skybox_transform->is_static = true;
skybox_transform->position = { -5.0f, -5.0f, -5.0f };
auto skybox_renderable = main_scene->AddComponent<engine::MeshRenderableComponent>(skybox);
skybox_renderable->mesh = GenCuboidMesh(app.renderer()->GetDevice(), 10.0f, 10.0f, 10.0f, 1.0f, true);
skybox_renderable->material = std::make_unique<engine::Material>(app.renderer(), app.GetResource<engine::Shader>("builtin.skybox"));
skybox_renderable->material->SetAlbedoTexture(app.GetResource<engine::Texture>("builtin.black"));
engine::Entity helmet = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/DamagedHelmet.glb")); engine::Entity helmet = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/DamagedHelmet.glb"));
main_scene->GetPosition(helmet) += glm::vec3{5.0f, 5.0f, 1.0f}; main_scene->GetPosition(helmet) += glm::vec3{5.0f, 5.0f, 1.0f};
main_scene->GetScale(helmet) *= 3.0f; main_scene->GetScale(helmet) *= 3.0f;
engine::Entity toycar = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/ToyCar.glb")); engine::Entity toycar = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/ToyCar.glb"));
main_scene->GetScale(toycar) *= 100.0f; main_scene->GetScale(toycar) *= 150.0f;
main_scene->GetPosition(toycar).z += 1.8f; main_scene->GetPosition(toycar).z -= 0.07f;
engine::Entity stairs = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/stairs.glb")); engine::Entity stairs = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/stairs.glb"));
main_scene->GetPosition(stairs) += glm::vec3{-8.0f, -5.0f, 0.1f}; main_scene->GetPosition(stairs) += glm::vec3{-8.0f, -5.0f, 0.1f};