This commit is contained in:
2024-02-07 05:55:45 -05:00
parent 23fc3371f4
commit d468e71a68
15 changed files with 686 additions and 536 deletions

View File

@@ -31,3 +31,6 @@ if (CMAKE_UPPER_BUILD_TYPE STREQUAL "DEBUG")
else ()
target_compile_definitions(FGLEngine PRIVATE ENABLE_IMGUI=0)
endif ()
#GLM settings
target_compile_definitions(FGLEngine PUBLIC GLM_FORCE_RADIANS GLM_FORCE_DEPTH_ZERO_TO_ONE)

View File

@@ -4,6 +4,9 @@
#include "Camera.hpp"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/string_cast.hpp>
#include <cassert>
#include <limits>
@@ -13,13 +16,7 @@ namespace fgl::engine
void Camera::setOrthographicProjection( float left, float right, float top, float bottom, float near, float far )
{
ZoneScoped;
projection_matrix = glm::mat4 { 1.0f };
projection_matrix[ 0 ][ 0 ] = 2.0f / ( right - left );
projection_matrix[ 1 ][ 1 ] = 2.0f / ( bottom - top );
projection_matrix[ 2 ][ 2 ] = 1.0f / ( far - near );
projection_matrix[ 3 ][ 0 ] = -( right + left ) / ( right - left );
projection_matrix[ 3 ][ 1 ] = -( bottom + top ) / ( bottom - top );
projection_matrix[ 3 ][ 2 ] = -near / ( far - near );
projection_matrix = glm::orthoLH( left, right, bottom, top, near, far );
//TODO: Figure out frustum culling for orthographic projection. (If we even wanna use it)
}
@@ -27,14 +24,7 @@ namespace fgl::engine
void Camera::setPerspectiveProjection( float fovy, float aspect, float near, float far )
{
ZoneScoped;
assert( glm::abs( aspect - std::numeric_limits< float >::epsilon() ) > 0 );
const float tan_half_fovy { std::tan( fovy / 2.0f ) };
projection_matrix = glm::mat4 { 0.0f };
projection_matrix[ 0 ][ 0 ] = 1.0f / ( aspect * tan_half_fovy );
projection_matrix[ 1 ][ 1 ] = 1.0f / ( tan_half_fovy );
projection_matrix[ 2 ][ 2 ] = far / ( far - near );
projection_matrix[ 2 ][ 3 ] = 1.0f;
projection_matrix[ 3 ][ 2 ] = -( far * near ) / ( far - near );
projection_matrix = glm::perspectiveLH( fovy, aspect, near, far );
base_frustum = createFrustum( *this, aspect, fovy, near, far );
}
@@ -42,40 +32,9 @@ namespace fgl::engine
void Camera::setViewDirection( glm::vec3 position, glm::vec3 direction, glm::vec3 up )
{
ZoneScoped;
const glm::vec3 w_direction { glm::normalize( direction ) };
const glm::vec3 u_right { glm::normalize( glm::cross( w_direction, up ) ) };
const glm::vec3 v_up { glm::cross( w_direction, u_right ) };
/*
* view_matrix
* u_r = view_right
* v_u = view_up
* w_d = view_direction
*
* | u_r.x u_r.y u_r.z 0 |
* | v_u.x v_u.y v_u.z 0 |
* | w_d.x w_d.y w_d.z 0 |
* | 0 0 0 1 |
*/
view_matrix = glm::mat4 { 1.0f };
view_matrix[ 0 ][ 0 ] = u_right.x;
view_matrix[ 1 ][ 0 ] = u_right.y;
view_matrix[ 2 ][ 0 ] = u_right.z;
view_matrix[ 0 ][ 1 ] = v_up.x;
view_matrix[ 1 ][ 1 ] = v_up.y;
view_matrix[ 2 ][ 1 ] = v_up.z;
view_matrix[ 0 ][ 2 ] = w_direction.x;
view_matrix[ 1 ][ 2 ] = w_direction.y;
view_matrix[ 2 ][ 2 ] = w_direction.z;
view_matrix[ 3 ][ 0 ] = -glm::dot( u_right, position );
view_matrix[ 3 ][ 1 ] = -glm::dot( v_up, position );
view_matrix[ 3 ][ 2 ] = -glm::dot( w_direction, position );
glm::lookAt( position, position + direction, up );
frustum = base_frustum * view_matrix;
return;
}
void Camera::setViewTarget( glm::vec3 position, glm::vec3 target, glm::vec3 up )
@@ -83,37 +42,110 @@ namespace fgl::engine
setViewDirection( position, glm::normalize( target - position ), up );
}
void Camera::setViewYXZ( glm::vec3 position, glm::vec3 rotation )
enum RotationOrder
{
ZoneScoped;
XYZ,
XZY,
YXZ,
YZX,
ZXY,
ZYX
};
glm::mat4 taitBryanMatrix( const glm::vec3 rotation, const RotationOrder order = XYZ )
{
glm::mat4 mat { 1.0f };
const float c1 { glm::cos( rotation.x ) };
const float s1 { glm::sin( rotation.x ) };
const float c2 { glm::cos( rotation.y ) };
const float s2 { glm::sin( rotation.y ) };
const float c3 { glm::cos( rotation.z ) };
const float s3 { glm::sin( rotation.z ) };
const float c2 { glm::cos( rotation.x ) };
const float s2 { glm::sin( rotation.x ) };
const float c1 { glm::cos( rotation.y ) };
const float s1 { glm::sin( rotation.y ) };
const glm::vec3 u { ( c1 * c3 + s1 * s2 * s3 ), ( c2 * s3 ), ( c1 * s2 * s3 - c3 * s1 ) };
const glm::vec3 v { ( c3 * s1 * s2 - c1 * s3 ), ( c2 * c3 ), ( c1 * c3 * s2 + s1 * s3 ) };
const glm::vec3 w { ( c2 * s1 ), ( -s2 ), ( c1 * c2 ) };
view_matrix = glm::mat4 { 1.0f };
view_matrix[ 0 ][ 0 ] = u.x;
view_matrix[ 1 ][ 0 ] = u.y;
view_matrix[ 2 ][ 0 ] = u.z;
switch ( order )
{
case RotationOrder::XYZ: // Pitch, Yaw, Roll
{
const glm::vec3 row_0 { ( c2 * c3 ), -( c2 * s3 ), s2 };
const glm::vec3 row_1 { ( c1 * s3 ) + ( c3 * s1 * s2 ),
( c1 * c3 ) - ( s1 * s2 * s3 ),
-( c2 * s1 ) };
const glm::vec3 row_2 { ( s1 * s3 ) - ( c1 * c3 * s2 ),
( c3 * s1 ) + ( c1 * s2 * s3 ),
( c1 * c2 ) };
view_matrix[ 0 ][ 1 ] = v.x;
view_matrix[ 1 ][ 1 ] = v.y;
view_matrix[ 2 ][ 1 ] = v.z;
mat[ 0 ] = glm::vec4( row_0, 0.0f );
mat[ 1 ] = glm::vec4( row_1, 0.0f );
mat[ 2 ] = glm::vec4( row_2, 0.0f );
return mat;
}
case RotationOrder::ZYX: // Yaw, Roll, Pitch
{
const glm::vec3 row_0 { ( c1 * c2 ),
( c1 * s2 * s3 ) - ( c3 * s1 ),
( s1 * s3 ) + ( c1 * c3 * s2 ) };
const glm::vec3 row_1 { ( c2 * s1 ),
( c1 * c3 ) + ( s1 * s2 * s3 ),
( c3 * s1 * s2 ) - ( c1 * s3 ) };
const glm::vec3 row_2 { -s2, c2 * s3, c2 * c3 };
view_matrix[ 0 ][ 2 ] = w.x;
view_matrix[ 1 ][ 2 ] = w.y;
view_matrix[ 2 ][ 2 ] = w.z;
mat[ 0 ] = glm::vec4( row_0, 0.0f );
mat[ 1 ] = glm::vec4( row_1, 0.0f );
mat[ 2 ] = glm::vec4( row_2, 0.0f );
return mat;
}
case RotationOrder::YXZ: // Roll, Pitch, Yaw
{
const glm::vec3 row_0 { ( c1 * c3 ) + ( s1 * s2 * s3 ), ( c3 * s1 * s2 ) - ( c1 * s3 ), c2 * s1 };
const glm::vec3 row_1 { c2 * s3, c2 * c3, -s2 };
const glm::vec3 row_2 { ( c1 * s2 * s3 ) - ( c3 * s1 ), ( c1 * c3 * s2 ) + ( s1 * s3 ), c1 * c2 };
mat[ 0 ] = glm::vec4( row_0, 0.0f );
mat[ 1 ] = glm::vec4( row_1, 0.0f );
mat[ 2 ] = glm::vec4( row_2, 0.0f );
return mat;
}
default:
throw std::runtime_error( "Unimplemented rotation order" );
}
}
void Camera::setViewYXZ( glm::vec3 position, glm::vec3 rotation, const ViewMode mode )
{
ZoneScoped;
switch ( mode )
{
case ViewMode::TaitBryan:
{
const glm::mat4 rotation_matrix { taitBryanMatrix( rotation ) };
const glm::vec3 forward { rotation_matrix * glm::vec4( constants::WORLD_FORWARD, 0.0f ) };
const glm::vec3 camera_up { rotation_matrix * glm::vec4( constants::WORLD_UP, 0.0f ) };
view_matrix[ 3 ][ 0 ] = -glm::dot( u, position );
view_matrix[ 3 ][ 1 ] = -glm::dot( v, position );
view_matrix[ 3 ][ 2 ] = -glm::dot( w, position );
view_matrix = glm::lookAtLH( position, position + forward, camera_up );
break;
}
case ViewMode::Euler:
{
//TODO: Implement
//view_matrix = glm::lookAtLH(position, position + );
}
default:
throw std::runtime_error( "Unimplemented view mode" );
}
frustum = base_frustum * view_matrix;
return;
}
Frustum

