normals and ortho starting:

This commit is contained in:
2026-03-31 02:23:15 -03:00
parent 684438cd67
commit 3f8f556960
7 changed files with 250 additions and 10 deletions

View File

@@ -0,0 +1,9 @@
#ifndef CAMERA_SLANG
#define CAMERA_SLANG
struct camera_data {
float4x4 view;
float4x4 projection;
};
#endif

View 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);
}

View File

@@ -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());

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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,

View File

@@ -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);
};