This commit is contained in:
xsl
2025-09-04 10:54:47 +08:00
commit 6bc8f61b18
1808 changed files with 208268 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "component.h"
#include <algorithm>
#include "node.h"
namespace vkb
{
namespace sg
{
Component::Component(const std::string &name) :
name{name}
{}
const std::string &Component::get_name() const
{
return name;
}
} // namespace sg
} // namespace vkb
+51
View File
@@ -0,0 +1,51 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeindex>
#include <vector>
namespace vkb
{
namespace sg
{
class Node;
/// @brief A generic class which can be used by nodes.
class Component
{
public:
Component() = default;
Component(const std::string &name);
Component(Component &&other) = default;
virtual ~Component() = default;
const std::string &get_name() const;
virtual std::type_index get_type() = 0;
private:
std::string name;
};
} // namespace sg
} // namespace vkb
+111
View File
@@ -0,0 +1,111 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "aabb.h"
#include "core/util/logging.hpp"
namespace vkb
{
namespace sg
{
AABB::AABB()
{
reset();
}
AABB::AABB(const glm::vec3 &min, const glm::vec3 &max) :
min{min},
max{max}
{
}
std::type_index AABB::get_type()
{
return typeid(AABB);
}
void AABB::update(const glm::vec3 &point)
{
min = glm::min(min, point);
max = glm::max(max, point);
}
void AABB::update(const std::vector<glm::vec3> &vertex_data, const std::vector<uint16_t> &index_data)
{
// Check if submesh is indexed
if (index_data.size() > 0)
{
// Update bounding box for each indexed vertex
for (size_t index_id = 0; index_id < index_data.size(); index_id++)
{
update(vertex_data[index_data[index_id]]);
}
}
else
{
// Update bounding box for each vertex
for (size_t vertex_id = 0; vertex_id < vertex_data.size(); vertex_id++)
{
update(vertex_data[vertex_id]);
}
}
}
void AABB::transform(glm::mat4 &transform)
{
min = max = glm::vec4(min, 1.0f) * transform;
// Update bounding box for the remaining 7 corners of the box
update(glm::vec4(min.x, min.y, max.z, 1.0f) * transform);
update(glm::vec4(min.x, max.y, min.z, 1.0f) * transform);
update(glm::vec4(min.x, max.y, max.z, 1.0f) * transform);
update(glm::vec4(max.x, min.y, min.z, 1.0f) * transform);
update(glm::vec4(max.x, min.y, max.z, 1.0f) * transform);
update(glm::vec4(max.x, max.y, min.z, 1.0f) * transform);
update(glm::vec4(max, 1.0f) * transform);
}
glm::vec3 AABB::get_scale() const
{
return (max - min);
}
glm::vec3 AABB::get_center() const
{
return (min + max) * 0.5f;
}
glm::vec3 AABB::get_min() const
{
return min;
}
glm::vec3 AABB::get_max() const
{
return max;
}
void AABB::reset()
{
min = std::numeric_limits<glm::vec3>::max();
max = std::numeric_limits<glm::vec3>::min();
}
} // namespace sg
} // namespace vkb
+104
View File
@@ -0,0 +1,104 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include "scene_graph/component.h"
#include "scene_graph/components/sub_mesh.h"
namespace vkb
{
namespace sg
{
/**
* @brief Axis Aligned Bounding Box
*/
class AABB : public Component
{
public:
AABB();
AABB(const glm::vec3 &min, const glm::vec3 &max);
virtual ~AABB() = default;
virtual std::type_index get_type() override;
/**
* @brief Update the bounding box based on the given vertex position
* @param point The 3D position of a point
*/
void update(const glm::vec3 &point);
/**
* @brief Update the bounding box based on the given submesh vertices
* @param vertex_data The position vertex data
* @param index_data The index vertex data
*/
void update(const std::vector<glm::vec3> &vertex_data, const std::vector<uint16_t> &index_data);
/**
* @brief Apply a given matrix transformation to the bounding box
* @param transform The matrix transform to apply
*/
void transform(glm::mat4 &transform);
/**
* @brief Scale vector of the bounding box
* @return vector in 3D space
*/
glm::vec3 get_scale() const;
/**
* @brief Center position of the bounding box
* @return vector in 3D space
*/
glm::vec3 get_center() const;
/**
* @brief Minimum position of the bounding box
* @return vector in 3D space
*/
glm::vec3 get_min() const;
/**
* @brief Maximum position of the bounding box
* @return vector in 3D space
*/
glm::vec3 get_max() const;
/**
* @brief Resets the min and max position coordinates
*/
void reset();
private:
glm::vec3 min;
glm::vec3 max;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,67 @@
/* Copyright (c) 2019-2020, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "camera.h"
#include "scene_graph/components/transform.h"
#include "scene_graph/node.h"
namespace vkb
{
namespace sg
{
Camera::Camera(const std::string &name) :
Component{name}
{}
std::type_index Camera::get_type()
{
return typeid(Camera);
}
glm::mat4 Camera::get_view()
{
if (!node)
{
throw std::runtime_error{"Camera component is not attached to a node"};
}
auto &transform = node->get_component<Transform>();
return glm::inverse(transform.get_world_matrix());
}
void Camera::set_node(Node &n)
{
node = &n;
}
Node *Camera::get_node()
{
return node;
}
const glm::mat4 Camera::get_pre_rotation()
{
return pre_rotation;
}
void Camera::set_pre_rotation(const glm::mat4 &pr)
{
pre_rotation = pr;
}
} // namespace sg
} // namespace vkb
+63
View File
@@ -0,0 +1,63 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include "common/helpers.h"
#include "scene_graph/component.h"
namespace vkb
{
namespace sg
{
class Camera : public Component
{
public:
Camera(const std::string &name);
virtual ~Camera() = default;
virtual std::type_index get_type() override;
virtual glm::mat4 get_projection() = 0;
glm::mat4 get_view();
void set_node(Node &node);
Node *get_node();
const glm::mat4 get_pre_rotation();
void set_pre_rotation(const glm::mat4 &pre_rotation);
private:
Node *node{nullptr};
glm::mat4 pre_rotation{1.0f};
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,379 @@
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hpp_image.h"
#include "common/hpp_utils.h"
#include "filesystem/legacy.h"
#include "scene_graph/components/image/astc.h"
#include "scene_graph/components/image/ktx.h"
#include "scene_graph/components/image/stb.h"
#include <stb_image_resize.h>
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_format_traits.hpp>
namespace vkb
{
namespace scene_graph
{
namespace components
{
bool is_astc(const vk::Format format)
{
return strncmp(vk::compressionScheme(format), "ASTC", 4) == 0;
}
HPPImage::HPPImage(const std::string &name, std::vector<uint8_t> &&d, std::vector<vkb::scene_graph::components::HPPMipmap> &&m) :
Component{name},
data{std::move(d)},
format{vk::Format::eR8G8B8A8Unorm},
mipmaps{std::move(m)}
{
update_hash();
}
std::unique_ptr<vkb::scene_graph::components::HPPImage> HPPImage::load(const std::string &name, const std::string &uri,
ContentType content_type)
{
std::unique_ptr<vkb::scene_graph::components::HPPImage> image{nullptr};
auto data = fs::read_asset(uri);
// Get extension
auto extension = get_extension(uri);
// the derived classes Stb, Astc, and Ktx are not transcoded (yet), so we need some more complex casting here...
if (extension == "png" || extension == "jpg")
{
image = std::unique_ptr<vkb::scene_graph::components::HPPImage>(reinterpret_cast<vkb::scene_graph::components::HPPImage *>(
std::make_unique<vkb::sg::Stb>(name, data, static_cast<vkb::sg::Image::ContentType>(content_type)).release()));
}
else if (extension == "astc")
{
image = std::unique_ptr<vkb::scene_graph::components::HPPImage>(
reinterpret_cast<vkb::scene_graph::components::HPPImage *>(std::make_unique<vkb::sg::Astc>(name, data).release()));
}
else if ((extension == "ktx") || (extension == "ktx2"))
{
image = std::unique_ptr<vkb::scene_graph::components::HPPImage>(reinterpret_cast<vkb::scene_graph::components::HPPImage *>(
std::make_unique<vkb::sg::Ktx>(name, data, static_cast<vkb::sg::Image::ContentType>(content_type)).release()));
}
return image;
}
std::type_index HPPImage::get_type()
{
return typeid(HPPImage);
}
void HPPImage::clear_data()
{
data.clear();
data.shrink_to_fit();
}
void HPPImage::coerce_format_to_srgb()
{
// When the color-space of a loaded image is unknown (from KTX1 for example) we
// may want to assume that the loaded data is in sRGB format (since it usually is).
// In those cases, this helper will get called which will force an existing unorm
// format to become an srgb format where one exists. If none exist, the format will
// remain unmodified.
switch (format)
{
case vk::Format::eR8Unorm:
format = vk::Format::eR8Srgb;
break;
case vk::Format::eR8G8Unorm:
format = vk::Format::eR8G8Srgb;
break;
case vk::Format::eR8G8B8Unorm:
format = vk::Format::eR8G8B8Srgb;
break;
case vk::Format::eB8G8R8Unorm:
format = vk::Format::eB8G8R8Srgb;
break;
case vk::Format::eR8G8B8A8Unorm:
format = vk::Format::eR8G8B8A8Srgb;
break;
case vk::Format::eB8G8R8A8Unorm:
format = vk::Format::eB8G8R8A8Srgb;
break;
case vk::Format::eA8B8G8R8UnormPack32:
format = vk::Format::eA8B8G8R8SrgbPack32;
break;
case vk::Format::eBc1RgbUnormBlock:
format = vk::Format::eBc1RgbSrgbBlock;
break;
case vk::Format::eBc1RgbaUnormBlock:
format = vk::Format::eBc1RgbaSrgbBlock;
break;
case vk::Format::eBc2UnormBlock:
format = vk::Format::eBc2SrgbBlock;
break;
case vk::Format::eBc3UnormBlock:
format = vk::Format::eBc3SrgbBlock;
break;
case vk::Format::eBc7UnormBlock:
format = vk::Format::eBc7SrgbBlock;
break;
case vk::Format::eEtc2R8G8B8UnormBlock:
format = vk::Format::eEtc2R8G8B8SrgbBlock;
break;
case vk::Format::eEtc2R8G8B8A1UnormBlock:
format = vk::Format::eEtc2R8G8B8A1SrgbBlock;
break;
case vk::Format::eEtc2R8G8B8A8UnormBlock:
format = vk::Format::eEtc2R8G8B8A8SrgbBlock;
break;
case vk::Format::eAstc4x4UnormBlock:
format = vk::Format::eAstc4x4SrgbBlock;
break;
case vk::Format::eAstc5x4UnormBlock:
format = vk::Format::eAstc5x4SrgbBlock;
break;
case vk::Format::eAstc5x5UnormBlock:
format = vk::Format::eAstc5x5SrgbBlock;
break;
case vk::Format::eAstc6x5UnormBlock:
format = vk::Format::eAstc6x5SrgbBlock;
break;
case vk::Format::eAstc6x6UnormBlock:
format = vk::Format::eAstc6x6SrgbBlock;
break;
case vk::Format::eAstc8x5UnormBlock:
format = vk::Format::eAstc8x5SrgbBlock;
break;
case vk::Format::eAstc8x6UnormBlock:
format = vk::Format::eAstc8x6SrgbBlock;
break;
case vk::Format::eAstc8x8UnormBlock:
format = vk::Format::eAstc8x8SrgbBlock;
break;
case vk::Format::eAstc10x5UnormBlock:
format = vk::Format::eAstc10x5SrgbBlock;
break;
case vk::Format::eAstc10x6UnormBlock:
format = vk::Format::eAstc10x6SrgbBlock;
break;
case vk::Format::eAstc10x8UnormBlock:
format = vk::Format::eAstc10x8SrgbBlock;
break;
case vk::Format::eAstc10x10UnormBlock:
format = vk::Format::eAstc10x10SrgbBlock;
break;
case vk::Format::eAstc12x10UnormBlock:
format = vk::Format::eAstc12x10SrgbBlock;
break;
case vk::Format::eAstc12x12UnormBlock:
format = vk::Format::eAstc12x12SrgbBlock;
break;
case vk::Format::ePvrtc12BppUnormBlockIMG:
format = vk::Format::ePvrtc12BppSrgbBlockIMG;
break;
case vk::Format::ePvrtc14BppUnormBlockIMG:
format = vk::Format::ePvrtc14BppSrgbBlockIMG;
break;
case vk::Format::ePvrtc22BppUnormBlockIMG:
format = vk::Format::ePvrtc22BppSrgbBlockIMG;
break;
case vk::Format::ePvrtc24BppUnormBlockIMG:
format = vk::Format::ePvrtc24BppSrgbBlockIMG;
break;
default:
break;
}
}
void HPPImage::create_vk_image(vkb::core::DeviceCpp &device, vk::ImageViewType image_view_type, vk::ImageCreateFlags flags)
{
assert(!vk_image && !vk_image_view && "Vulkan HPPImage already constructed");
vk_image = std::make_unique<vkb::core::HPPImage>(device,
get_extent(),
format,
vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst,
VMA_MEMORY_USAGE_GPU_ONLY,
vk::SampleCountFlagBits::e1,
to_u32(mipmaps.size()),
layers,
vk::ImageTiling::eOptimal,
flags);
vk_image->set_debug_name(get_name());
vk_image_view = std::make_unique<vkb::core::HPPImageView>(*vk_image, image_view_type);
vk_image_view->set_debug_name("View on " + get_name());
}
void HPPImage::generate_mipmaps()
{
assert(mipmaps.size() == 1 && "Mipmaps already generated");
if (mipmaps.size() > 1)
{
return; // Do not generate again
}
auto extent = get_extent();
auto next_width = std::max<uint32_t>(1u, extent.width / 2);
auto next_height = std::max<uint32_t>(1u, extent.height / 2);
auto channels = 4;
auto next_size = next_width * next_height * channels;
while (true)
{
// Make space for next mipmap
auto old_size = to_u32(data.size());
data.resize(old_size + next_size);
auto &prev_mipmap = mipmaps.back();
// Update mipmaps
vkb::scene_graph::components::HPPMipmap next_mipmap{};
next_mipmap.level = prev_mipmap.level + 1;
next_mipmap.offset = old_size;
next_mipmap.extent = vk::Extent3D{next_width, next_height, 1u};
// Fill next mipmap memory
stbir_resize_uint8(data.data() + prev_mipmap.offset, prev_mipmap.extent.width, prev_mipmap.extent.height, 0,
data.data() + next_mipmap.offset, next_mipmap.extent.width, next_mipmap.extent.height, 0, channels);
mipmaps.emplace_back(std::move(next_mipmap));
// Next mipmap values
next_width = std::max<uint32_t>(1u, next_width / 2);
next_height = std::max<uint32_t>(1u, next_height / 2);
next_size = next_width * next_height * channels;
if (next_width == 1 && next_height == 1)
{
break;
}
}
}
void HPPImage::update_hash()
{
data_hash = vkb::calculate_hash(data);
}
void HPPImage::update_hash(size_t hash)
{
data_hash = hash;
}
const std::vector<uint8_t> &HPPImage::get_data() const
{
return data;
}
const vk::Extent3D &HPPImage::get_extent() const
{
assert(!mipmaps.empty());
return mipmaps[0].extent;
}
vk::Format HPPImage::get_format() const
{
return format;
}
const uint32_t HPPImage::get_layers() const
{
return layers;
}
const std::vector<vkb::scene_graph::components::HPPMipmap> &HPPImage::get_mipmaps() const
{
return mipmaps;
}
const std::vector<std::vector<vk::DeviceSize>> &HPPImage::get_offsets() const
{
return offsets;
}
const vkb::core::HPPImage &HPPImage::get_vk_image() const
{
assert(vk_image && "Vulkan HPPImage was not created");
return *vk_image;
}
const vkb::core::HPPImageView &HPPImage::get_vk_image_view() const
{
assert(vk_image_view && "Vulkan HPPImage view was not created");
return *vk_image_view;
}
vkb::scene_graph::components::HPPMipmap &HPPImage::get_mipmap(const size_t index)
{
assert(index < mipmaps.size());
return mipmaps[index];
}
std::vector<uint8_t> &HPPImage::get_mut_data()
{
return data;
}
std::vector<vkb::scene_graph::components::HPPMipmap> &HPPImage::get_mut_mipmaps()
{
return mipmaps;
}
void HPPImage::set_data(const uint8_t *raw_data, size_t size)
{
assert(data.empty() && "HPPImage data already set");
data = {raw_data, raw_data + size};
update_hash();
}
void HPPImage::set_depth(const uint32_t depth)
{
assert(!mipmaps.empty());
mipmaps[0].extent.depth = depth;
}
void HPPImage::set_format(const vk::Format f)
{
format = f;
}
void HPPImage::set_height(const uint32_t height)
{
assert(!mipmaps.empty());
mipmaps[0].extent.height = height;
}
void HPPImage::set_layers(uint32_t l)
{
layers = l;
}
void HPPImage::set_offsets(const std::vector<std::vector<vk::DeviceSize>> &o)
{
offsets = o;
}
void HPPImage::set_width(const uint32_t width)
{
assert(!mipmaps.empty());
mipmaps[0].extent.width = width;
}
} // namespace components
} // namespace scene_graph
} // namespace vkb
@@ -0,0 +1,117 @@
/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/vk_common.h"
#include "core/hpp_image.h"
#include "scene_graph/component.h"
#include <vulkan/vulkan.hpp>
namespace vkb
{
namespace core
{
template <vkb::BindingType bindingType>
class Device;
using DeviceCpp = Device<vkb::BindingType::Cpp>;
} // namespace core
namespace scene_graph::components
{
/**
* @param format Vulkan format
* @return Whether the vulkan format is ASTC
*/
bool is_astc(vk::Format format);
/**
* @brief Mipmap information
*/
struct HPPMipmap
{
uint32_t level = 0; /// Mipmap level
uint32_t offset = 0; /// Byte offset used for uploading
vk::Extent3D extent = {0, 0, 0}; /// Width depth and height of the mipmap
};
class HPPImage : public vkb::sg::Component
{
public:
HPPImage(const std::string &name, std::vector<uint8_t> &&data = {}, std::vector<vkb::scene_graph::components::HPPMipmap> &&mipmaps = {{}});
virtual ~HPPImage() = default;
public:
/**
* @brief Type of content held in image.
* This helps to steer the image loaders when deciding what the format should be.
* Some image containers don't know whether the data they contain is sRGB or not.
* Since most applications save color images in sRGB, knowing that an image
* contains color data helps us to better guess its format when unknown.
*/
enum ContentType
{
Unknown,
Color,
Other
};
static std::unique_ptr<vkb::scene_graph::components::HPPImage> load(const std::string &name, const std::string &uri, ContentType content_type);
// from Component
virtual std::type_index get_type() override;
void clear_data();
void coerce_format_to_srgb();
void create_vk_image(vkb::core::DeviceCpp &device, vk::ImageViewType image_view_type = vk::ImageViewType::e2D, vk::ImageCreateFlags flags = {});
void generate_mipmaps();
const std::vector<uint8_t> &get_data() const;
const vk::Extent3D &get_extent() const;
vk::Format get_format() const;
const uint32_t get_layers() const;
const std::vector<vkb::scene_graph::components::HPPMipmap> &get_mipmaps() const;
const std::vector<std::vector<vk::DeviceSize>> &get_offsets() const;
const vkb::core::HPPImage &get_vk_image() const;
const vkb::core::HPPImageView &get_vk_image_view() const;
void update_hash(size_t data_hash);
void update_hash();
protected:
vkb::scene_graph::components::HPPMipmap &get_mipmap(size_t index);
std::vector<uint8_t> &get_mut_data();
std::vector<vkb::scene_graph::components::HPPMipmap> &get_mut_mipmaps();
void set_data(const uint8_t *raw_data, size_t size);
void set_depth(uint32_t depth);
void set_format(vk::Format format);
void set_height(uint32_t height);
void set_layers(uint32_t layers);
void set_offsets(const std::vector<std::vector<vk::DeviceSize>> &offsets);
void set_width(uint32_t width);
private:
std::vector<uint8_t> data;
size_t data_hash{0};
vk::Format format = vk::Format::eUndefined;
uint32_t layers = 1;
std::vector<vkb::scene_graph::components::HPPMipmap> mipmaps{{}};
std::vector<std::vector<vk::DeviceSize>> offsets; // Offsets stored like offsets[array_layer][mipmap_layer]
std::unique_ptr<vkb::core::HPPImage> vk_image;
std::unique_ptr<vkb::core::HPPImageView> vk_image_view;
};
} // namespace scene_graph::components
} // namespace vkb
@@ -0,0 +1,44 @@
/* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "hpp_texture.h"
#include "material.h"
namespace vkb
{
namespace scene_graph
{
namespace components
{
/**
* @brief facade class around vkb::sg::Material, providing a vulkan.hpp-based interface
*
* See vkb::sb::Material for documentation
*/
class HPPMaterial : private vkb::sg::Material
{
public:
std::unordered_map<std::string, vkb::scene_graph::components::HPPTexture *> const &get_textures() const
{
return reinterpret_cast<std::unordered_map<std::string, vkb::scene_graph::components::HPPTexture *> const &>(vkb::sg::Material::textures);
}
};
} // namespace components
} // namespace scene_graph
} // namespace vkb
@@ -0,0 +1,44 @@
/* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "hpp_sub_mesh.h"
#include "mesh.h"
namespace vkb
{
namespace scene_graph
{
namespace components
{
/**
* @brief facade class around vkb::sg::Mesh, providing a vulkan.hpp-based interface
*
* See vkb::sb::Mesh for documentation
*/
class HPPMesh : private vkb::sg::Mesh
{
public:
std::vector<vkb::scene_graph::components::HPPSubMesh *> const &get_submeshes() const
{
return reinterpret_cast<std::vector<vkb::scene_graph::components::HPPSubMesh *> const &>(vkb::sg::Mesh::get_submeshes());
}
};
} // namespace components
} // namespace scene_graph
} // namespace vkb
@@ -0,0 +1,61 @@
/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <scene_graph/components/hpp_material.h>
#include <scene_graph/components/sub_mesh.h>
namespace vkb
{
namespace scene_graph
{
namespace components
{
/**
* @brief facade class around vkb::sg::SubMesh, providing a vulkan.hpp-based interface
*
* See vkb::sb::SubMeshfor for documentation
*/
class HPPSubMesh : private vkb::sg::SubMesh
{
public:
using vkb::sg::SubMesh::vertex_indices;
vkb::core::BufferCpp const &get_index_buffer() const
{
return reinterpret_cast<vkb::core::BufferCpp const &>(*vkb::sg::SubMesh::index_buffer);
}
vk::IndexType get_index_type() const
{
return static_cast<vk::IndexType>(vkb::sg::SubMesh::index_type);
}
const HPPMaterial *get_material() const
{
return reinterpret_cast<vkb::scene_graph::components::HPPMaterial const *>(vkb::sg::SubMesh::get_material());
}
vkb::core::BufferCpp const &get_vertex_buffer(std::string const &name) const
{
return reinterpret_cast<vkb::core::BufferCpp const &>(vkb::sg::SubMesh::vertex_buffers.at(name));
}
};
} // namespace components
} // namespace scene_graph
} // namespace vkb
@@ -0,0 +1,49 @@
/* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "hpp_image.h"
#include "texture.h"
namespace vkb
{
namespace scene_graph
{
namespace components
{
/**
* @brief facade class around vkb::sg::Texture, providing a vulkan.hpp-based interface
*
* See vkb::sb::Texture for documentation
*/
class HPPTexture : private vkb::sg::Texture
{
public:
vkb::scene_graph::components::HPPImage *get_image()
{
return reinterpret_cast<vkb::scene_graph::components::HPPImage *>(vkb::sg::Texture::get_image());
}
void set_image(vkb::scene_graph::components::HPPImage &image)
{
vkb::sg::Texture::set_image(reinterpret_cast<vkb::sg::Image &>(image));
}
};
} // namespace components
} // namespace scene_graph
} // namespace vkb
+408
View File
@@ -0,0 +1,408 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "image.h"
#include <cstddef>
#include <mutex>
#include "common/error.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include <stb_image_resize.h>
#include "common/utils.h"
#include "filesystem/legacy.h"
#include "scene_graph/components/image/astc.h"
#include "scene_graph/components/image/ktx.h"
#include "scene_graph/components/image/stb.h"
namespace vkb
{
namespace sg
{
bool is_astc(const VkFormat format)
{
return (format == VK_FORMAT_ASTC_4x4_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_4x4_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_5x4_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_5x4_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_5x5_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_5x5_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_6x5_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_6x5_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_6x6_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_6x6_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_8x5_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_8x5_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_8x6_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_8x6_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_8x8_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_8x8_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_10x5_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_10x5_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_10x6_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_10x6_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_10x8_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_10x8_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_10x10_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_10x10_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_12x10_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_12x10_SRGB_BLOCK ||
format == VK_FORMAT_ASTC_12x12_UNORM_BLOCK ||
format == VK_FORMAT_ASTC_12x12_SRGB_BLOCK);
}
// When the color-space of a loaded image is unknown (from KTX1 for example) we
// may want to assume that the loaded data is in sRGB format (since it usually is).
// In those cases, this helper will get called which will force an existing unorm
// format to become an srgb format where one exists. If none exist, the format will
// remain unmodified.
static VkFormat maybe_coerce_to_srgb(VkFormat fmt)
{
switch (fmt)
{
case VK_FORMAT_R8_UNORM:
return VK_FORMAT_R8_SRGB;
case VK_FORMAT_R8G8_UNORM:
return VK_FORMAT_R8G8_SRGB;
case VK_FORMAT_R8G8B8_UNORM:
return VK_FORMAT_R8G8B8_SRGB;
case VK_FORMAT_B8G8R8_UNORM:
return VK_FORMAT_B8G8R8_SRGB;
case VK_FORMAT_R8G8B8A8_UNORM:
return VK_FORMAT_R8G8B8A8_SRGB;
case VK_FORMAT_B8G8R8A8_UNORM:
return VK_FORMAT_B8G8R8A8_SRGB;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
return VK_FORMAT_A8B8G8R8_SRGB_PACK32;
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
return VK_FORMAT_BC1_RGB_SRGB_BLOCK;
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
return VK_FORMAT_BC1_RGBA_SRGB_BLOCK;
case VK_FORMAT_BC2_UNORM_BLOCK:
return VK_FORMAT_BC2_SRGB_BLOCK;
case VK_FORMAT_BC3_UNORM_BLOCK:
return VK_FORMAT_BC3_SRGB_BLOCK;
case VK_FORMAT_BC7_UNORM_BLOCK:
return VK_FORMAT_BC7_SRGB_BLOCK;
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK;
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
return VK_FORMAT_ASTC_4x4_SRGB_BLOCK;
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
return VK_FORMAT_ASTC_5x4_SRGB_BLOCK;
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
return VK_FORMAT_ASTC_5x5_SRGB_BLOCK;
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
return VK_FORMAT_ASTC_6x5_SRGB_BLOCK;
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
return VK_FORMAT_ASTC_6x6_SRGB_BLOCK;
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
return VK_FORMAT_ASTC_8x5_SRGB_BLOCK;
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
return VK_FORMAT_ASTC_8x6_SRGB_BLOCK;
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
return VK_FORMAT_ASTC_8x8_SRGB_BLOCK;
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
return VK_FORMAT_ASTC_10x5_SRGB_BLOCK;
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
return VK_FORMAT_ASTC_10x6_SRGB_BLOCK;
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
return VK_FORMAT_ASTC_10x8_SRGB_BLOCK;
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
return VK_FORMAT_ASTC_10x10_SRGB_BLOCK;
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
return VK_FORMAT_ASTC_12x10_SRGB_BLOCK;
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
return VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
return VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG;
case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
return VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG;
default:
return fmt;
}
}
Image::Image(const std::string &name, std::vector<uint8_t> &&d, std::vector<Mipmap> &&m) :
Component{name},
data{std::move(d)},
format{VK_FORMAT_R8G8B8A8_UNORM},
mipmaps{std::move(m)}
{
update_hash();
}
std::type_index Image::get_type()
{
return typeid(Image);
}
const std::vector<uint8_t> &Image::get_data() const
{
return data;
}
size_t Image::get_data_hash() const
{
return data_hash;
}
void Image::clear_data()
{
data.clear();
data.shrink_to_fit();
}
VkFormat Image::get_format() const
{
return format;
}
const VkExtent3D &Image::get_extent() const
{
assert(!mipmaps.empty());
return mipmaps[0].extent;
}
const uint32_t Image::get_layers() const
{
return layers;
}
const std::vector<Mipmap> &Image::get_mipmaps() const
{
return mipmaps;
}
const std::vector<std::vector<VkDeviceSize>> &Image::get_offsets() const
{
return offsets;
}
void Image::create_vk_image(vkb::core::DeviceC &device, VkImageViewType image_view_type, VkImageCreateFlags flags)
{
assert(!vk_image && !vk_image_view && "Vulkan image already constructed");
vk_image = std::make_unique<core::Image>(device,
get_extent(),
format,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VMA_MEMORY_USAGE_GPU_ONLY,
VK_SAMPLE_COUNT_1_BIT,
to_u32(mipmaps.size()),
layers,
VK_IMAGE_TILING_OPTIMAL,
flags);
vk_image->set_debug_name(get_name());
vk_image_view = std::make_unique<core::ImageView>(*vk_image, image_view_type);
vk_image_view->set_debug_name("View on " + get_name());
}
const core::Image &Image::get_vk_image() const
{
assert(vk_image && "Vulkan image was not created");
return *vk_image;
}
const core::ImageView &Image::get_vk_image_view() const
{
assert(vk_image_view && "Vulkan image view was not created");
return *vk_image_view;
}
Mipmap &Image::get_mipmap(const size_t index)
{
assert(index < mipmaps.size());
return mipmaps[index];
}
// Note that this function returns the required size for ALL mip levels, *including* the base level.
uint32_t get_required_mipmaps_size(const VkExtent3D &extent)
{
constexpr uint32_t channels = 4;
auto width = std::max<uint32_t>(1, extent.width);
auto height = std::max<uint32_t>(1, extent.height);
auto size = width * height * channels;
auto result = size;
while (size != channels)
{
width = std::max<uint32_t>(1u, width >> 1);
height = std::max<uint32_t>(1u, height >> 1);
size = width * height * channels;
result += size;
}
return result;
}
void Image::generate_mipmaps()
{
assert(mipmaps.size() == 1 && "Mipmaps already generated");
if (mipmaps.size() > 1)
{
return; // Do not generate again
}
auto extent = get_extent();
auto next_width = std::max<uint32_t>(1u, extent.width / 2);
auto next_height = std::max<uint32_t>(1u, extent.height / 2);
auto channels = 4;
auto next_size = next_width * next_height * channels;
// Allocate for all the mips at once. The function returns the total size needed for the
// existing base mip as well as all the mips that will be generated.
data.reserve(get_required_mipmaps_size(extent));
while (true)
{
// Make space for next mipmap
auto old_size = to_u32(data.size());
data.resize(old_size + next_size);
auto &prev_mipmap = mipmaps.back();
// Update mipmaps
Mipmap next_mipmap{};
next_mipmap.level = prev_mipmap.level + 1;
next_mipmap.offset = old_size;
next_mipmap.extent = {next_width, next_height, 1u};
// Fill next mipmap memory
stbir_resize_uint8(data.data() + prev_mipmap.offset, prev_mipmap.extent.width, prev_mipmap.extent.height, 0,
data.data() + next_mipmap.offset, next_mipmap.extent.width, next_mipmap.extent.height, 0, channels);
mipmaps.emplace_back(std::move(next_mipmap));
// Next mipmap values
next_width = std::max<uint32_t>(1u, next_width / 2);
next_height = std::max<uint32_t>(1u, next_height / 2);
next_size = next_width * next_height * channels;
if (next_width == 1 && next_height == 1)
{
break;
}
}
}
std::vector<Mipmap> &Image::get_mut_mipmaps()
{
return mipmaps;
}
std::vector<uint8_t> &Image::get_mut_data()
{
return data;
}
void Image::update_hash()
{
data_hash = vkb::calculate_hash(data);
}
void Image::update_hash(size_t hash)
{
data_hash = hash;
}
void Image::set_data(const uint8_t *raw_data, size_t size)
{
assert(data.empty() && "Image data already set");
data = {raw_data, raw_data + size};
update_hash();
}
void Image::set_format(const VkFormat f)
{
format = f;
}
void Image::set_width(const uint32_t width)
{
assert(!mipmaps.empty());
mipmaps[0].extent.width = width;
}
void Image::set_height(const uint32_t height)
{
assert(!mipmaps.empty());
mipmaps[0].extent.height = height;
}
void Image::set_depth(const uint32_t depth)
{
assert(!mipmaps.empty());
mipmaps[0].extent.depth = depth;
}
void Image::set_layers(uint32_t l)
{
layers = l;
}
void Image::set_offsets(const std::vector<std::vector<VkDeviceSize>> &o)
{
offsets = o;
}
void Image::coerce_format_to_srgb()
{
format = maybe_coerce_to_srgb(format);
}
std::unique_ptr<Image> Image::load(const std::string &name, const std::string &uri,
ContentType content_type)
{
std::unique_ptr<Image> image{nullptr};
auto data = fs::read_asset(uri);
// Get extension
auto extension = get_extension(uri);
if (extension == "png" || extension == "jpg")
{
image = std::make_unique<Stb>(name, data, content_type);
}
else if (extension == "astc")
{
image = std::make_unique<Astc>(name, data);
}
else if (extension == "ktx")
{
image = std::make_unique<Ktx>(name, data, content_type);
}
else if (extension == "ktx2")
{
image = std::make_unique<Ktx>(name, data, content_type);
}
return image;
}
} // namespace sg
} // namespace vkb
+152
View File
@@ -0,0 +1,152 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include <volk.h>
#include "core/image.h"
#include "core/image_view.h"
#include "scene_graph/component.h"
namespace vkb
{
namespace sg
{
/**
* @param format Vulkan format
* @return Whether the vulkan format is ASTC
*/
bool is_astc(VkFormat format);
/**
* @brief Mipmap information
*/
struct Mipmap
{
/// Mipmap level
uint32_t level = 0;
/// Byte offset used for uploading
uint32_t offset = 0;
/// Width depth and height of the mipmap
VkExtent3D extent = {0, 0, 0};
};
class Image : public Component
{
public:
/**
* @brief Type of content held in image.
* This helps to steer the image loaders when deciding what the format should be.
* Some image containers don't know whether the data they contain is sRGB or not.
* Since most applications save color images in sRGB, knowing that an image
* contains color data helps us to better guess its format when unknown.
*/
enum ContentType
{
Unknown,
Color,
Other
};
Image(const std::string &name, std::vector<uint8_t> &&data = {}, std::vector<Mipmap> &&mipmaps = {{}});
static std::unique_ptr<Image> load(const std::string &name, const std::string &uri, ContentType content_type);
virtual ~Image() = default;
virtual std::type_index get_type() override;
const std::vector<uint8_t> &get_data() const;
size_t get_data_hash() const;
void clear_data();
VkFormat get_format() const;
const VkExtent3D &get_extent() const;
const uint32_t get_layers() const;
const std::vector<Mipmap> &get_mipmaps() const;
const std::vector<std::vector<VkDeviceSize>> &get_offsets() const;
void generate_mipmaps();
void create_vk_image(vkb::core::DeviceC &device, VkImageViewType image_view_type = VK_IMAGE_VIEW_TYPE_2D, VkImageCreateFlags flags = 0);
const core::Image &get_vk_image() const;
const core::ImageView &get_vk_image_view() const;
void coerce_format_to_srgb();
protected:
std::vector<uint8_t> &get_mut_data();
void set_data(const uint8_t *raw_data, size_t size);
void set_format(VkFormat format);
void set_width(uint32_t width);
void set_height(uint32_t height);
void set_depth(uint32_t depth);
void set_layers(uint32_t layers);
void set_offsets(const std::vector<std::vector<VkDeviceSize>> &offsets);
Mipmap &get_mipmap(size_t index);
std::vector<Mipmap> &get_mut_mipmaps();
void update_hash();
void update_hash(size_t data_hash);
private:
std::vector<uint8_t> data;
size_t data_hash{0};
VkFormat format{VK_FORMAT_UNDEFINED};
uint32_t layers{1};
std::vector<Mipmap> mipmaps{{}};
// Offsets stored like offsets[array_layer][mipmap_layer]
std::vector<std::vector<VkDeviceSize>> offsets;
std::unique_ptr<core::Image> vk_image;
std::unique_ptr<core::ImageView> vk_image_view;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,381 @@
/* Copyright (c) 2019-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "scene_graph/components/image/astc.h"
#include <filesystem>
#include <mutex>
#include "common/error.h"
#include "core/util/profiling.hpp"
#include "common/glm_common.h"
#if defined(_WIN32) || defined(_WIN64)
// Windows.h defines IGNORE, so we must #undef it to avoid clashes with astc header
# undef IGNORE
#endif
#include <astcenc.h>
#include <filesystem/filesystem.hpp>
#define MAGIC_FILE_CONSTANT 0x5CA1AB13
#define ASTC_CACHE_DIRECTORY "cache/astc_to_bin"
constexpr uint32_t ASTC_CACHE_HEADER_SIZE = 64;
constexpr uint32_t ASTC_CACHE_SEED = 1619;
namespace vkb
{
namespace sg
{
using Path = std::filesystem::path;
BlockDim to_blockdim(const VkFormat format)
{
switch (format)
{
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
return {4, 4, 1};
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
return {5, 4, 1};
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
return {5, 5, 1};
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
return {6, 5, 1};
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
return {6, 6, 1};
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
return {8, 5, 1};
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
return {8, 6, 1};
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
return {8, 8, 1};
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
return {10, 5, 1};
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
return {10, 6, 1};
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
return {10, 8, 1};
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
return {10, 10, 1};
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
return {12, 10, 1};
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
return {12, 12, 1};
default:
throw std::runtime_error{"Invalid astc format"};
}
}
inline astcenc_profile to_profile(const VkFormat format)
{
switch (format)
{
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
return ASTCENC_PRF_LDR;
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
return ASTCENC_PRF_LDR_SRGB;
default:
throw std::runtime_error{"Invalid astc format"};
}
}
struct AstcHeader
{
uint8_t magic[4];
uint8_t blockdim_x;
uint8_t blockdim_y;
uint8_t blockdim_z;
uint8_t xsize[3]; // x-size = xsize[0] + xsize[1] + xsize[2]
uint8_t ysize[3]; // x-size, y-size and z-size are given in texels;
uint8_t zsize[3]; // block count is inferred
};
void Astc::init()
{
}
void Astc::decode(BlockDim blockdim, VkExtent3D extent, const uint8_t *compressed_data, uint32_t compressed_size)
{
PROFILE_SCOPE("Decode ASTC Image");
// Actual decoding
astcenc_swizzle swizzle = {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A};
// Configure the compressor run
astcenc_config astc_config;
auto atscresult = astcenc_config_init(
ASTCENC_PRF_LDR_SRGB,
blockdim.x,
blockdim.y,
blockdim.z,
ASTCENC_PRE_FAST,
ASTCENC_FLG_DECOMPRESS_ONLY,
&astc_config);
if (atscresult != ASTCENC_SUCCESS)
{
throw std::runtime_error{"Error initializing astc"};
}
if (extent.width == 0 || extent.height == 0 || extent.depth == 0)
{
throw std::runtime_error{"Error reading astc: invalid size"};
}
// Allocate working state given config and thread_count
astcenc_context *astc_context;
astcenc_context_alloc(&astc_config, 1, &astc_context);
astcenc_image decoded{};
decoded.dim_x = extent.width;
decoded.dim_y = extent.height;
decoded.dim_z = extent.depth;
decoded.data_type = ASTCENC_TYPE_U8;
// allocate storage for the decoded image
// The astcenc_decompress_image function will write directly to the image data vector
auto uncompressed_size = decoded.dim_x * decoded.dim_y * decoded.dim_z * 4;
auto &decoded_data = get_mut_data();
decoded_data.resize(uncompressed_size);
void *data_ptr = static_cast<void *>(decoded_data.data());
decoded.data = &data_ptr;
atscresult = astcenc_decompress_image(astc_context, compressed_data, compressed_size, &decoded, &swizzle, 0);
if (atscresult != ASTCENC_SUCCESS)
{
throw std::runtime_error("Error decoding astc");
}
astcenc_context_free(astc_context);
set_format(VK_FORMAT_R8G8B8A8_SRGB);
set_width(decoded.dim_x);
set_height(decoded.dim_y);
set_depth(decoded.dim_z);
}
Astc::Astc(const Image &image) :
Image{image.get_name()}
{
init();
auto fs = vkb::filesystem::get();
size_t key = ASTC_CACHE_SEED;
glm::detail::hash_combine(key, image.get_data_hash());
constexpr bool use_cache = true;
constexpr uint32_t bytes_per_pixel = 4;
constexpr const char file_cache_header[ASTC_CACHE_HEADER_SIZE] = "ASTCConvertedDataV01";
const auto profile = to_profile(image.get_format());
auto can_load_from_file = [this, profile, fs, file_cache_header, bytes_per_pixel, use_cache](const Path &path, std::vector<uint8_t> &dst_data, uint32_t width, uint32_t height, uint32_t depth) {
if (!use_cache)
{
LOGD("Device does not support ASTC format and cache is disabled. ASTC image {} will be decoded.", get_name())
return false;
}
try
{
if (!fs->exists(path))
{
LOGW("Device does not support ASTC format and cache file {} does not exist. ASTC image {} will be decoded.", path.string(), get_name())
return false;
}
else
{
LOGD("Loading ASTC image {} from cache file {}", get_name(), path.string())
}
size_t offset = 0;
auto copy_from_file = [fs, path](void *dst, size_t *offset, size_t content_size) {
const auto bin_content = fs->read_chunk(path, *offset, content_size);
std::memcpy(dst, &bin_content[0], content_size);
*offset += content_size;
};
char header[ASTC_CACHE_HEADER_SIZE];
copy_from_file(&header, &offset, ASTC_CACHE_HEADER_SIZE);
if (std::strcmp(header, file_cache_header) != 0)
{
return false;
}
uint32_t file_width, file_height, file_depth;
copy_from_file(&file_width, &offset, sizeof(std::uint32_t));
copy_from_file(&file_height, &offset, sizeof(std::uint32_t));
copy_from_file(&file_depth, &offset, sizeof(std::uint32_t));
if (file_width != width || width == 0 ||
file_height != height || height == 0 ||
file_depth != depth || depth == 0)
{
return false;
}
else
{
set_width(width);
set_height(height);
set_depth(depth);
set_format(profile == ASTCENC_PRF_LDR_SRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM);
}
auto image_size = width * height * depth * bytes_per_pixel;
dst_data.resize(image_size);
copy_from_file(dst_data.data(), &offset, image_size);
return true;
}
catch (const std::runtime_error &e)
{
LOGE("ERROR loading file {} from cache. Error: <{}>", path.string(), e.what())
// file is truncated
return false;
}
};
auto save_to_file = [fs, file_cache_header, bytes_per_pixel, use_cache](const Path &path, uint8_t *dst_data, uint32_t width, uint32_t height, uint32_t depth) {
if (!use_cache)
{
return;
}
try
{
LOGI("Saving ASTC cache data to file: {}", path.string());
auto image_size = width * height * depth * bytes_per_pixel;
std::vector<uint8_t> astc_file_content;
astc_file_content.reserve(sizeof(file_cache_header) + (3 * sizeof(std::uint32_t)) + image_size);
auto append_to_file = [](std::vector<uint8_t> &dst_file, const std::uint8_t *content, size_t content_size) {
dst_file.insert(dst_file.end(), content, content + content_size);
};
append_to_file(astc_file_content, (uint8_t *) &file_cache_header, sizeof(file_cache_header));
append_to_file(astc_file_content, (uint8_t *) &width, sizeof(uint32_t));
append_to_file(astc_file_content, (uint8_t *) &height, sizeof(uint32_t));
append_to_file(astc_file_content, (uint8_t *) &depth, sizeof(uint32_t));
append_to_file(astc_file_content, (uint8_t *) dst_data, image_size);
fs->write_file(path, astc_file_content);
}
catch (const std::runtime_error &e)
{
LOGE("ERROR: saving to file: {}\nError<{}>", path.string(), e.what())
}
};
// Locate mip #0 in the KTX. This is the first one in the data array for KTX1s, but the last one in KTX2s!
auto mip_it = std::ranges::find_if(image.get_mipmaps(),
[](auto &mip) { return mip.level == 0; });
assert(mip_it != image.get_mipmaps().end() && "Mip #0 not found");
const std::string path = fmt::format("{}/{}.bin", ASTC_CACHE_DIRECTORY, uint64_t(key));
if (!can_load_from_file(path, get_mut_data(), mip_it->extent.width, mip_it->extent.height, mip_it->extent.depth))
{
// When decoding ASTC on CPU (as it is the case in here), we don't decode all mips in the mip chain.
// Instead, we just decode mip #0 and re-generate the other LODs later (via image->generate_mipmaps()).
const auto blockdim = to_blockdim(image.get_format());
const auto &extent = mip_it->extent;
auto size = extent.width * extent.height * extent.depth * 4;
const uint8_t *data_ptr = image.get_data().data() + mip_it->offset;
decode(blockdim, mip_it->extent, data_ptr, size);
save_to_file(path, get_mut_data().data(), mip_it->extent.width, mip_it->extent.height, mip_it->extent.depth);
}
update_hash(image.get_data_hash());
}
Astc::Astc(const std::string &name, const std::vector<uint8_t> &data) :
Image{name}
{
init();
// Read header
if (data.size() < sizeof(AstcHeader))
{
throw std::runtime_error{"Error reading astc: invalid memory"};
}
AstcHeader header{};
std::memcpy(&header, data.data(), sizeof(AstcHeader));
uint32_t magicval = header.magic[0] + 256 * static_cast<uint32_t>(header.magic[1]) + 65536 * static_cast<uint32_t>(header.magic[2]) + 16777216 * static_cast<uint32_t>(header.magic[3]);
if (magicval != MAGIC_FILE_CONSTANT)
{
throw std::runtime_error{"Error reading astc: invalid magic"};
}
BlockDim blockdim = {
/* xdim = */ header.blockdim_x,
/* ydim = */ header.blockdim_y,
/* zdim = */ header.blockdim_z};
VkExtent3D extent = {
/* width = */ static_cast<uint32_t>(header.xsize[0] + 256 * header.xsize[1] + 65536 * header.xsize[2]),
/* height = */ static_cast<uint32_t>(header.ysize[0] + 256 * header.ysize[1] + 65536 * header.ysize[2]),
/* depth = */ static_cast<uint32_t>(header.zsize[0] + 256 * header.zsize[1] + 65536 * header.zsize[2])};
decode(blockdim, extent, data.data() + sizeof(AstcHeader), to_u32(data.size() - sizeof(AstcHeader)));
update_hash(get_data_hash());
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,67 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/vk_common.h"
#include "scene_graph/components/image.h"
namespace vkb
{
namespace sg
{
struct BlockDim
{
uint8_t x;
uint8_t y;
uint8_t z;
};
class Astc : public Image
{
public:
/**
* @brief Decodes an ASTC image
* @param image Image to decode
*/
Astc(const Image &image);
/**
* @brief Decodes ASTC data with an ASTC header
* @param name Name of the component
* @param data ASTC data with header
*/
Astc(const std::string &name, const std::vector<uint8_t> &data);
virtual ~Astc() = default;
private:
/**
* @brief Decodes ASTC data
* @param blockdim Dimensions of the block
* @param extent Extent of the image
* @param data Pointer to ASTC image data
*/
void decode(BlockDim blockdim, VkExtent3D extent, const uint8_t *data, uint32_t size);
/**
* @brief Initializes ASTC library
*/
void init();
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,184 @@
/* Copyright (c) 2019-2025, Arm Limited and Contributors
* Copyright (c) 2019-2025, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "scene_graph/components/image/ktx.h"
#include "common/error.h"
#include <ktx.h>
#include <ktxvulkan.h>
namespace vkb
{
namespace sg
{
struct CallbackData final
{
ktxTexture *texture;
std::vector<Mipmap> *mipmaps;
};
/// Row padding is different between KTX (pad to 4) and Vulkan (none).
/// Also region->bufferOffset, i.e. the start of each image, has
/// to be a multiple of 4 and also a multiple of the element size.
static ktx_error_code_e KTX_APIENTRY optimal_tiling_callback(int mip_level,
int face,
int width,
int height,
int depth,
ktx_uint64_t face_lod_size,
void *pixels,
void *user_data)
{
auto *callback_data = reinterpret_cast<CallbackData *>(user_data);
assert(static_cast<size_t>(mip_level) < callback_data->mipmaps->size() && "Not enough space in the mipmap vector");
ktx_size_t mipmap_offset = 0;
auto result = ktxTexture_GetImageOffset(callback_data->texture, mip_level, 0, face, &mipmap_offset);
if (result != KTX_SUCCESS)
{
return result;
}
auto &mipmap = callback_data->mipmaps->at(mip_level);
mipmap.level = mip_level;
mipmap.offset = to_u32(mipmap_offset);
mipmap.extent.width = width;
mipmap.extent.height = height;
mipmap.extent.depth = depth;
return KTX_SUCCESS;
}
Ktx::Ktx(const std::string &name, const std::vector<uint8_t> &data, ContentType content_type) :
Image{name}
{
auto data_buffer = reinterpret_cast<const ktx_uint8_t *>(data.data());
auto data_size = static_cast<ktx_size_t>(data.size());
ktxTexture *texture;
auto load_ktx_result = ktxTexture_CreateFromMemory(data_buffer,
data_size,
KTX_TEXTURE_CREATE_NO_FLAGS,
&texture);
if (load_ktx_result != KTX_SUCCESS)
{
throw std::runtime_error{"Error loading KTX texture: " + name};
}
if (texture->pData)
{
// Already loaded
set_data(texture->pData, texture->dataSize);
}
else
{
// Load
auto &mut_data = get_mut_data();
auto size = texture->dataSize;
mut_data.resize(size);
auto load_data_result = ktxTexture_LoadImageData(texture, mut_data.data(), size);
if (load_data_result != KTX_SUCCESS)
{
throw std::runtime_error{"Error loading KTX image data: " + name};
}
}
set_width(texture->baseWidth);
set_height(texture->baseHeight);
set_depth(texture->baseDepth);
set_layers(texture->numLayers);
update_hash();
bool cubemap = false;
// Use the faces if there are 6 (for cubemap)
if (texture->numLayers == 1 && texture->numFaces == 6)
{
cubemap = true;
set_layers(texture->numFaces);
}
auto updated_format = ktxTexture_GetVkFormat(texture);
set_format(updated_format);
if (texture->classId == ktxTexture1_c && content_type == Color)
{
// KTX-1 files don't contain color space information. Color data is normally
// in sRGB, but the format we get back won't report that, so this will adjust it
// if necessary.
coerce_format_to_srgb();
}
auto &mipmap_levels = get_mut_mipmaps();
mipmap_levels.resize(texture->numLevels);
CallbackData callback_data{};
callback_data.texture = texture;
callback_data.mipmaps = &mipmap_levels;
auto result = ktxTexture_IterateLevels(texture, optimal_tiling_callback, &callback_data);
if (result != KTX_SUCCESS)
{
throw std::runtime_error("Error loading KTX texture");
}
// If the texture contains more than one layer, then populate the offsets otherwise take the mipmap level offsets
if (texture->numLayers > 1 || cubemap)
{
uint32_t layer_count = cubemap ? texture->numFaces : texture->numLayers;
std::vector<std::vector<VkDeviceSize>> offsets;
for (uint32_t layer = 0; layer < layer_count; layer++)
{
std::vector<VkDeviceSize> layer_offsets{};
for (uint32_t level = 0; level < texture->numLevels; level++)
{
ktx_size_t offset;
KTX_error_code result;
if (cubemap)
{
result = ktxTexture_GetImageOffset(texture, level, 0, layer, &offset);
}
else
{
result = ktxTexture_GetImageOffset(texture, level, layer, 0, &offset);
}
layer_offsets.push_back(static_cast<VkDeviceSize>(offset));
}
offsets.push_back(layer_offsets);
}
set_offsets(offsets);
}
else
{
std::vector<std::vector<VkDeviceSize>> offsets{};
offsets.resize(1);
for (size_t level = 0; level < mipmap_levels.size(); level++)
{
offsets[0].push_back(static_cast<VkDeviceSize>(mipmap_levels[level].offset));
}
set_offsets(offsets);
}
ktxTexture_Destroy(texture);
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,35 @@
/* Copyright (c) 2019-2022, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "scene_graph/components/image.h"
namespace vkb
{
namespace sg
{
class Ktx : public Image
{
public:
Ktx(const std::string &name, const std::vector<uint8_t> &data, ContentType content_type);
virtual ~Ktx() = default;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,55 @@
/* Copyright (c) 2019-2022, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "scene_graph/components/image/stb.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
namespace vkb
{
namespace sg
{
Stb::Stb(const std::string &name, const std::vector<uint8_t> &data, ContentType content_type) :
Image{name}
{
int width;
int height;
int comp;
int req_comp = 4;
auto data_buffer = reinterpret_cast<const stbi_uc *>(data.data());
auto data_size = static_cast<int>(data.size());
auto raw_data = stbi_load_from_memory(data_buffer, data_size, &width, &height, &comp, req_comp);
if (!raw_data)
{
throw std::runtime_error{"Failed to load " + name + ": " + stbi_failure_reason()};
}
set_data(raw_data, width * height * req_comp);
stbi_image_free(raw_data);
set_format(content_type == Color ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM);
set_width(to_u32(width));
set_height(to_u32(height));
set_depth(1u);
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,35 @@
/* Copyright (c) 2019-2022, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "scene_graph/components/image.h"
namespace vkb
{
namespace sg
{
class Stb : public Image
{
public:
Stb(const std::string &name, const std::vector<uint8_t> &data, ContentType content_type);
virtual ~Stb() = default;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,64 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "light.h"
namespace vkb
{
namespace sg
{
Light::Light(const std::string &name) :
Component{name}
{}
std::type_index Light::get_type()
{
return typeid(Light);
}
void Light::set_node(Node &n)
{
node = &n;
}
Node *Light::get_node()
{
return node;
}
void Light::set_light_type(const LightType &type)
{
this->light_type = type;
}
const LightType &Light::get_light_type()
{
return light_type;
}
void Light::set_properties(const LightProperties &properties)
{
this->properties = properties;
}
const LightProperties &Light::get_properties()
{
return properties;
}
} // namespace sg
} // namespace vkb
+92
View File
@@ -0,0 +1,92 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include "core/shader_module.h"
#include "scene_graph/component.h"
namespace vkb
{
namespace sg
{
enum LightType
{
Directional = 0,
Point = 1,
Spot = 2,
// Insert new light type here
Max
};
struct LightProperties
{
glm::vec3 direction{0.0f, 0.0f, -1.0f};
glm::vec3 color{1.0f, 1.0f, 1.0f};
float intensity{1.0f};
float range{0.0f};
float inner_cone_angle{0.0f};
float outer_cone_angle{0.0f};
};
class Light : public Component
{
public:
Light(const std::string &name);
Light(Light &&other) = default;
virtual ~Light() = default;
virtual std::type_index get_type() override;
void set_node(Node &node);
Node *get_node();
void set_light_type(const LightType &type);
const LightType &get_light_type();
void set_properties(const LightProperties &properties);
const LightProperties &get_properties();
private:
Node *node{nullptr};
LightType light_type;
LightProperties properties;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,34 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "material.h"
namespace vkb
{
namespace sg
{
Material::Material(const std::string &name) :
Component{name}
{}
std::type_index Material::get_type()
{
return typeid(Material);
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,78 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include "scene_graph/component.h"
namespace vkb
{
namespace sg
{
class Texture;
/**
* @brief How the alpha value of the main factor and texture should be interpreted
*/
enum class AlphaMode
{
/// Alpha value is ignored
Opaque,
/// Either full opaque or fully transparent
Mask,
/// Output is combined with the background
Blend
};
class Material : public Component
{
public:
Material(const std::string &name);
Material(Material &&other) = default;
virtual ~Material() = default;
virtual std::type_index get_type() override;
std::unordered_map<std::string, Texture *> textures;
/// Emissive color of the material
glm::vec3 emissive{0.0f, 0.0f, 0.0f};
/// Whether the material is double sided
bool double_sided{false};
/// Cutoff threshold when in Mask mode
float alpha_cutoff{0.5f};
/// Alpha rendering mode
AlphaMode alpha_mode{AlphaMode::Opaque};
};
} // namespace sg
} // namespace vkb
+63
View File
@@ -0,0 +1,63 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mesh.h"
namespace vkb
{
namespace sg
{
Mesh::Mesh(const std::string &name) :
Component{name}
{}
void Mesh::update_bounds(const std::vector<glm::vec3> &vertex_data, const std::vector<uint16_t> &index_data)
{
bounds.update(vertex_data, index_data);
}
std::type_index Mesh::get_type()
{
return typeid(Mesh);
}
const AABB &Mesh::get_bounds() const
{
return bounds;
}
void Mesh::add_submesh(SubMesh &submesh)
{
submeshes.push_back(&submesh);
}
const std::vector<SubMesh *> &Mesh::get_submeshes() const
{
return submeshes;
}
void Mesh::add_node(Node &node)
{
nodes.push_back(&node);
}
const std::vector<Node *> &Mesh::get_nodes() const
{
return nodes;
}
} // namespace sg
} // namespace vkb
+63
View File
@@ -0,0 +1,63 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "scene_graph/component.h"
#include "scene_graph/components/aabb.h"
namespace vkb
{
namespace sg
{
class SubMesh;
class Mesh : public Component
{
public:
Mesh(const std::string &name);
virtual ~Mesh() = default;
void update_bounds(const std::vector<glm::vec3> &vertex_data, const std::vector<uint16_t> &index_data = {});
virtual std::type_index get_type() override;
const AABB &get_bounds() const;
void add_submesh(SubMesh &submesh);
const std::vector<SubMesh *> &get_submeshes() const;
void add_node(Node &node);
const std::vector<Node *> &get_nodes() const;
private:
AABB bounds;
std::vector<SubMesh *> submeshes;
std::vector<Node *> nodes;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,107 @@
/* Copyright (c) 2023-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "orthographic_camera.h"
#include <glm/gtc/matrix_transform.hpp>
namespace vkb
{
namespace sg
{
OrthographicCamera::OrthographicCamera(const std::string &name) :
Camera{name}
{}
OrthographicCamera::OrthographicCamera(const std::string &name, float left, float right, float bottom, float top, float near_plane, float far_plane) :
Camera{name},
left{left},
right{right},
top{top},
bottom{bottom},
near_plane{near_plane},
far_plane{far_plane}
{
}
void OrthographicCamera::set_left(float new_left)
{
left = new_left;
}
float OrthographicCamera::get_left() const
{
return left;
}
void OrthographicCamera::set_right(float new_right)
{
right = new_right;
}
float OrthographicCamera::get_right() const
{
return right;
}
void OrthographicCamera::set_bottom(float new_bottom)
{
bottom = new_bottom;
}
float OrthographicCamera::get_bottom() const
{
return bottom;
}
void OrthographicCamera::set_top(float new_top)
{
top = new_top;
}
float OrthographicCamera::get_top() const
{
return top;
}
void OrthographicCamera::set_near_plane(float new_near_plane)
{
near_plane = new_near_plane;
}
float OrthographicCamera::get_near_plane() const
{
return near_plane;
}
void OrthographicCamera::set_far_plane(float new_far_plane)
{
far_plane = new_far_plane;
}
float OrthographicCamera::get_far_plane() const
{
return far_plane;
}
glm::mat4 OrthographicCamera::get_projection()
{
// Note: Using reversed depth-buffer for increased precision, so Znear and Zfar are flipped
return glm::ortho(left, right, bottom, top, far_plane, near_plane);
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,79 @@
/* Copyright (c) 2020-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/error.h"
#include "common/glm_common.h"
#include "scene_graph/components/camera.h"
namespace vkb
{
namespace sg
{
class OrthographicCamera : public Camera
{
public:
OrthographicCamera(const std::string &name);
OrthographicCamera(const std::string &name, float left, float right, float bottom, float top, float near_plane, float far_plane);
virtual ~OrthographicCamera() = default;
void set_left(float left);
float get_left() const;
void set_right(float right);
float get_right() const;
void set_bottom(float bottom);
float get_bottom() const;
void set_top(float top);
float get_top() const;
void set_near_plane(float near_plane);
float get_near_plane() const;
void set_far_plane(float far_plane);
float get_far_plane() const;
virtual glm::mat4 get_projection() override;
private:
float left{-1.0f};
float right{1.0f};
float bottom{-1.0f};
float top{1.0f};
float near_plane{0.0f};
float far_plane{1.0f};
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,33 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pbr_material.h"
namespace vkb
{
namespace sg
{
PBRMaterial::PBRMaterial(const std::string &name) :
Material{name}
{}
std::type_index PBRMaterial::get_type()
{
return typeid(PBRMaterial);
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,53 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include "scene_graph/components/material.h"
namespace vkb
{
namespace sg
{
class PBRMaterial : public Material
{
public:
PBRMaterial(const std::string &name);
virtual ~PBRMaterial() = default;
virtual std::type_index get_type() override;
glm::vec4 base_color_factor{0.0f, 0.0f, 0.0f, 0.0f};
float metallic_factor{0.0f};
float roughness_factor{0.0f};
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,76 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "perspective_camera.h"
#include <glm/gtc/matrix_transform.hpp>
namespace vkb
{
namespace sg
{
PerspectiveCamera::PerspectiveCamera(const std::string &name) :
Camera{name}
{}
void PerspectiveCamera::set_field_of_view(float new_fov)
{
fov = new_fov;
}
float PerspectiveCamera::get_far_plane() const
{
return far_plane;
}
void PerspectiveCamera::set_far_plane(float zfar)
{
far_plane = zfar;
}
float PerspectiveCamera::get_near_plane() const
{
return near_plane;
}
void PerspectiveCamera::set_near_plane(float znear)
{
near_plane = znear;
}
void PerspectiveCamera::set_aspect_ratio(float new_aspect_ratio)
{
aspect_ratio = new_aspect_ratio;
}
float PerspectiveCamera::get_field_of_view()
{
return fov;
}
float PerspectiveCamera::get_aspect_ratio()
{
return aspect_ratio;
}
glm::mat4 PerspectiveCamera::get_projection()
{
// Note: Using reversed depth-buffer for increased precision, so Znear and Zfar are flipped
return glm::perspective(fov, aspect_ratio, far_plane, near_plane);
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,77 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include "scene_graph/components/camera.h"
namespace vkb
{
namespace sg
{
class PerspectiveCamera : public Camera
{
public:
PerspectiveCamera(const std::string &name);
virtual ~PerspectiveCamera() = default;
void set_aspect_ratio(float aspect_ratio);
void set_field_of_view(float fov);
float get_far_plane() const;
void set_far_plane(float zfar);
float get_near_plane() const;
void set_near_plane(float znear);
float get_aspect_ratio();
float get_field_of_view();
virtual glm::mat4 get_projection() override;
private:
/**
* @brief Screen size aspect ratio
*/
float aspect_ratio{1.0f};
/**
* @brief Horizontal field of view in radians
*/
float fov{glm::radians(60.0f)};
float far_plane{100.0};
float near_plane{0.1f};
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,34 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sampler.h"
namespace vkb
{
namespace sg
{
Sampler::Sampler(const std::string &name, core::Sampler &&vk_sampler) :
Component{name},
vk_sampler{std::move(vk_sampler)}
{}
std::type_index Sampler::get_type()
{
return typeid(Sampler);
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,46 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "core/sampler.h"
#include "scene_graph/component.h"
namespace vkb
{
namespace sg
{
class Sampler : public Component
{
public:
Sampler(const std::string &name, core::Sampler &&vk_sampler);
Sampler(Sampler &&other) = default;
virtual ~Sampler() = default;
virtual std::type_index get_type() override;
core::Sampler vk_sampler;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,75 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sub_mesh.h"
#include "material.h"
#include "rendering/subpass.h"
namespace vkb
{
namespace sg
{
SubMesh::SubMesh(const std::string &name) :
Component{name}
{}
std::type_index SubMesh::get_type()
{
return typeid(SubMesh);
}
void SubMesh::set_attribute(const std::string &attribute_name, const VertexAttribute &attribute)
{
vertex_attributes[attribute_name] = attribute;
}
bool SubMesh::get_attribute(const std::string &attribute_name, VertexAttribute &attribute) const
{
auto attrib_it = vertex_attributes.find(attribute_name);
if (attrib_it == vertex_attributes.end())
{
return false;
}
attribute = attrib_it->second;
return true;
}
void SubMesh::set_material(const Material &new_material)
{
material = &new_material;
}
const Material *SubMesh::get_material() const
{
return material;
}
const ShaderVariant &SubMesh::get_shader_variant() const
{
return shader_variant;
}
ShaderVariant &SubMesh::get_mut_shader_variant()
{
return shader_variant;
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,87 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#include "common/vk_common.h"
#include "core/buffer.h"
#include "core/shader_module.h"
#include "scene_graph/component.h"
namespace vkb
{
namespace sg
{
class Material;
struct VertexAttribute
{
VkFormat format = VK_FORMAT_UNDEFINED;
std::uint32_t stride = 0;
std::uint32_t offset = 0;
};
class SubMesh : public Component
{
public:
SubMesh(const std::string &name = {});
virtual ~SubMesh() = default;
virtual std::type_index get_type() override;
VkIndexType index_type{};
std::uint32_t index_offset = 0;
std::uint32_t vertices_count = 0;
std::uint32_t vertex_indices = 0;
std::unordered_map<std::string, vkb::core::BufferC> vertex_buffers;
std::unique_ptr<vkb::core::BufferC> index_buffer;
void set_attribute(const std::string &name, const VertexAttribute &attribute);
bool get_attribute(const std::string &name, VertexAttribute &attribute) const;
void set_material(const Material &material);
const Material *get_material() const;
const ShaderVariant &get_shader_variant() const;
ShaderVariant &get_mut_shader_variant();
private:
std::unordered_map<std::string, VertexAttribute> vertex_attributes;
const Material *material{nullptr};
ShaderVariant shader_variant;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,57 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "texture.h"
#include "scene_graph/components/image.h"
#include "scene_graph/components/sampler.h"
namespace vkb
{
namespace sg
{
Texture::Texture(const std::string &name) :
Component{name}
{}
std::type_index Texture::get_type()
{
return typeid(Texture);
}
void Texture::set_image(Image &i)
{
image = &i;
}
Image *Texture::get_image()
{
return image;
}
void Texture::set_sampler(Sampler &s)
{
sampler = &s;
}
Sampler *Texture::get_sampler()
{
assert(sampler && "Texture has no sampler");
return sampler;
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,60 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "scene_graph/component.h"
#include "scene_graph/components/sampler.h"
namespace vkb
{
namespace sg
{
class Image;
class Sampler;
class Texture : public Component
{
public:
Texture(const std::string &name);
Texture(Texture &&other) = default;
virtual ~Texture() = default;
virtual std::type_index get_type() override;
void set_image(Image &image);
Image *get_image();
void set_sampler(Sampler &sampler);
Sampler *get_sampler();
private:
Image *image{nullptr};
Sampler *sampler{nullptr};
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,129 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "transform.h"
#include "common/glm_common.h"
#include <glm/gtx/matrix_decompose.hpp>
#include "scene_graph/node.h"
namespace vkb
{
namespace sg
{
Transform::Transform(Node &n) :
node{n}
{
}
Node &Transform::get_node()
{
return node;
}
std::type_index Transform::get_type()
{
return typeid(Transform);
}
void Transform::set_translation(const glm::vec3 &new_translation)
{
translation = new_translation;
invalidate_world_matrix();
}
void Transform::set_rotation(const glm::quat &new_rotation)
{
rotation = new_rotation;
invalidate_world_matrix();
}
void Transform::set_scale(const glm::vec3 &new_scale)
{
scale = new_scale;
invalidate_world_matrix();
}
const glm::vec3 &Transform::get_translation() const
{
return translation;
}
const glm::quat &Transform::get_rotation() const
{
return rotation;
}
const glm::vec3 &Transform::get_scale() const
{
return scale;
}
void Transform::set_matrix(const glm::mat4 &matrix)
{
glm::vec3 skew;
glm::vec4 perspective;
glm::decompose(matrix, scale, rotation, translation, skew, perspective);
invalidate_world_matrix();
}
glm::mat4 Transform::get_matrix() const
{
return glm::translate(glm::mat4(1.0), translation) *
glm::mat4_cast(rotation) *
glm::scale(glm::mat4(1.0), scale);
}
glm::mat4 Transform::get_world_matrix()
{
update_world_transform();
return world_matrix;
}
void Transform::invalidate_world_matrix()
{
update_world_matrix = true;
}
void Transform::update_world_transform()
{
if (!update_world_matrix)
{
return;
}
world_matrix = get_matrix();
auto parent = node.get_parent();
if (parent)
{
auto &transform = parent->get_component<Transform>();
world_matrix = transform.get_world_matrix() * world_matrix;
}
update_world_matrix = false;
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,91 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include <glm/gtx/quaternion.hpp>
#include "scene_graph/component.h"
namespace vkb
{
namespace sg
{
class Node;
class Transform : public Component
{
public:
Transform(Node &node);
virtual ~Transform() = default;
Node &get_node();
virtual std::type_index get_type() override;
void set_translation(const glm::vec3 &translation);
void set_rotation(const glm::quat &rotation);
void set_scale(const glm::vec3 &scale);
const glm::vec3 &get_translation() const;
const glm::quat &get_rotation() const;
const glm::vec3 &get_scale() const;
void set_matrix(const glm::mat4 &matrix);
glm::mat4 get_matrix() const;
glm::mat4 get_world_matrix();
/**
* @brief Marks the world transform invalid if any of
* the local transform are changed or the parent
* world transform has changed.
*/
void invalidate_world_matrix();
private:
Node &node;
glm::vec3 translation = glm::vec3(0.0, 0.0, 0.0);
glm::quat rotation = glm::quat(1.0, 0.0, 0.0, 0.0);
glm::vec3 scale = glm::vec3(1.0, 1.0, 1.0);
glm::mat4 world_matrix = glm::mat4(1.0);
bool update_world_matrix = false;
void update_world_transform();
};
} // namespace sg
} // namespace vkb
+78
View File
@@ -0,0 +1,78 @@
/* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "scene.h"
#include "scene_graph/components/camera.h"
#include "scene_graph/components/hpp_mesh.h"
#include "scene_graph/script.h"
#include "scene_graph/scripts/animation.h"
namespace vkb
{
namespace scene_graph
{
/**
* @brief facade class around vkb::sg::Scene, providing a vulkan.hpp-based interface
*
* See vkb::sb::Scene for documentation
*/
class HPPScene : private vkb::sg::Scene
{
public:
template <class T>
std::vector<T *> get_components() const
{
if constexpr (std::is_same<T, vkb::sg::Animation>::value || std::is_same<T, vkb::sg::Camera>::value || std::is_same<T, vkb::sg::Script>::value ||
std::is_same<T, vkb::sg::SubMesh>::value || std::is_same<T, vkb::sg::Texture>::value)
{
return vkb::sg::Scene::get_components<T>();
}
else if constexpr (std::is_same<T, vkb::scene_graph::components::HPPMesh>::value)
{
std::vector<vkb::sg::Mesh *> meshes = vkb::sg::Scene::get_components<vkb::sg::Mesh>();
return *reinterpret_cast<std::vector<T *> *>(&meshes);
}
else
{
assert(false); // path never passed -> Please add a type-check here!
return {};
}
}
template <class T>
bool has_component() const
{
if constexpr (std::is_same<T, vkb::sg::Animation>::value || std::is_same<T, vkb::sg::Camera>::value || std::is_same<T, vkb::sg::Script>::value ||
std::is_same<T, vkb::sg::SubMesh>::value || std::is_same<T, vkb::sg::Texture>::value)
{
return vkb::sg::Scene::has_component(typeid(T));
}
else if constexpr (std::is_same<T, vkb::scene_graph::components::HPPMesh>::value)
{
return vkb::sg::Scene::has_component<vkb::sg::Mesh>();
}
else
{
assert(false); // path never passed -> Please add a type-check here!
return false;
}
}
};
} // namespace scene_graph
} // namespace vkb
+92
View File
@@ -0,0 +1,92 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "node.h"
#include "component.h"
#include "components/transform.h"
namespace vkb
{
namespace sg
{
Node::Node(const size_t id, const std::string &name) :
id{id},
name{name},
transform{*this}
{
set_component(transform);
}
const size_t Node::get_id() const
{
return id;
}
const std::string &Node::get_name() const
{
return name;
}
void Node::set_parent(Node &p)
{
parent = &p;
transform.invalidate_world_matrix();
}
Node *Node::get_parent() const
{
return parent;
}
void Node::add_child(Node &child)
{
children.push_back(&child);
}
const std::vector<Node *> &Node::get_children() const
{
return children;
}
void Node::set_component(Component &component)
{
auto it = components.find(component.get_type());
if (it != components.end())
{
it->second = &component;
}
else
{
components.insert(std::make_pair(component.get_type(), &component));
}
}
Component &Node::get_component(const std::type_index index)
{
return *components.at(index);
}
bool Node::has_component(const std::type_index index)
{
return components.count(index) > 0;
}
} // namespace sg
} // namespace vkb
+91
View File
@@ -0,0 +1,91 @@
/* Copyright (c) 2018-2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeindex>
#include <unordered_map>
#include <vector>
#include "scene_graph/components/transform.h"
namespace vkb
{
namespace sg
{
class Component;
/// @brief A leaf of the tree structure which can have children and a single parent.
class Node
{
public:
Node(const size_t id, const std::string &name);
virtual ~Node() = default;
const size_t get_id() const;
const std::string &get_name() const;
Transform &get_transform()
{
return transform;
}
void set_parent(Node &parent);
Node *get_parent() const;
void add_child(Node &child);
const std::vector<Node *> &get_children() const;
void set_component(Component &component);
template <class T>
inline T &get_component()
{
return dynamic_cast<T &>(get_component(typeid(T)));
}
Component &get_component(const std::type_index index);
template <class T>
bool has_component()
{
return has_component(typeid(T));
}
bool has_component(const std::type_index index);
private:
size_t id;
std::string name;
Transform transform;
Node *parent{nullptr};
std::vector<Node *> children;
std::unordered_map<std::type_index, Component *> components;
};
} // namespace sg
} // namespace vkb
+139
View File
@@ -0,0 +1,139 @@
/* Copyright (c) 2018-2023, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "scene.h"
#include <queue>
#include "component.h"
#include "components/sub_mesh.h"
#include "node.h"
namespace vkb
{
namespace sg
{
Scene::Scene(const std::string &name) :
name{name}
{}
void Scene::set_name(const std::string &new_name)
{
name = new_name;
}
const std::string &Scene::get_name() const
{
return name;
}
void Scene::set_nodes(std::vector<std::unique_ptr<Node>> &&n)
{
assert(nodes.empty() && "Scene nodes were already set");
nodes = std::move(n);
}
void Scene::add_node(std::unique_ptr<Node> &&n)
{
nodes.emplace_back(std::move(n));
}
void Scene::add_child(Node &child)
{
root->add_child(child);
}
std::unique_ptr<Component> Scene::get_model(uint32_t index)
{
auto meshes = std::move(components.at(typeid(SubMesh)));
assert(index < meshes.size());
return std::move(meshes[index]);
}
void Scene::add_component(std::unique_ptr<Component> &&component, Node &node)
{
node.set_component(*component);
if (component)
{
components[component->get_type()].push_back(std::move(component));
}
}
void Scene::add_component(std::unique_ptr<Component> &&component)
{
if (component)
{
components[component->get_type()].push_back(std::move(component));
}
}
void Scene::set_components(const std::type_index &type_info, std::vector<std::unique_ptr<Component>> &&new_components)
{
components[type_info] = std::move(new_components);
}
const std::vector<std::unique_ptr<Component>> &Scene::get_components(const std::type_index &type_info) const
{
return components.at(type_info);
}
bool Scene::has_component(const std::type_index &type_info) const
{
auto component = components.find(type_info);
return (component != components.end() && !component->second.empty());
}
Node *Scene::find_node(const std::string &node_name)
{
for (auto root_node : root->get_children())
{
std::queue<sg::Node *> traverse_nodes{};
traverse_nodes.push(root_node);
while (!traverse_nodes.empty())
{
auto node = traverse_nodes.front();
traverse_nodes.pop();
if (node->get_name() == node_name)
{
return node;
}
for (auto child_node : node->get_children())
{
traverse_nodes.push(child_node);
}
}
}
return nullptr;
}
void Scene::set_root_node(Node &node)
{
root = &node;
}
Node &Scene::get_root_node()
{
return *root;
}
} // namespace sg
} // namespace vkb
+144
View File
@@ -0,0 +1,144 @@
/* Copyright (c) 2018-2020, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <algorithm>
#include <memory>
#include <string>
#include <typeindex>
#include <unordered_map>
#include <vector>
#include "scene_graph/components/light.h"
#include "scene_graph/components/texture.h"
namespace vkb
{
namespace sg
{
class Node;
class Component;
class SubMesh;
/// @brief A collection of nodes organized in a tree structure.
/// It can contain more than one root node.
class Scene
{
public:
Scene() = default;
Scene(const std::string &name);
void set_name(const std::string &name);
const std::string &get_name() const;
void set_nodes(std::vector<std::unique_ptr<Node>> &&nodes);
void add_node(std::unique_ptr<Node> &&node);
void add_child(Node &child);
std::unique_ptr<Component> get_model(uint32_t index = 0);
void add_component(std::unique_ptr<Component> &&component);
void add_component(std::unique_ptr<Component> &&component, Node &node);
/**
* @brief Set list of components for the given type
* @param type_info The type of the component
* @param components The list of components (retained)
*/
void set_components(const std::type_index &type_info, std::vector<std::unique_ptr<Component>> &&components);
/**
* @brief Set list of components casted from the given template type
*/
template <class T>
void set_components(std::vector<std::unique_ptr<T>> &&components)
{
std::vector<std::unique_ptr<Component>> result(components.size());
std::transform(components.begin(), components.end(), result.begin(),
[](std::unique_ptr<T> &component) -> std::unique_ptr<Component> {
return std::unique_ptr<Component>(std::move(component));
});
set_components(typeid(T), std::move(result));
}
/**
* @brief Clears a list of components
*/
template <class T>
void clear_components()
{
set_components(typeid(T), {});
}
/**
* @return List of pointers to components casted to the given template type
*/
template <class T>
std::vector<T *> get_components() const
{
std::vector<T *> result;
if (has_component(typeid(T)))
{
auto &scene_components = get_components(typeid(T));
result.resize(scene_components.size());
std::transform(scene_components.begin(), scene_components.end(), result.begin(),
[](const std::unique_ptr<Component> &component) -> T * {
return dynamic_cast<T *>(component.get());
});
}
return result;
}
/**
* @return List of components for the given type
*/
const std::vector<std::unique_ptr<Component>> &get_components(const std::type_index &type_info) const;
template <class T>
bool has_component() const
{
return has_component(typeid(T));
}
bool has_component(const std::type_index &type_info) const;
Node *find_node(const std::string &name);
void set_root_node(Node &node);
Node &get_root_node();
private:
std::string name;
/// List of all the nodes
std::vector<std::unique_ptr<Node>> nodes;
Node *root{nullptr};
std::unordered_map<std::type_index, std::vector<std::unique_ptr<Component>>> components;
};
} // namespace sg
} // namespace vkb
+53
View File
@@ -0,0 +1,53 @@
/* Copyright (c) 2019-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "script.h"
namespace vkb
{
namespace sg
{
Script::Script(const std::string &name) :
Component{name}
{}
std::type_index Script::get_type()
{
return typeid(Script);
}
void Script::input_event(const InputEvent & /*input_event*/)
{
}
void Script::resize(uint32_t /*width*/, uint32_t /*height*/)
{
}
NodeScript::NodeScript(Node &node, const std::string &name) :
Script{name},
node{node}
{
}
Node &NodeScript::get_node()
{
return node;
}
} // namespace sg
} // namespace vkb
+68
View File
@@ -0,0 +1,68 @@
/* Copyright (c) 2019-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "platform/input_events.h"
#include "scene_graph/component.h"
namespace vkb
{
namespace sg
{
/**
* @brief Generic structure to receive platform events.
* Used for adding game logic to scene graph objects.
*/
class Script : public Component
{
public:
Script(const std::string &name = "");
virtual ~Script() = default;
virtual std::type_index get_type() override;
/**
* @brief Main loop script events
*/
virtual void update(float delta_time) = 0;
virtual void input_event(const InputEvent &input_event);
virtual void resize(uint32_t width, uint32_t height);
};
class NodeScript : public Script
{
public:
NodeScript(Node &node, const std::string &name = "");
virtual ~NodeScript() = default;
Node &get_node();
private:
Node &node;
};
} // namespace sg
} // namespace vkb
+181
View File
@@ -0,0 +1,181 @@
/* Copyright (c) 2020-2023, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "animation.h"
#include "scene_graph/node.h"
namespace vkb
{
namespace sg
{
Animation::Animation(const std::string &name) :
Script{name}
{
}
Animation::Animation(const Animation &other) :
channels{other.channels}
{
}
void Animation::add_channel(Node &node, const AnimationTarget &target, const AnimationSampler &sampler)
{
channels.push_back({node, target, sampler});
}
void Animation::update(float delta_time)
{
current_time += delta_time;
if (current_time > end_time)
{
current_time -= end_time;
}
for (auto &channel : channels)
{
for (size_t i = 0; i < channel.sampler.inputs.size() - 1; ++i)
{
if ((current_time >= channel.sampler.inputs[i]) && (current_time <= channel.sampler.inputs[i + 1]))
{
float time = (current_time - channel.sampler.inputs[i]) / (channel.sampler.inputs[i + 1] - channel.sampler.inputs[i]);
auto &transform = channel.node.get_transform();
if (channel.sampler.type == AnimationType::Linear)
{
switch (channel.target)
{
case Translation:
{
transform.set_translation(glm::vec3(glm::mix(channel.sampler.outputs[i],
channel.sampler.outputs[i + 1],
time)));
break;
}
case Rotation:
{
glm::quat q1;
q1.x = channel.sampler.outputs[i].x;
q1.y = channel.sampler.outputs[i].y;
q1.z = channel.sampler.outputs[i].z;
q1.w = channel.sampler.outputs[i].w;
glm::quat q2;
q2.x = channel.sampler.outputs[i + 1].x;
q2.y = channel.sampler.outputs[i + 1].y;
q2.z = channel.sampler.outputs[i + 1].z;
q2.w = channel.sampler.outputs[i + 1].w;
transform.set_rotation(glm::normalize(glm::slerp(q1, q2, time)));
break;
}
case Scale:
{
transform.set_scale(glm::vec3(glm::mix(channel.sampler.outputs[i],
channel.sampler.outputs[i + 1],
time)));
}
}
}
else if (channel.sampler.type == AnimationType::Step)
{
switch (channel.target)
{
case Translation:
{
transform.set_translation(glm::vec3(channel.sampler.outputs[i]));
break;
}
case Rotation:
{
glm::quat q1;
q1.x = channel.sampler.outputs[i].x;
q1.y = channel.sampler.outputs[i].y;
q1.z = channel.sampler.outputs[i].z;
q1.w = channel.sampler.outputs[i].w;
transform.set_rotation(glm::normalize(q1));
break;
}
case Scale:
{
transform.set_scale(glm::vec3(channel.sampler.outputs[i]));
}
}
}
else if (channel.sampler.type == AnimationType::CubicSpline)
{
float delta = channel.sampler.inputs[i + 1] - channel.sampler.inputs[i];
glm::vec4 p0 = channel.sampler.outputs[i * 3 + 1]; // Starting point
glm::vec4 p1 = channel.sampler.outputs[(i + 1) * 3 + 1]; // Ending point
glm::vec4 m0 = delta * channel.sampler.outputs[i * 3 + 2]; // Delta time * out tangent
glm::vec4 m1 = delta * channel.sampler.outputs[(i + 1) * 3 + 0]; // Delta time * in tangent of next point
// This equation is taken from the GLTF 2.0 specification Appendix C (https://github.com/KhronosGroup/glTF/tree/main/specification/2.0#appendix-c-spline-interpolation)
glm::vec4 result = (2.0f * glm::pow(time, 3.0f) - 3.0f * glm::pow(time, 2.0f) + 1.0f) * p0 + (glm::pow(time, 3.0f) - 2.0f * glm::pow(time, 2.0f) + time) * m0 + (-2.0f * glm::pow(time, 3.0f) + 3.0f * glm::pow(time, 2.0f)) * p1 + (glm::pow(time, 3.0f) - glm::pow(time, 2.0f)) * m1;
auto &transform = channel.node.get_transform();
switch (channel.target)
{
case Translation:
{
transform.set_translation(glm::vec3(result));
break;
}
case Rotation:
{
glm::quat q1;
q1.x = result.x;
q1.y = result.y;
q1.z = result.z;
q1.w = result.w;
transform.set_rotation(glm::normalize(q1));
break;
}
case Scale:
{
transform.set_scale(glm::vec3(result));
}
}
}
}
}
}
}
void Animation::update_times(float new_start_time, float new_end_time)
{
if (new_start_time < start_time)
{
start_time = new_start_time;
}
if (new_end_time > end_time)
{
end_time = new_end_time;
}
}
} // namespace sg
} // namespace vkb
+88
View File
@@ -0,0 +1,88 @@
/* Copyright (c) 2020-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "scene_graph/components/transform.h"
#include "scene_graph/script.h"
namespace vkb
{
namespace sg
{
enum AnimationType
{
Linear,
Step,
CubicSpline
};
enum AnimationTarget
{
Translation,
Rotation,
Scale
};
struct AnimationSampler
{
AnimationType type{Linear};
std::vector<float> inputs{};
std::vector<glm::vec4> outputs{};
};
struct AnimationChannel
{
Node &node;
AnimationTarget target;
AnimationSampler sampler;
};
class Animation : public Script
{
public:
Animation(const std::string &name = "");
Animation(const Animation &);
virtual void update(float delta_time) override;
void update_times(float start_time, float end_time);
void add_channel(Node &node, const AnimationTarget &target, const AnimationSampler &sampler);
private:
std::vector<AnimationChannel> channels;
float current_time{0.0f};
float start_time{std::numeric_limits<float>::max()};
float end_time{std::numeric_limits<float>::min()};
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,241 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
* Copyright (c) 2020-2024, Andrew Cox, Huawei Technologies Research & Development (UK) Limited
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "free_camera.h"
#include "common/error.h"
#include "common/glm_common.h"
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/quaternion.hpp>
#include "scene_graph/components/perspective_camera.h"
#include "scene_graph/components/transform.h"
#include "scene_graph/node.h"
namespace vkb
{
namespace sg
{
const float FreeCamera::TOUCH_DOWN_MOVE_FORWARD_WAIT_TIME = 2.0f;
const float FreeCamera::ROTATION_MOVE_WEIGHT = 0.1f;
const float FreeCamera::KEY_ROTATION_MOVE_WEIGHT = 0.5f;
const float FreeCamera::TRANSLATION_MOVE_WEIGHT = 3.0f;
const float FreeCamera::TRANSLATION_MOVE_STEP = 50.0f;
const uint32_t FreeCamera::TRANSLATION_MOVE_SPEED = 4;
FreeCamera::FreeCamera(Node &node) :
NodeScript{node, "FreeCamera"}
{}
void FreeCamera::update(float delta_time)
{
glm::vec3 delta_translation(0.0f, 0.0f, 0.0f);
glm::vec3 delta_rotation(0.0f, 0.0f, 0.0f);
float mul_translation = speed_multiplier;
if (key_pressed[KeyCode::W])
{
delta_translation.z -= TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::S])
{
delta_translation.z += TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::A])
{
delta_translation.x -= TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::D])
{
delta_translation.x += TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::Q])
{
delta_translation.y -= TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::E])
{
delta_translation.y += TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::LeftControl])
{
mul_translation *= (1.0f * TRANSLATION_MOVE_SPEED);
}
if (key_pressed[KeyCode::LeftShift])
{
mul_translation *= (1.0f / TRANSLATION_MOVE_SPEED);
}
if (key_pressed[KeyCode::I])
{
delta_rotation.x += KEY_ROTATION_MOVE_WEIGHT;
}
if (key_pressed[KeyCode::K])
{
delta_rotation.x -= KEY_ROTATION_MOVE_WEIGHT;
}
if (key_pressed[KeyCode::J])
{
delta_rotation.y += KEY_ROTATION_MOVE_WEIGHT;
}
if (key_pressed[KeyCode::L])
{
delta_rotation.y -= KEY_ROTATION_MOVE_WEIGHT;
}
if (mouse_button_pressed[MouseButton::Left] && mouse_button_pressed[MouseButton::Right])
{
delta_rotation.z += TRANSLATION_MOVE_WEIGHT * mouse_move_delta.x;
}
else if (mouse_button_pressed[MouseButton::Right])
{
delta_rotation.x -= ROTATION_MOVE_WEIGHT * mouse_move_delta.y;
delta_rotation.y -= ROTATION_MOVE_WEIGHT * mouse_move_delta.x;
}
else if (mouse_button_pressed[MouseButton::Left])
{
delta_translation.x += TRANSLATION_MOVE_WEIGHT * mouse_move_delta.x;
delta_translation.y += TRANSLATION_MOVE_WEIGHT * -mouse_move_delta.y;
}
if (touch_pointer_pressed[0])
{
delta_rotation.x -= ROTATION_MOVE_WEIGHT * touch_move_delta.y;
delta_rotation.y -= ROTATION_MOVE_WEIGHT * touch_move_delta.x;
if (touch_pointer_time > TOUCH_DOWN_MOVE_FORWARD_WAIT_TIME)
{
delta_translation.z -= TRANSLATION_MOVE_STEP;
}
else
{
touch_pointer_time += delta_time;
}
}
delta_translation *= mul_translation * delta_time;
delta_rotation *= delta_time;
// Only re-calculate the transform if it's changed
if (delta_rotation != glm::vec3(0.0f, 0.0f, 0.0f) || delta_translation != glm::vec3(0.0f, 0.0f, 0.0f))
{
auto &transform = get_node().get_component<Transform>();
glm::quat qx = glm::angleAxis(delta_rotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
glm::quat qy = glm::angleAxis(delta_rotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
glm::quat orientation = glm::normalize(qy * transform.get_rotation() * qx);
transform.set_translation(transform.get_translation() + delta_translation * glm::conjugate(orientation));
transform.set_rotation(orientation);
}
mouse_move_delta = {};
touch_move_delta = {};
}
void FreeCamera::input_event(const InputEvent &input_event)
{
if (input_event.get_source() == EventSource::Keyboard)
{
const auto &key_event = static_cast<const KeyInputEvent &>(input_event);
if (key_event.get_action() == KeyAction::Down ||
key_event.get_action() == KeyAction::Repeat)
{
key_pressed[key_event.get_code()] = true;
}
else
{
key_pressed[key_event.get_code()] = false;
}
}
else if (input_event.get_source() == EventSource::Mouse)
{
const auto &mouse_button = static_cast<const MouseButtonInputEvent &>(input_event);
glm::vec2 mouse_pos{std::floor(mouse_button.get_pos_x()), std::floor(mouse_button.get_pos_y())};
if (mouse_button.get_action() == MouseAction::Down)
{
mouse_button_pressed[mouse_button.get_button()] = true;
}
if (mouse_button.get_action() == MouseAction::Up)
{
mouse_button_pressed[mouse_button.get_button()] = false;
}
if (mouse_button.get_action() == MouseAction::Move)
{
mouse_move_delta = mouse_pos - mouse_last_pos;
mouse_last_pos = mouse_pos;
}
}
else if (input_event.get_source() == EventSource::Touchscreen)
{
const auto &touch_event = static_cast<const TouchInputEvent &>(input_event);
glm::vec2 touch_pos{std::floor(touch_event.get_pos_x()), std::floor(touch_event.get_pos_y())};
if (touch_event.get_action() == TouchAction::Down)
{
touch_pointer_pressed[touch_event.get_pointer_id()] = true;
touch_last_pos = touch_pos;
}
if (touch_event.get_action() == TouchAction::Up)
{
touch_pointer_pressed[touch_event.get_pointer_id()] = false;
touch_pointer_time = 0.0f;
}
if (touch_event.get_action() == TouchAction::Move && touch_event.get_pointer_id() == 0)
{
touch_move_delta = touch_pos - touch_last_pos;
touch_last_pos = touch_pos;
}
}
}
void FreeCamera::resize(uint32_t width, uint32_t height)
{
auto &camera_node = get_node();
if (camera_node.has_component<Camera>())
{
if (auto camera = dynamic_cast<PerspectiveCamera *>(&camera_node.get_component<Camera>()))
{
camera->set_aspect_ratio(static_cast<float>(width) / height);
}
}
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,81 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <string>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include "scene_graph/script.h"
namespace vkb
{
namespace sg
{
class FreeCamera : public NodeScript
{
public:
static const float TOUCH_DOWN_MOVE_FORWARD_WAIT_TIME;
static const float ROTATION_MOVE_WEIGHT;
static const float KEY_ROTATION_MOVE_WEIGHT;
static const float TRANSLATION_MOVE_WEIGHT;
static const float TRANSLATION_MOVE_STEP;
static const uint32_t TRANSLATION_MOVE_SPEED;
FreeCamera(Node &node);
virtual ~FreeCamera() = default;
virtual void update(float delta_time) override;
virtual void input_event(const InputEvent &input_event) override;
virtual void resize(uint32_t width, uint32_t height) override;
private:
float speed_multiplier{3.0f};
glm::vec2 mouse_move_delta{0.0f};
glm::vec2 mouse_last_pos{0.0f};
glm::vec2 touch_move_delta{0.0f};
glm::vec2 touch_last_pos{0.0f};
float touch_pointer_time{0.0f};
std::unordered_map<KeyCode, bool> key_pressed;
std::unordered_map<MouseButton, bool> mouse_button_pressed;
std::unordered_map<int32_t, bool> touch_pointer_pressed;
};
} // namespace sg
} // namespace vkb
@@ -0,0 +1,58 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "node_animation.h"
#include "common/error.h"
#include "common/glm_common.h"
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/quaternion.hpp>
#include "scene_graph/components/perspective_camera.h"
#include "scene_graph/components/transform.h"
#include "scene_graph/node.h"
namespace vkb
{
namespace sg
{
NodeAnimation::NodeAnimation(Node &node, TransformAnimFn animation_fn) :
NodeScript{node, ""},
animation_fn{animation_fn}
{
}
void NodeAnimation::update(float delta_time)
{
if (animation_fn)
{
animation_fn(get_node().get_component<Transform>(), delta_time);
}
}
void NodeAnimation::set_animation(TransformAnimFn handle)
{
animation_fn = handle;
}
void NodeAnimation::clear_animation()
{
animation_fn = {};
}
} // namespace sg
} // namespace vkb
@@ -0,0 +1,56 @@
/* Copyright (c) 2019-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "scene_graph/components/transform.h"
#include "scene_graph/script.h"
/**
* @param std::shared_ptr<vkb::sg::Transform> The transform to animate
* @param float The delta time of the frame to scale animations
*/
using TransformAnimFn = std::function<void(vkb::sg::Transform &, float)>;
namespace vkb
{
namespace sg
{
class NodeAnimation : public NodeScript
{
public:
NodeAnimation(Node &node, TransformAnimFn animation_fn);
virtual ~NodeAnimation() = default;
virtual void update(float delta_time) override;
void set_animation(TransformAnimFn handle);
void clear_animation();
private:
TransformAnimFn animation_fn{};
};
} // namespace sg
} // namespace vkb