View File

@@ -70,9 +70,16 @@ namespace fgl::engine
const glm::vec3 getDown() const { return -getUp(); }
void setViewDirection( glm::vec3 pos, glm::vec3 direction, glm::vec3 up = WORLD_UP );
void setViewTarget( glm::vec3 pos, glm::vec3 target, glm::vec3 up = WORLD_UP );
void setViewYXZ( glm::vec3 pos, glm::vec3 rotation );
void setViewDirection( glm::vec3 pos, glm::vec3 direction, glm::vec3 up = constants::WORLD_UP );
void setViewTarget( glm::vec3 pos, glm::vec3 target, glm::vec3 up = constants::WORLD_UP );
enum ViewMode
{
Euler,
TaitBryan
};
void setViewYXZ( glm::vec3 pos, glm::vec3 rotation, const ViewMode mode = TaitBryan );
};
} // namespace fgl::engine

View File

@@ -98,7 +98,7 @@ namespace fgl::engine
Camera camera {};
auto viewer { GameObject::createGameObject() };
viewer.transform.translation.z = -2.5f;
viewer.transform.translation = constants::WORLD_CENTER + glm::vec3( 0.0f, 0.0f, -2.5f );
KeyboardMovementController camera_controller {};
auto current_time { std::chrono::high_resolution_clock::now() };

View File

