normals and ortho starting:
This commit is contained in:
9
assets/shaders/camera.slang
Normal file
9
assets/shaders/camera.slang
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef CAMERA_SLANG
|
||||
#define CAMERA_SLANG
|
||||
|
||||
struct camera_data {
|
||||
float4x4 view;
|
||||
float4x4 projection;
|
||||
};
|
||||
|
||||
#endif
|
||||
31
assets/shaders/render_mesh.slang
Normal file
31
assets/shaders/render_mesh.slang
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "camera.slang"
|
||||
|
||||
[[vk_binding(0, 0)]] ConstantBuffer<camera_data> camera;
|
||||
|
||||
struct input_vertex
|
||||
{
|
||||
[[vk::location(0)]] float3 pos : POSITION;
|
||||
[[vk::location(1)]] float3 normal : NORMAL;
|
||||
[[vk::location(2)]] float2 texcoord : TEXCOORD;
|
||||
};
|
||||
|
||||
struct post_transform_vertex {
|
||||
float4 position : SV_Position;
|
||||
float3 normal : NORMAL;
|
||||
float2 texcoord : TEXCOORD;
|
||||
};
|
||||
|
||||
[shader("vertex")]
|
||||
post_transform_vertex vert_main(input_vertex vertex) {
|
||||
post_transform_vertex output;
|
||||
output.position = mul(camera.projection, mul(camera.view, float4(vertex.pos, 1.0f)));
|
||||
output.normal = vertex.normal;
|
||||
output.texcoord = vertex.texcoord;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
[shader("fragment")]
|
||||
float4 frag_main(post_transform_vertex input) : SV_Target {
|
||||
return float4(input.normal * 0.5f + 0.5f, 1.0f);
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
#include "engine.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <glm/trigonometric.hpp>
|
||||
#include <limits>
|
||||
#include <ranges>
|
||||
|
||||
#include "glm/ext/matrix_clip_space.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto load_scene_to_renderer(trb::render::renderer &renderer,
|
||||
trb::game::scene &scene) {
|
||||
@@ -18,6 +23,85 @@ namespace {
|
||||
std::views::transform(upload_mesh_node) |
|
||||
std::ranges::to<std::vector<std::uint32_t>>();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<glm::vec3, glm::vec3>
|
||||
bounds_of(const trb::game::scene &scene) {
|
||||
auto min = glm::vec3(std::numeric_limits<float>::infinity());
|
||||
auto max = glm::vec3(-std::numeric_limits<float>::infinity());
|
||||
|
||||
const auto is_mesh_node = [](const auto &v) {
|
||||
return std::holds_alternative<trb::game::mesh_node>(v);
|
||||
};
|
||||
|
||||
for (const auto &v : scene.nodes | std::views::filter(is_mesh_node)) {
|
||||
const auto &mesh = std::get<trb::game::mesh_node>(v);
|
||||
for (const auto [position, normal, uv] : mesh.vertices) {
|
||||
min = glm::min(min, position);
|
||||
max = glm::max(max, position);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(min, max);
|
||||
}
|
||||
|
||||
struct level_view {
|
||||
glm::mat4 view;
|
||||
glm::mat4 projection;
|
||||
};
|
||||
[[nodiscard]] level_view calculate_camera_matrices(glm::vec3 min, glm::vec3 max,
|
||||
float aspectRatio,
|
||||
float zoom = 1.0f) {
|
||||
// 1. Calculate the X/Z center. We ignore the Y center per your requirement.
|
||||
glm::vec3 center =
|
||||
glm::vec3((min.x + max.x) * 0.5f, 0.0f, (min.z + max.z) * 0.5f);
|
||||
|
||||
// 2. Position the camera.
|
||||
// For 45 degrees in Y-up: we move 'distance' up (Y) and 'distance' back (Z).
|
||||
float distance = 100.0f;
|
||||
glm::vec3 cameraPos = center + glm::vec3(0.0f, distance, distance);
|
||||
|
||||
// 3. View Matrix
|
||||
// Up is (0, 1, 0). Camera looks at the X/Z center.
|
||||
glm::mat4 view = glm::lookAt(cameraPos, center, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
// 4. Determine Ortho Bounds
|
||||
// We must check all corners because at 45 degrees, the Y-height
|
||||
// of objects affects their projected X/Y position on screen.
|
||||
glm::vec3 corners[8] = {{min.x, min.y, min.z}, {max.x, min.y, min.z},
|
||||
{min.x, max.y, min.z}, {max.x, max.y, min.z},
|
||||
{min.x, min.y, max.z}, {max.x, min.y, max.z},
|
||||
{min.x, max.y, max.z}, {max.x, max.y, max.z}};
|
||||
|
||||
float vMinX = FLT_MAX, vMaxX = -FLT_MAX;
|
||||
float vMinY = FLT_MAX, vMaxY = -FLT_MAX;
|
||||
|
||||
for (const auto &corner : corners) {
|
||||
glm::vec4 vSpace = view * glm::vec4(corner, 1.0f);
|
||||
vMinX = glm::min(vMinX, vSpace.x);
|
||||
vMaxX = glm::max(vMaxX, vSpace.x);
|
||||
vMinY = glm::min(vMinY, vSpace.y);
|
||||
vMaxY = glm::max(vMaxY, vSpace.y);
|
||||
}
|
||||
|
||||
// 5. Apply Zoom and Aspect Ratio
|
||||
float width = (vMaxX - vMinX) / zoom;
|
||||
float height = (vMaxY - vMinY) / zoom;
|
||||
float midX = (vMinX + vMaxX) * 0.5f;
|
||||
float midY = (vMinY + vMaxY) * 0.5f;
|
||||
|
||||
if (aspectRatio > (width / height)) {
|
||||
width = height * aspectRatio;
|
||||
} else {
|
||||
height = width / aspectRatio;
|
||||
}
|
||||
|
||||
glm::mat4 projection =
|
||||
glm::ortho(midX - width * 0.5f, midX + width * 0.5f, midY - height * 0.5f,
|
||||
midY + height * 0.5f, -1000.0f, 1000.0f);
|
||||
|
||||
return {view, projection};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace trb::game {
|
||||
@@ -30,7 +114,10 @@ engine::engine()
|
||||
void engine::run() {
|
||||
while (!display.should_close()) {
|
||||
try {
|
||||
renderer.render();
|
||||
const auto [map_min, map_max] = ::bounds_of(level_border);
|
||||
const auto [view, projection] =
|
||||
::calculate_camera_matrices(map_min, map_max, 1920.0f / 1080.0f);
|
||||
renderer.render(level_border_render_ids, view, projection);
|
||||
} catch (const vuk::VkException &ex) {
|
||||
if (ex.what() != std::string("Out of date.")) {
|
||||
std::rethrow_exception(std::current_exception());
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <fastgltf/core.hpp>
|
||||
#include <fastgltf/tools.hpp>
|
||||
#include <fastgltf/util.hpp>
|
||||
#include <ranges>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
@@ -15,6 +17,9 @@ namespace {
|
||||
assert(position_it != it->attributes.end());
|
||||
assert(it->indicesAccessor.has_value());
|
||||
|
||||
auto *normal_it = it->findAttribute("NORMAL");
|
||||
assert(normal_it != it->attributes.end());
|
||||
|
||||
const auto index = std::distance(mesh.primitives.begin(), it);
|
||||
|
||||
auto &primitive = meshes.emplace_back();
|
||||
@@ -22,8 +27,7 @@ namespace {
|
||||
throw std::runtime_error("only triangular meshes are supported");
|
||||
}
|
||||
|
||||
// TODO: Materials, texcoords
|
||||
|
||||
auto positions = std::vector<glm::vec3>();
|
||||
{
|
||||
auto &position_accessor = asset.accessors[position_it->accessorIndex];
|
||||
if (!position_accessor.bufferViewIndex.has_value())
|
||||
@@ -32,7 +36,7 @@ namespace {
|
||||
fastgltf::iterateAccessorWithIndex<fastgltf::math::fvec3>(
|
||||
asset, position_accessor,
|
||||
[&](fastgltf::math::fvec3 pos, std::size_t idx) {
|
||||
primitive.positions.emplace_back(pos.x(), pos.y(), pos.z());
|
||||
positions.emplace_back(pos.x(), pos.y(), pos.z());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,6 +51,29 @@ namespace {
|
||||
fastgltf::copyFromAccessor<std::uint32_t>(asset, index_accessor,
|
||||
primitive.indices.data());
|
||||
}
|
||||
|
||||
auto normals = std::vector<glm::vec3>();
|
||||
{
|
||||
auto &normal_accessor = asset.accessors[normal_it->accessorIndex];
|
||||
if (!normal_accessor.bufferViewIndex.has_value()) {
|
||||
throw std::runtime_error(
|
||||
"no index buffer was specified for mesh primitive");
|
||||
}
|
||||
|
||||
fastgltf::iterateAccessorWithIndex<fastgltf::math::fvec3>(
|
||||
asset, normal_accessor,
|
||||
[&](fastgltf::math::fvec3 pos, std::size_t idx) {
|
||||
normals.emplace_back(pos.x(), pos.y(), pos.z());
|
||||
});
|
||||
}
|
||||
|
||||
for (const auto [position, normal] : std::views::zip(positions, normals)) {
|
||||
primitive.vertices.push_back({
|
||||
.position = position,
|
||||
.normal = normal,
|
||||
.uv = glm::vec2(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return meshes;
|
||||
|
||||
@@ -9,8 +9,14 @@
|
||||
|
||||
namespace trb::game {
|
||||
|
||||
struct vertex {
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 uv;
|
||||
};
|
||||
|
||||
struct mesh_node {
|
||||
std::vector<glm::vec3> positions;
|
||||
std::vector<vertex> vertices;
|
||||
std::vector<std::uint32_t> indices;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,59 @@
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
[[nodiscard]] auto render_mesh() {
|
||||
return vuk::make_pass(
|
||||
"render mesh", [](vuk::CommandBuffer &command_buffer,
|
||||
VUK_BA(vuk::eMemoryRead) camera_buffer,
|
||||
VUK_BA(vuk::eIndirectRead) indirect_buffer,
|
||||
VUK_BA(vuk::eVertexRead) vertex_buffer,
|
||||
VUK_BA(vuk::eIndexRead) index_buffer,
|
||||
VUK_IA(vuk::eColorWrite) color_target) {
|
||||
auto attributes = std::vector<vuk::VertexInputAttributeDescription>();
|
||||
attributes.push_back({
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = vuk::Format::eR32G32B32Sfloat,
|
||||
.offset = 0,
|
||||
});
|
||||
attributes.push_back({
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = vuk::Format::eR32G32B32Sfloat,
|
||||
.offset = sizeof(glm::vec3),
|
||||
});
|
||||
attributes.push_back({
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = vuk::Format::eR32G32Sfloat,
|
||||
.offset = sizeof(glm::vec3) + sizeof(glm::vec3),
|
||||
});
|
||||
|
||||
command_buffer.bind_graphics_pipeline("mesh")
|
||||
.set_dynamic_state(vuk::DynamicStateFlagBits::eScissor |
|
||||
vuk::DynamicStateFlagBits::eViewport)
|
||||
.set_viewport(0, vuk::Rect2D::framebuffer())
|
||||
.set_scissor(0, vuk::Rect2D::framebuffer())
|
||||
.set_rasterization({
|
||||
//.polygonMode = vuk::PolygonMode::eLine,
|
||||
})
|
||||
/*
|
||||
.set_depth_stencil({
|
||||
.depthTestEnable = false,
|
||||
.depthWriteEnable = false,
|
||||
.depthCompareOp = vuk::CompareOp::eGreater,
|
||||
})*/
|
||||
.set_color_blend(color_target, {})
|
||||
.bind_vertex_buffer(0, vertex_buffer, attributes, sizeof(float) * 8)
|
||||
.bind_index_buffer(index_buffer, vuk::IndexType::eUint32)
|
||||
.bind_buffer(0, 0, camera_buffer)
|
||||
.draw_indexed_indirect(1, indirect_buffer);
|
||||
|
||||
return std::make_tuple(std::move(camera_buffer),
|
||||
std::move(color_target));
|
||||
});
|
||||
}
|
||||
|
||||
namespace trb::render {
|
||||
|
||||
std::uint32_t renderer::next_mesh_id() {
|
||||
@@ -63,7 +116,10 @@ renderer::create_graphics_pipeline(std::string_view module_name) {
|
||||
"frag_main");
|
||||
}
|
||||
|
||||
void renderer::create_pipelines() {}
|
||||
void renderer::create_pipelines() {
|
||||
context.runtime.create_named_pipeline(
|
||||
"mesh", create_graphics_pipeline("render_mesh.slang"));
|
||||
}
|
||||
|
||||
vuk::Value<vuk::ImageAttachment> renderer::get_swap_target() {
|
||||
auto imported_swapchain = vuk::acquire_swapchain(context.swapchain);
|
||||
@@ -83,11 +139,34 @@ renderer::vuk_frame_resources renderer::get_next_frame_resources() {
|
||||
}
|
||||
|
||||
renderer::renderer(ui::display *display)
|
||||
: context(display), rng(std::random_device()()) {}
|
||||
: context(display), rng(std::random_device()()) {
|
||||
create_pipelines();
|
||||
}
|
||||
|
||||
void renderer::render() {
|
||||
void renderer::render(std::span<const std::uint32_t> meshes, glm::mat4 view,
|
||||
glm::mat4 projection) {
|
||||
auto [frame_allocator, frame_target] = get_next_frame_resources();
|
||||
|
||||
struct {
|
||||
glm::mat4 view;
|
||||
glm::mat4 projection;
|
||||
} camera_data{
|
||||
.view = view,
|
||||
.projection = projection,
|
||||
};
|
||||
auto [camera_buffer, camera_future] = vuk::create_buffer(
|
||||
frame_allocator, vuk::MemoryUsage::eCPUtoGPU,
|
||||
vuk::DomainFlagBits::eTransferOperation, std::span(&camera_data, 1));
|
||||
|
||||
for (const auto id : meshes) {
|
||||
auto &mesh = registered_meshes.at(id);
|
||||
std::tie(camera_future, frame_target) = render_mesh()(
|
||||
camera_future,
|
||||
vuk::make_constant("mesh indirect command", *mesh.indirect_command),
|
||||
vuk::make_constant("mesh vertices", *mesh.vertices),
|
||||
vuk::make_constant("mesh indices", *mesh.indices), frame_target);
|
||||
}
|
||||
|
||||
auto entire_thing = vuk::enqueue_presentation(frame_target);
|
||||
entire_thing.submit(frame_allocator, context.compiler);
|
||||
}
|
||||
@@ -97,7 +176,7 @@ std::uint32_t renderer::register_mesh(const game::mesh_node &mesh) {
|
||||
|
||||
auto [vertex_buffer, vertex_future] = vuk::create_buffer(
|
||||
context.superframe_allocator, vuk::MemoryUsage::eCPUtoGPU,
|
||||
vuk::DomainFlagBits::eTransferOperation, std::span(mesh.positions));
|
||||
vuk::DomainFlagBits::eTransferOperation, std::span(mesh.vertices));
|
||||
|
||||
auto [index_buffer, index_future] = vuk::create_buffer(
|
||||
context.superframe_allocator, vuk::MemoryUsage::eCPUtoGPU,
|
||||
|
||||
@@ -56,7 +56,8 @@ private:
|
||||
public:
|
||||
explicit renderer(ui::display *display);
|
||||
|
||||
void render();
|
||||
void render(std::span<const std::uint32_t> meshes, glm::mat4 view,
|
||||
glm::mat4 projection);
|
||||
|
||||
[[nodiscard]] std::uint32_t register_mesh(const game::mesh_node &mesh);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user