Cleanup
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() };
|
||||
|
||||
@@ -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
30
src/engine/constants.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
64
src/engine/model/Vertex.cpp
Normal file
64
src/engine/model/Vertex.cpp
Normal 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
|
||||
40
src/engine/model/Vertex.hpp
Normal file
40
src/engine/model/Vertex.hpp
Normal 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
|
||||
323
src/engine/model/gltfLoading.cpp
Normal file
323
src/engine/model/gltfLoading.cpp
Normal 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
|
||||
107
src/engine/model/objLoading.cpp
Normal file
107
src/engine/model/objLoading.cpp
Normal 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
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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 );
|
||||
|
||||
Reference in New Issue
Block a user