@@ -4,13 +4,13 @@
#pragma once
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/gtc/matrix_transform.hpp>
#include <memory>
#include <unordered_map>
#include "constants.hpp"
namespace fgl::engine
{
@@ -18,7 +18,7 @@ namespace fgl::engine
struct TransformComponent
{
glm::vec3 translation { 0.0f, 0.0f, 0.0f };
glm::vec3 translation { constants::DEFAULT_VEC3 };
glm::vec3 scale { 1.0f, 1.0f, 1.0f };
glm::vec3 rotation { 0.0f, 0.0f, 0.0f };

30
src/engine/constants.hpp Normal file
View File

@@ -0,0 +1,30 @@
//
// Created by kj16609 on 1/30/24.
//
#pragma once
#include <glm/vec3.hpp>
namespace fgl::engine::constants
{
constexpr glm::vec3 DEFAULT_VEC3 { std::numeric_limits< float >::max() };
constexpr glm::vec3 WORLD_CENTER { 0.0f, 0.0f, 0.0f };
// Z UP
constexpr glm::vec3 WORLD_UP { 0.0f, 0.0f, 1.0f };
constexpr glm::vec3 WORLD_DOWN { -WORLD_UP };
// X RIGHT
constexpr glm::vec3 WORLD_RIGHT { 1.0f, 0.0f, 0.0f };
constexpr glm::vec3 WORLD_LEFT { -WORLD_RIGHT };
// Y FORWARD
constexpr glm::vec3 WORLD_FORWARD { 0.0f, 1.0f, 0.0f };
constexpr glm::vec3 WORLD_BACKWARD { -WORLD_FORWARD };
constexpr float DEFAULT_FLOAT { std::numeric_limits< float >::max() };
} // namespace fgl::engine::constants

View File

@@ -8,8 +8,6 @@
#include <cstring>
#define GLM_ENABLE_EXPERIMENTAL
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/gtx/hash.hpp>
#include <iostream>
@@ -21,30 +19,6 @@
#include "engine/image/ImageView.hpp"
#include "engine/image/Sampler.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Weffc++"
#include "objectloaders/tiny_gltf.h"
#include "objectloaders/tiny_obj_loader.h"
#pragma GCC diagnostic pop
#include "engine/utils.hpp"
namespace std
{
template <>
struct hash< fgl::engine::Vertex >
{
size_t operator()( const fgl::engine::Vertex& vertex ) const
{
std::size_t seed { 0 };
fgl::engine::hashCombine( seed, vertex.m_position, vertex.m_color, vertex.m_normal, vertex.m_uv );
return seed;
}
};
} // namespace std
namespace fgl::engine
{
@@ -102,7 +76,7 @@ namespace fgl::engine
m_draw_parameters( buildParameters( builder.m_primitives ) ),
m_bounding_box( bounding_box )
{
assert( bounding_box.middle != DEFAULT_COORDINATE_VEC3 );
assert( bounding_box.middle != constants::DEFAULT_VEC3 );
m_primitives = std::move( builder.m_primitives );
}
@@ -127,38 +101,6 @@ namespace fgl::engine
}
}
std::vector< vk::VertexInputBindingDescription > Vertex::getBindingDescriptions()
{
std::vector< vk::VertexInputBindingDescription > binding_descriptions {
{ 0, sizeof( Vertex ), vk::VertexInputRate::eVertex },
{ 1, sizeof( ModelMatrixInfo ), vk::VertexInputRate::eInstance }
};
return binding_descriptions;
}
std::vector< vk::VertexInputAttributeDescription > Vertex::getAttributeDescriptions()
{
std::vector< vk::VertexInputAttributeDescription > attribute_descriptions {};
attribute_descriptions.emplace_back( 0, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_position ) );
attribute_descriptions.emplace_back( 1, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_color ) );
attribute_descriptions.emplace_back( 2, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_normal ) );
attribute_descriptions.emplace_back( 3, 0, vk::Format::eR32G32Sfloat, offsetof( Vertex, m_uv ) );
//Normal Matrix
attribute_descriptions.emplace_back( 4, 1, vk::Format::eR32G32B32A32Sfloat, 0 );
attribute_descriptions.emplace_back( 5, 1, vk::Format::eR32G32B32A32Sfloat, 1 * sizeof( glm::vec4 ) );
attribute_descriptions.emplace_back( 6, 1, vk::Format::eR32G32B32A32Sfloat, 2 * sizeof( glm::vec4 ) );
attribute_descriptions.emplace_back( 7, 1, vk::Format::eR32G32B32A32Sfloat, 3 * sizeof( glm::vec4 ) );
attribute_descriptions.emplace_back( 8, 1, vk::Format::eR32Sint, 4 * sizeof( glm::vec4 ) );
static_assert( 4 * sizeof( glm::vec4 ) + sizeof( int ) == sizeof( ModelMatrixInfo ) );
return attribute_descriptions;
}
void ModelBuilder::loadModel( const std::filesystem::path& filepath )
{
if ( filepath.extension() == ".obj" )
@@ -170,381 +112,7 @@ namespace fgl::engine
loadGltf( filepath );
}
else
//Dunno
throw std::runtime_error( "Unknown model file extension" );
}
template < typename T >
std::vector< T > extractData( const tinygltf::Model& model, const tinygltf::Accessor& accessor )
{
if ( accessor.sparse.isSparse )
{
//Sparse loading required
throw std::runtime_error( "Sparse loading not implemeneted" );
}
else
{
auto& buffer_view { model.bufferViews.at( accessor.bufferView ) };
auto& buffer { model.buffers.at( buffer_view.buffer ) };
std::vector< T > data;
std::uint16_t copy_size { 0 };
switch ( accessor.componentType )
{
default:
throw std::runtime_error( "Unhandled access size" );
case TINYGLTF_COMPONENT_TYPE_FLOAT:
copy_size = sizeof( float );
break;
case TINYGLTF_COMPONENT_TYPE_BYTE:
copy_size = sizeof( std::byte );
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
copy_size = sizeof( unsigned int );
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
copy_size = sizeof( unsigned short );
break;
}
switch ( accessor.type )
{
default:
throw std::runtime_error( "UNhandled access type" );
case TINYGLTF_TYPE_VEC3:
copy_size *= 3;
break;
case TINYGLTF_TYPE_VEC2:
copy_size *= 2;
break;
case TINYGLTF_TYPE_SCALAR:
//noop
break;
}
constexpr auto T_SIZE { sizeof( T ) };
assert( T_SIZE == copy_size && "Accessor copy values not greater than or matching sizeof(T)" );
const auto real_size { copy_size * accessor.count };
data.resize( real_size );
std::memcpy( data.data(), buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset, real_size );
return data;
}
};
void ModelBuilder::loadGltf( const std::filesystem::path& filepath )
{
std::cout << "Loading gltf model " << filepath << std::endl;
if ( !std::filesystem::exists( filepath ) ) throw std::runtime_error( "File does not exist" );
m_primitives.clear();
tinygltf::Model model {};
tinygltf::TinyGLTF loader {};
std::string err;
std::string warn;
loader.RemoveImageLoader();
loader.LoadASCIIFromFile( &model, &err, &warn, filepath.string() );
if ( !err.empty() ) throw std::runtime_error( err );
if ( !warn.empty() )
std::cout << "Warning while loading model \"" << filepath.string() << "\"\nWarning:" << warn << std::endl;
for ( const tinygltf::Mesh& mesh : model.meshes )
{
std::vector< glm::vec3 > model_positions;
for ( const tinygltf::Primitive& primitive : mesh.primitives )
{
//TODO: Implement modes
std::cout << "Attributes: \n";
for ( const auto& thing : primitive.attributes )
{
std::cout << "\t" << thing.first << "\n";
}
std::cout << std::endl;
//Load indicies
auto& indicies_accessor { model.accessors.at( primitive.indices ) };
std::vector< std::uint32_t > indicies_data {};
if ( indicies_accessor.componentType == TINYGLTF_COMPONENT_TYPE_INT )
{
indicies_data = extractData< std::uint32_t >( model, model.accessors.at( primitive.indices ) );
}
else if ( indicies_accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
{
auto tmp { extractData< std::uint16_t >( model, model.accessors.at( primitive.indices ) ) };
indicies_data.reserve( tmp.size() );
for ( auto& val : tmp )
{
indicies_data.emplace_back( val );
}
}
//Load positions
auto& position_accessor { model.accessors.at( primitive.attributes.at( "POSITION" ) ) };
std::vector< glm::vec3 > position_data { extractData< glm::vec3 >( model, position_accessor ) };
model_positions.insert( model_positions.end(), position_data.begin(), position_data.end() );
BoundingBox bounding_box { generateBoundingFromPoints( position_data ) };
std::vector< glm::vec3 > normals;
if ( primitive.attributes.find( "NORMAL" ) != primitive.attributes.end() )
{
normals =
extractData< glm::vec3 >( model, model.accessors.at( primitive.attributes.at( "NORMAL" ) ) );
}
else // TODO: Precompute normals if required
normals.resize( position_data.size() );
//TODO: Implement TANGENT reading
std::vector< glm::vec3 > tangents;
std::vector< glm::vec2 > texcoords;
for ( const auto& [ attr_name, attr_idx ] : primitive.attributes )
{
if ( attr_name.starts_with( "TEXCOORD_" ) )
{
//Rip out name and figure out index
const auto idx { std::stoi( attr_name.substr( strlen( "TEXCOORD_" ) ) ) };
if ( idx != 0 ) throw std::runtime_error( "Multiple tex coordinates not supported" );
auto& texcoord_accessor { model.accessors.at( attr_idx ) };
texcoords = extractData< glm::vec2 >( model, texcoord_accessor );
}
}
std::vector< Vertex > verts;
verts.resize( position_data.size() );
for ( std::size_t i = 0; i < position_data.size(); i++ )
{
//Fix position to be -Z UP
//verts[ i ].m_position = position_data[ i ];
verts[ i ].m_position = { position_data[ i ].x, -position_data[ i ].y, position_data[ i ].z };
verts[ i ].m_normal = normals[ i ];
verts[ i ].m_uv = texcoords[ i ];
}
VertexBufferSuballocation vertex_buffer { m_vertex_buffer, verts };
IndexBufferSuballocation index_buffer { m_index_buffer, indicies_data };
if ( primitive.material >= 0 && primitive.material < model.materials.size() )
{
const auto& material { model.materials.at( primitive.material ) };
//TODO: Implement material normals
//Color texture
if ( material.values.contains( "baseColorTexture" ) )
{
const auto& color_texture { material.values.at( "baseColorTexture" ) };
const auto color_index { color_texture.TextureIndex() };
const auto& texture { model.textures.at( color_index ) };
const auto& source { model.images.at( texture.source ) };
const auto& sampler { model.samplers.at( texture.sampler ) };
auto translateFilterToVK = []( const int val ) -> vk::Filter
{
switch ( val )
{
default:
throw std::runtime_error( "Failed to translate filter value to vk filter value" );
case GL_NEAREST:
return vk::Filter::eNearest;
case GL_LINEAR:
[[fallthrough]];
case GL_LINEAR_MIPMAP_LINEAR:
return vk::Filter::eLinear;
}
};
auto translateWarppingToVk = []( const int val ) -> vk::SamplerAddressMode
{
switch ( val )
{
default:
throw std::
runtime_error( "Failed to translate wrapping filter to vk address mode" );
case GL_REPEAT:
return vk::SamplerAddressMode::eRepeat;
#ifdef GL_CLAMP_TO_BORDER
case GL_CLAMP_TO_BORDER:
return vk::SamplerAddressMode::eClampToBorder;
#endif
#ifdef GL_CLAMP_TO_EDGE
case GL_CLAMP_TO_EDGE:
return vk::SamplerAddressMode::eClampToEdge;
#endif
}
};
assert(
sampler.wrapS == sampler.wrapT
&& "Can't support different wrap modes for textures on each axis" );
Texture tex { Texture::loadFromFile( filepath.parent_path() / source.uri ) };
Sampler smp { translateFilterToVK( sampler.minFilter ),
translateFilterToVK( sampler.magFilter ),
vk::SamplerMipmapMode::eLinear,
translateWarppingToVk( sampler.wrapS ) };
tex.getImageView().getSampler() = std::move( smp );
tex.createImGuiSet();
Texture::getTextureDescriptorSet().bindTexture( 0, tex );
Texture::getTextureDescriptorSet().update();
//Stage texture
auto cmd { Device::getInstance().beginSingleTimeCommands() };
tex.stage( cmd );
Device::getInstance().endSingleTimeCommands( cmd );
tex.dropStaging();
Primitive prim {
std::move( vertex_buffer ), std::move( index_buffer ), bounding_box, std::move( tex )
};
m_primitives.emplace_back( std::move( prim ) );
continue;
}
}
else
std::cout << "No material" << std::endl;
Primitive prim { std::move( vertex_buffer ), std::move( index_buffer ), bounding_box };
m_primitives.emplace_back( std::move( prim ) );
}
std::cout << "Mesh has " << mesh.primitives.size() << " primitives" << std::endl;
}
for ( const tinygltf::Scene& scene : model.scenes )
{
std::cout << "Loading scene " << scene.name << std::endl;
std::cout << "Scene has " << scene.nodes.size() << " nodes" << std::endl;
for ( auto child : scene.nodes )
{
std::cout << "Child: " << child << std::endl;
}
}
std::cout << "Scenes: " << model.scenes.size() << std::endl;
std::cout << "Meshes: " << model.meshes.size() << std::endl;
}
void ModelBuilder::loadObj( const std::filesystem::path& filepath )
{
m_primitives.clear();
tinyobj::attrib_t attrib {};
std::vector< tinyobj::shape_t > shapes {};
std::vector< tinyobj::material_t > materials {};
std::string warn {};
std::string error {};
if ( !tinyobj::LoadObj( &attrib, &shapes, &materials, &warn, &error, filepath.string< char >().c_str() ) )
throw std::runtime_error( warn + error );
std::unordered_map< Vertex, std::uint32_t > unique_verts {};
std::vector< Vertex > verts;
std::vector< std::uint32_t > indicies;
for ( const auto& shape : shapes )
{
for ( const auto& index : shape.mesh.indices )
{
Vertex vert {};
if ( index.vertex_index >= 0 )
{
vert.m_position = {
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 0 ) ],
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 1 ) ],
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 2 ) ],
};
vert.m_color = { attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 0 ) ],
attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 1 ) ],
attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 2 ) ] };
assert( vert.m_color[ 0 ] > 0.2f );
assert( vert.m_color[ 1 ] > 0.2f );
assert( vert.m_color[ 2 ] > 0.2f );
}
if ( index.normal_index >= 0 )
{
vert.m_normal = {
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 0 ) ],
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 1 ) ],
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 2 ) ],
};
}
if ( index.texcoord_index >= 0 )
{
vert.m_uv = {
attrib.texcoords[ static_cast< std::uint64_t >( 3 * index.texcoord_index + 0 ) ],
attrib.texcoords[ static_cast< std::uint64_t >( 3 * index.texcoord_index + 1 ) ],
};
}
if ( auto itter = unique_verts.find( vert ); itter != unique_verts.end() )
{
indicies.push_back( unique_verts[ vert ] );
}
else
{
const auto insert_op { unique_verts.insert( std::make_pair( vert, verts.size() ) ) };
verts.emplace_back( vert );
if ( insert_op.second )
indicies.push_back( insert_op.first->second );
else
throw std::runtime_error( "Failed to insert new vertex" );
}
}
}
std::vector< glm::vec3 > vert_pos;
for ( const auto& vert : verts )
{
vert_pos.emplace_back( vert.m_position );
}
BoundingBox bounding_box { generateBoundingFromPoints( vert_pos ) };
m_primitives.emplace_back(
VertexBufferSuballocation( m_vertex_buffer, std::move( verts ) ),
IndexBufferSuballocation( m_index_buffer, std::move( indicies ) ),
bounding_box );
std::cout << unique_verts.size() << " unique verts" << std::endl;
}
} // namespace fgl::engine

View File

@@ -4,8 +4,6 @@
#pragma once
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <algorithm>
@@ -15,6 +13,7 @@
#include <vector>
#include "BoundingBox.hpp"
#include "Vertex.hpp"
#include "engine/Device.hpp"
#include "engine/buffers/Buffer.hpp"
#include "engine/buffers/BufferSuballocation.hpp"
@@ -26,25 +25,6 @@
namespace fgl::engine
{
struct Vertex
{
glm::vec3 m_position { 0.0f, 0.0f, 0.0f };
glm::vec3 m_color { 1.0f, 1.0f, 1.0f };
glm::vec3 m_normal { 0.0f, 0.0f, 0.0f };
glm::vec2 m_uv { 0.0f, 0.0f };
static std::vector< vk::VertexInputBindingDescription > getBindingDescriptions();
static std::vector< vk::VertexInputAttributeDescription > getAttributeDescriptions();
Vertex() noexcept = default;
bool operator==( const Vertex& other ) const
{
return m_position == other.m_position && m_color == other.m_color && m_normal == other.m_normal
&& m_uv == other.m_uv;
}
};
struct ModelMatrixInfo
{
glm::mat4 model_matrix;

View File

@@ -0,0 +1,64 @@
//
// Created by kj16609 on 2/5/24.
//
#include "Vertex.hpp"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/hash.hpp>
#include "Model.hpp"
#include "engine/utils.hpp"
namespace fgl::engine
{
bool Vertex::operator==( const Vertex& other ) const
{
return m_position == other.m_position && m_color == other.m_color && m_normal == other.m_normal
&& m_uv == other.m_uv;
}
std::vector< vk::VertexInputBindingDescription > Vertex::getBindingDescriptions()
{
std::vector< vk::VertexInputBindingDescription > binding_descriptions {
{ 0, sizeof( Vertex ), vk::VertexInputRate::eVertex },
{ 1, sizeof( ModelMatrixInfo ), vk::VertexInputRate::eInstance }
};
return binding_descriptions;
}
std::vector< vk::VertexInputAttributeDescription > Vertex::getAttributeDescriptions()
{
std::vector< vk::VertexInputAttributeDescription > attribute_descriptions {};
attribute_descriptions.emplace_back( 0, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_position ) );
attribute_descriptions.emplace_back( 1, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_color ) );
attribute_descriptions.emplace_back( 2, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_normal ) );
attribute_descriptions.emplace_back( 3, 0, vk::Format::eR32G32Sfloat, offsetof( Vertex, m_uv ) );
//Normal Matrix
attribute_descriptions.emplace_back( 4, 1, vk::Format::eR32G32B32A32Sfloat, 0 );
attribute_descriptions.emplace_back( 5, 1, vk::Format::eR32G32B32A32Sfloat, 1 * sizeof( glm::vec4 ) );
attribute_descriptions.emplace_back( 6, 1, vk::Format::eR32G32B32A32Sfloat, 2 * sizeof( glm::vec4 ) );
attribute_descriptions.emplace_back( 7, 1, vk::Format::eR32G32B32A32Sfloat, 3 * sizeof( glm::vec4 ) );
attribute_descriptions.emplace_back( 8, 1, vk::Format::eR32Sint, 4 * sizeof( glm::vec4 ) );
static_assert( 4 * sizeof( glm::vec4 ) + sizeof( int ) == sizeof( ModelMatrixInfo ) );
return attribute_descriptions;
}
} // namespace fgl::engine
namespace std
{
std::size_t hash< fgl::engine::Vertex >::operator()( const fgl::engine::Vertex& vertex ) const
{
std::size_t seed { 0 };
fgl::engine::hashCombine( seed, vertex.m_position, vertex.m_color, vertex.m_normal, vertex.m_uv );
return seed;
}
} // namespace std

View File

@@ -0,0 +1,40 @@
//
// Created by kj16609 on 2/5/24.
//
#pragma once
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <vulkan/vulkan.hpp>
namespace fgl::engine
{
struct Vertex
{
glm::vec3 m_position { 0.0f, 0.0f, 0.0f };
glm::vec3 m_color { 1.0f, 1.0f, 1.0f };
glm::vec3 m_normal { 0.0f, 0.0f, 0.0f };
glm::vec2 m_uv { 0.0f, 0.0f };
static std::vector< vk::VertexInputBindingDescription > getBindingDescriptions();
static std::vector< vk::VertexInputAttributeDescription > getAttributeDescriptions();
Vertex() noexcept = default;
bool operator==( const Vertex& other ) const;
};
} // namespace fgl::engine
namespace std
{
template <>
struct hash< fgl::engine::Vertex >
{
std::size_t operator()( const fgl::engine::Vertex& vertex ) const;
};
} // namespace std

View File

@@ -0,0 +1,323 @@
//
// Created by kj16609 on 2/5/24.
//
#include "Model.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Weffc++"
#include "engine/image/Sampler.hpp"
#include "objectloaders/tiny_gltf.h"
#pragma GCC diagnostic pop
#include "engine/descriptors/DescriptorSet.hpp"
#include "engine/image/ImageView.hpp"
namespace fgl::engine
{
template < typename T >
std::vector< T > extractData( const tinygltf::Model& model, const tinygltf::Accessor& accessor )
{
if ( accessor.sparse.isSparse )
{
//Sparse loading required
throw std::runtime_error( "Sparse loading not implemeneted" );
}
else
{
auto& buffer_view { model.bufferViews.at( accessor.bufferView ) };
auto& buffer { model.buffers.at( buffer_view.buffer ) };
std::vector< T > data;
std::uint16_t copy_size { 0 };
switch ( accessor.componentType )
{
default:
throw std::runtime_error( "Unhandled access size" );
case TINYGLTF_COMPONENT_TYPE_FLOAT:
copy_size = sizeof( float );
break;
case TINYGLTF_COMPONENT_TYPE_BYTE:
copy_size = sizeof( std::byte );
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
copy_size = sizeof( unsigned int );
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
copy_size = sizeof( unsigned short );
break;
}
switch ( accessor.type )
{
default:
throw std::runtime_error( "UNhandled access type" );
case TINYGLTF_TYPE_VEC3:
copy_size *= 3;
break;
case TINYGLTF_TYPE_VEC2:
copy_size *= 2;
break;
case TINYGLTF_TYPE_SCALAR:
//noop
break;
}
constexpr auto T_SIZE { sizeof( T ) };
assert( T_SIZE == copy_size && "Accessor copy values not greater than or matching sizeof(T)" );
const auto real_size { copy_size * accessor.count };
data.resize( real_size );
std::memcpy( data.data(), buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset, real_size );
return data;
}
}
template <>
std::vector< ModelCoordinate > extractData<
ModelCoordinate >( const tinygltf::Model& model, const tinygltf::Accessor& accessor )
{
const std::vector< glm::vec3 > data { extractData< glm::vec3 >( model, accessor ) };
std::vector< ModelCoordinate > ret;
ret.reserve( data.size() );
for ( const auto& val : data )
{
ret.emplace_back( val );
}
return ret;
}
void ModelBuilder::loadGltf( const std::filesystem::path& filepath )
{
std::cout << "Loading gltf model " << filepath << std::endl;
if ( !std::filesystem::exists( filepath ) ) throw std::runtime_error( "File does not exist" );
m_primitives.clear();
tinygltf::Model model {};
tinygltf::TinyGLTF loader {};
std::string err;
std::string warn;
loader.RemoveImageLoader();
loader.LoadASCIIFromFile( &model, &err, &warn, filepath.string() );
if ( !err.empty() ) throw std::runtime_error( err );
if ( !warn.empty() )
std::cout << "Warning while loading model \"" << filepath.string() << "\"\nWarning:" << warn << std::endl;
for ( const tinygltf::Mesh& mesh : model.meshes )
{
std::vector< glm::vec3 > model_positions;
for ( const tinygltf::Primitive& primitive : mesh.primitives )
{
//TODO: Implement modes
std::cout << "Attributes: \n";
for ( const auto& thing : primitive.attributes )
{
std::cout << "\t" << thing.first << "\n";
}
std::cout << std::endl;
//Load indicies
auto& indicies_accessor { model.accessors.at( primitive.indices ) };
std::vector< std::uint32_t > indicies_data {};
if ( indicies_accessor.componentType == TINYGLTF_COMPONENT_TYPE_INT )
{
indicies_data = extractData< std::uint32_t >( model, model.accessors.at( primitive.indices ) );
}
else if ( indicies_accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
{
auto tmp { extractData< std::uint16_t >( model, model.accessors.at( primitive.indices ) ) };
indicies_data.reserve( tmp.size() );
for ( auto& val : tmp )
{
indicies_data.emplace_back( val );
}
}
//Load positions
auto& position_accessor { model.accessors.at( primitive.attributes.at( "POSITION" ) ) };
std::vector< ModelCoordinate > position_data {
extractData< ModelCoordinate >( model, position_accessor )
};
model_positions.insert( model_positions.end(), position_data.begin(), position_data.end() );
ModelBoundingBox bounding_box { generateBoundingFromPoints< CoordinateType::Model >( position_data ) };
std::vector< glm::vec3 > normals;
if ( primitive.attributes.find( "NORMAL" ) != primitive.attributes.end() )
{
normals =
extractData< glm::vec3 >( model, model.accessors.at( primitive.attributes.at( "NORMAL" ) ) );
}
else // TODO: Precompute normals if required
normals.resize( position_data.size() );
//TODO: Implement TANGENT reading
std::vector< glm::vec3 > tangents;
std::vector< glm::vec2 > texcoords;
for ( const auto& [ attr_name, attr_idx ] : primitive.attributes )
{
if ( attr_name.starts_with( "TEXCOORD_" ) )
{
//Rip out name and figure out index
const auto idx { std::stoi( attr_name.substr( strlen( "TEXCOORD_" ) ) ) };
if ( idx != 0 ) throw std::runtime_error( "Multiple tex coordinates not supported" );
auto& texcoord_accessor { model.accessors.at( attr_idx ) };
texcoords = extractData< glm::vec2 >( model, texcoord_accessor );
}
}
std::vector< Vertex > verts;
verts.resize( position_data.size() );
for ( std::size_t i = 0; i < position_data.size(); i++ )
{
//Fix position to be -Z UP
//verts[ i ].m_position = position_data[ i ];
verts[ i ].m_position =
glm::vec3( position_data[ i ].x, -position_data[ i ].z, -position_data[ i ].y );
verts[ i ].m_normal = normals[ i ];
verts[ i ].m_uv = texcoords[ i ];
}
VertexBufferSuballocation vertex_buffer { m_vertex_buffer, verts };
IndexBufferSuballocation index_buffer { m_index_buffer, indicies_data };
if ( primitive.material >= 0 && primitive.material < model.materials.size() )
{
const auto& material { model.materials.at( primitive.material ) };
//TODO: Implement material normals
//Color texture
if ( material.values.contains( "baseColorTexture" ) )
{
const auto& color_texture { material.values.at( "baseColorTexture" ) };
const auto color_index { color_texture.TextureIndex() };
const auto& texture { model.textures.at( color_index ) };
const auto& source { model.images.at( texture.source ) };
const auto& sampler { model.samplers.at( texture.sampler ) };
auto translateFilterToVK = []( const int val ) -> vk::Filter
{
switch ( val )
{
default:
throw std::runtime_error( "Failed to translate filter value to vk filter value" );
case GL_NEAREST:
return vk::Filter::eNearest;
case GL_LINEAR:
[[fallthrough]];
case GL_LINEAR_MIPMAP_LINEAR:
return vk::Filter::eLinear;
}
};
auto translateWarppingToVk = []( const int val ) -> vk::SamplerAddressMode
{
switch ( val )
{
default:
throw std::
runtime_error( "Failed to translate wrapping filter to vk address mode" );
case GL_REPEAT:
return vk::SamplerAddressMode::eRepeat;
#ifdef GL_CLAMP_TO_BORDER
case GL_CLAMP_TO_BORDER:
return vk::SamplerAddressMode::eClampToBorder;
#endif
#ifdef GL_CLAMP_TO_EDGE
case GL_CLAMP_TO_EDGE:
return vk::SamplerAddressMode::eClampToEdge;
#endif
}
};
assert(
sampler.wrapS == sampler.wrapT
&& "Can't support different wrap modes for textures on each axis" );
Texture tex { Texture::loadFromFile( filepath.parent_path() / source.uri ) };
Sampler smp { translateFilterToVK( sampler.minFilter ),
translateFilterToVK( sampler.magFilter ),
vk::SamplerMipmapMode::eLinear,
translateWarppingToVk( sampler.wrapS ) };
tex.getImageView().getSampler() = std::move( smp );
tex.createImGuiSet();
Texture::getTextureDescriptorSet().bindTexture( 0, tex );
Texture::getTextureDescriptorSet().update();
//Stage texture
auto cmd { Device::getInstance().beginSingleTimeCommands() };
tex.stage( cmd );
Device::getInstance().endSingleTimeCommands( cmd );
tex.dropStaging();
Primitive prim {
std::move( vertex_buffer ), std::move( index_buffer ), bounding_box, std::move( tex )
};
m_primitives.emplace_back( std::move( prim ) );
continue;
}
}
else
std::cout << "No material" << std::endl;
Primitive prim { std::move( vertex_buffer ), std::move( index_buffer ), bounding_box };
m_primitives.emplace_back( std::move( prim ) );
}
std::cout << "Mesh has " << mesh.primitives.size() << " primitives" << std::endl;
}
for ( const tinygltf::Scene& scene : model.scenes )
{
std::cout << "Loading scene " << scene.name << std::endl;
std::cout << "Scene has " << scene.nodes.size() << " nodes" << std::endl;
for ( auto child : scene.nodes )
{
std::cout << "Child: " << child << std::endl;
}
}
std::cout << "Scenes: " << model.scenes.size() << std::endl;
std::cout << "Meshes: " << model.meshes.size() << std::endl;
}
} // namespace fgl::engine

View File

@@ -0,0 +1,107 @@
//
// Created by kj16609 on 2/5/24.
//
#include "Model.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Weffc++"
#include "objectloaders/tiny_obj_loader.h"
#pragma GCC diagnostic pop
#include "Vertex.hpp"
namespace fgl::engine
{
void ModelBuilder::loadObj( const std::filesystem::path& filepath )
{
m_primitives.clear();
tinyobj::attrib_t attrib {};
std::vector< tinyobj::shape_t > shapes {};
std::vector< tinyobj::material_t > materials {};
std::string warn {};
std::string error {};
if ( !tinyobj::LoadObj( &attrib, &shapes, &materials, &warn, &error, filepath.string< char >().c_str() ) )
throw std::runtime_error( warn + error );
std::unordered_map< Vertex, std::uint32_t > unique_verts {};
std::vector< Vertex > verts;
std::vector< std::uint32_t > indicies;
for ( const auto& shape : shapes )
{
for ( const auto& index : shape.mesh.indices )
{
Vertex vert {};
if ( index.vertex_index >= 0 )
{
vert.m_position = {
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 0 ) ],
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 1 ) ],
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 2 ) ],
};
vert.m_color = { attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 0 ) ],
attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 1 ) ],
attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 2 ) ] };
assert( vert.m_color[ 0 ] > 0.2f );
assert( vert.m_color[ 1 ] > 0.2f );
assert( vert.m_color[ 2 ] > 0.2f );
}
if ( index.normal_index >= 0 )
{
vert.m_normal = {
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 0 ) ],
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 1 ) ],
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 2 ) ],
};
}
if ( index.texcoord_index >= 0 )
{
vert.m_uv = {
attrib.texcoords[ static_cast< std::uint64_t >( 3 * index.texcoord_index + 0 ) ],
attrib.texcoords[ static_cast< std::uint64_t >( 3 * index.texcoord_index + 1 ) ],
};
}
if ( auto itter = unique_verts.find( vert ); itter != unique_verts.end() )
{
indicies.push_back( unique_verts[ vert ] );
}
else
{
const auto insert_op { unique_verts.insert( std::make_pair( vert, verts.size() ) ) };
verts.emplace_back( vert );
if ( insert_op.second )
indicies.push_back( insert_op.first->second );
else
throw std::runtime_error( "Failed to insert new vertex" );
}
}
}
std::vector< ModelCoordinate > vert_pos;
for ( const auto& vert : verts )
{
vert_pos.emplace_back( vert.m_position );
}
BoundingBox bounding_box { generateBoundingFromPoints( vert_pos ) };
m_primitives.emplace_back(
VertexBufferSuballocation( m_vertex_buffer, std::move( verts ) ),
IndexBufferSuballocation( m_index_buffer, std::move( indicies ) ),
bounding_box );
std::cout << unique_verts.size() << " unique verts" << std::endl;
}
} // namespace fgl::engine

View File

@@ -4,12 +4,9 @@
#include "EntityRendererSystem.hpp"
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ON
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/intersect.hpp>
#include <imgui/imgui.h>
#include <tracy/TracyC.h>
#include <vulkan/vulkan.hpp>
@@ -102,7 +99,7 @@ namespace fgl::engine
debug::world::drawBoundingBox( model_bounding_box, info.camera );
if ( !model_bounding_box.isInFrustum( info.camera_frustum ) ) continue;
//if ( !model_bounding_box.isInFrustum( info.camera_frustum ) ) continue;
for ( const auto& primitive : obj.model->m_primitives )
{

View File

@@ -10,7 +10,7 @@ namespace fgl::engine
{
template < typename T, typename... Ts >
void hashCombine( std::size_t& seed, const T& v, const Ts&... ts )
inline void hashCombine( std::size_t& seed, const T& v, const Ts&... ts )
{
std::hash< T > hasher;
seed ^= hasher( v ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 );