work on model loader

This commit is contained in:
Bailey Harrison 2024-02-03 00:01:01 +00:00
parent 3b81f12d56
commit 620525758d
8 changed files with 114 additions and 299 deletions

View File

@ -128,10 +128,10 @@ struct DescriptorSetLayoutBinding {
}; };
struct SamplerInfo { struct SamplerInfo {
Filter minify; Filter minify = gfx::Filter::kLinear;
Filter magnify; Filter magnify = gfx::Filter::kLinear;
Filter mipmap; Filter mipmap = gfx::Filter::kLinear;
bool anisotropic_filtering; bool anisotropic_filtering = true;
bool operator==(const SamplerInfo&) const = default; bool operator==(const SamplerInfo&) const = default;
}; };

View File

@ -10,14 +10,8 @@ namespace engine {
class Texture { class Texture {
public: public:
enum class Filtering {
kOff,
kBilinear,
kTrilinear,
kAnisotropic,
};
Texture(Renderer* renderer, const uint8_t* bitmap, int width, int height, Filtering filtering, bool srgb); Texture(Renderer* renderer, const uint8_t* bitmap, int width, int height, gfx::SamplerInfo samplerInfo, bool srgb);
~Texture(); ~Texture();
Texture(const Texture&) = delete; Texture(const Texture&) = delete;
@ -32,7 +26,7 @@ class Texture {
const gfx::Sampler* sampler_; // not owned by Texture, owned by Renderer const gfx::Sampler* sampler_; // not owned by Texture, owned by Renderer
}; };
std::unique_ptr<Texture> LoadTextureFromFile(const std::string& path, Texture::Filtering filtering, Renderer* renderer, bool srgb = true); std::unique_ptr<Texture> LoadTextureFromFile(const std::string& path, gfx::SamplerInfo samplerInfo, Renderer* renderer, bool srgb = true);
} // namespace engine } // namespace engine

View File

@ -6,6 +6,15 @@
namespace engine::util { namespace engine::util {
/*
* Loads the default scene found in a glTF file into 'scene'.
* 'isStatic' will mark every transform as static to aid rendering optimisation.
* Returns the top-level glTF node as an engine entity.
*
* Loader limitations:
* - Can only load .glb files
* - glTF files must contain all textures
*/
engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic = false); engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic = false);
} }

View File

@ -167,13 +167,21 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph
/* default textures */ /* default textures */
{ {
auto whiteTexture = LoadTextureFromFile(GetResourcePath("engine/textures/white.png"), Texture::Filtering::kOff, renderer(), true); auto whiteTexture = LoadTextureFromFile(GetResourcePath("engine/textures/white.png"), gfx::SamplerInfo{}, renderer(), true);
GetResourceManager<Texture>()->AddPersistent("builtin.white", std::move(whiteTexture)); GetResourceManager<Texture>()->AddPersistent("builtin.white", std::move(whiteTexture));
} }
{ {
auto normalTexture = LoadTextureFromFile(GetResourcePath("engine/textures/normal.png"), Texture::Filtering::kOff, renderer(), false); auto normalTexture = LoadTextureFromFile(GetResourcePath("engine/textures/normal.png"), gfx::SamplerInfo{}, renderer(), false);
GetResourceManager<Texture>()->AddPersistent("builtin.normal", std::move(normalTexture)); GetResourceManager<Texture>()->AddPersistent("builtin.normal", std::move(normalTexture));
} }
/* default materials */
{
auto defaultMaterial = std::make_unique<Material>(renderer(), GetResource<Shader>("builtin.fancy"));
defaultMaterial->SetAlbedoTexture(GetResource<Texture>("builtin.white"));
defaultMaterial->SetNormalTexture(GetResource<Texture>("builtin.normal"));
GetResourceManager<Material>()->AddPersistent("builtin.default", std::move(defaultMaterial));
}
} }
Application::~Application() Application::~Application()

View File

@ -8,42 +8,13 @@
namespace engine { namespace engine {
Texture::Texture(Renderer* renderer, const uint8_t* bitmap, int width, int height, Filtering filtering, bool srgb) : gfx_(renderer->GetDevice()) Texture::Texture(Renderer* renderer, const uint8_t* bitmap, int width, int height, gfx::SamplerInfo samplerInfo, bool srgb) : gfx_(renderer->GetDevice())
{ {
gfx::SamplerInfo samplerInfo{};
samplerInfo.magnify = gfx::Filter::kLinear;
switch (filtering) {
case Filtering::kOff:
samplerInfo.minify = gfx::Filter::kNearest;
samplerInfo.mipmap = gfx::Filter::kNearest;
samplerInfo.anisotropic_filtering = false;
break;
case Filtering::kBilinear:
samplerInfo.minify = gfx::Filter::kLinear;
samplerInfo.mipmap = gfx::Filter::kNearest;
samplerInfo.anisotropic_filtering = false;
break;
case Filtering::kTrilinear:
samplerInfo.minify = gfx::Filter::kLinear;
samplerInfo.mipmap = gfx::Filter::kLinear;
samplerInfo.anisotropic_filtering = false;
break;
case Filtering::kAnisotropic:
samplerInfo.minify = gfx::Filter::kLinear;
samplerInfo.mipmap = gfx::Filter::kLinear;
samplerInfo.anisotropic_filtering = true;
}
if (renderer->samplers.contains(samplerInfo) == false) { if (renderer->samplers.contains(samplerInfo) == false) {
renderer->samplers.insert(std::make_pair(samplerInfo, gfx_->CreateSampler(samplerInfo))); renderer->samplers.insert(std::make_pair(samplerInfo, gfx_->CreateSampler(samplerInfo)));
} }
gfx::ImageFormat format = gfx::ImageFormat::kLinear; gfx::ImageFormat format = srgb ? gfx::ImageFormat::kSRGB : gfx::ImageFormat::kLinear;
if (srgb) {
format = gfx::ImageFormat::kSRGB;
}
image_ = gfx_->CreateImage(width, height, format, bitmap); image_ = gfx_->CreateImage(width, height, format, bitmap);
sampler_ = renderer->samplers.at(samplerInfo); sampler_ = renderer->samplers.at(samplerInfo);
@ -57,11 +28,11 @@ Texture::~Texture()
gfx_->DestroyImage(image_); gfx_->DestroyImage(image_);
} }
std::unique_ptr<Texture> LoadTextureFromFile(const std::string& path, Texture::Filtering filtering, Renderer* renderer, bool srgb) std::unique_ptr<Texture> LoadTextureFromFile(const std::string& path, gfx::SamplerInfo samplerInfo, Renderer* renderer, bool srgb)
{ {
int width, height; int width, height;
auto bitmap = util::ReadImageFile(path, width, height); auto bitmap = util::ReadImageFile(path, width, height);
return std::make_unique<Texture>(renderer, bitmap->data(), width, height, filtering, srgb); return std::make_unique<Texture>(renderer, bitmap->data(), width, height, samplerInfo, srgb);
} }
} // namespace engine } // namespace engine

View File

@ -53,250 +53,6 @@ static glm::mat4 MatFromDoubleArray(const std::vector<double>& arr)
return mat; return mat;
} }
static void CreateNodes(engine::Scene& app_scene, const tg::Scene& gl_scene, const tg::Model& gl_model, engine::Entity parent_entity, const tg::Node& node)
{
static int node_count = 0;
int node_uuid = node_count++;
glm::vec3 pos{0.0f, 0.0f, 0.0f};
glm::quat rot{0.0f, 0.0f, 0.0f, 1.0f};
glm::vec3 scale{1.0f, 1.0f, 1.0f};
if (node.matrix.size() == 16) {
const glm::mat4 matrix = MatFromDoubleArray(node.matrix);
DecomposeTransform(matrix, pos, rot, scale);
}
else {
if (node.translation.size() == 3) {
pos.x = node.translation[0];
pos.y = node.translation[1];
pos.z = node.translation[2];
}
if (node.rotation.size() == 4) {
rot.x = node.rotation[0];
rot.y = node.rotation[1];
rot.z = node.rotation[2];
rot.w = node.rotation[3];
}
if (node.scale.size() == 3) {
scale.x = node.scale[0];
scale.y = node.scale[1];
scale.z = node.scale[2];
}
}
engine::Entity entity = app_scene.CreateEntity(std::string("test_node") + std::to_string(node_uuid), parent_entity, pos, rot, scale);
if (node.mesh >= 0) {
const tg::Mesh& mesh = gl_model.meshes.at(node.mesh);
const tg::Primitive& prim = mesh.primitives.front(); // required
if (prim.mode != TINYGLTF_MODE_TRIANGLES) throw std::runtime_error("glTF loader only supports triangles!");
const tg::Accessor& indices_accessor = gl_model.accessors.at(prim.indices); // not required. TODO: handle no indices
size_t indices_int_size = 0;
if (indices_accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) indices_int_size = 1;
if (indices_accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) indices_int_size = 2;
if (indices_accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) indices_int_size = 4;
if (indices_int_size == 0) throw std::runtime_error("GLTF parse error!");
const tg::BufferView& indices_bufferview = gl_model.bufferViews.at(indices_accessor.bufferView); // required for TG
const size_t indices_byteoffset = indices_accessor.byteOffset + indices_bufferview.byteOffset;
const tg::Buffer& indices_buffer = gl_model.buffers.at(indices_bufferview.buffer);
std::unique_ptr<std::vector<uint32_t>> indices = nullptr;
if (indices_int_size == 4) {
const uint32_t* const indices_data = reinterpret_cast<const uint32_t*>(indices_buffer.data.data() + indices_byteoffset);
// in future, let Mesh constructor use spans to avoid unneccesary copy here
indices = std::make_unique<std::vector<uint32_t>>(indices_data, indices_data + indices_accessor.count);
}
else if (indices_int_size == 2) {
indices = std::make_unique<std::vector<uint32_t>>();
const uint16_t* const indices_data = reinterpret_cast<const uint16_t*>(indices_buffer.data.data() + indices_byteoffset);
for (size_t i = 0; i < indices_accessor.count; ++i) {
indices->push_back(indices_data[i]);
}
}
if (indices == nullptr) {
throw std::runtime_error("TODO: handle and support this");
}
const tg::Accessor& pos_accessor = gl_model.accessors.at(prim.attributes.at("POSITION"));
if (pos_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) throw std::runtime_error("Position att. must be float!");
if (pos_accessor.type != 3) throw std::runtime_error("Position att. dim. must be 3!");
const tg::BufferView& pos_bufferview = gl_model.bufferViews.at(pos_accessor.bufferView);
const size_t pos_byteoffset = pos_accessor.byteOffset + pos_bufferview.byteOffset;
const size_t pos_bytestride = pos_accessor.ByteStride(pos_bufferview);
const tg::Buffer& pos_buffer = gl_model.buffers.at(pos_bufferview.buffer);
const tg::Accessor& norm_accessor = gl_model.accessors.at(prim.attributes.at("NORMAL"));
if (norm_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) throw std::runtime_error("Normal att. must be float!");
if (norm_accessor.type != 3) throw std::runtime_error("Normal att. dim. must be 3!");
const tg::BufferView& norm_bufferview = gl_model.bufferViews.at(norm_accessor.bufferView);
const size_t norm_byteoffset = norm_accessor.byteOffset + norm_bufferview.byteOffset;
const size_t norm_bytestride = norm_accessor.ByteStride(norm_bufferview);
const tg::Buffer& norm_buffer = gl_model.buffers.at(norm_bufferview.buffer);
std::vector<engine::Vertex> vertices(pos_accessor.count);
if (prim.attributes.contains("TEXCOORD_0")) {
const tg::Accessor& uv_accessor = gl_model.accessors.at(prim.attributes.at("TEXCOORD_0"));
if (uv_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) throw std::runtime_error("UV att. must be float!");
if (uv_accessor.type != 2) throw std::runtime_error("UV att. dim. must be 2!");
const tg::BufferView& uv_bufferview = gl_model.bufferViews.at(uv_accessor.bufferView);
const size_t uv_byteoffset = uv_accessor.byteOffset + uv_bufferview.byteOffset;
const size_t uv_bytestride = uv_accessor.ByteStride(uv_bufferview);
const tg::Buffer& uv_buffer = gl_model.buffers.at(uv_bufferview.buffer);
for (size_t i = 0; i < vertices.size(); ++i) {
vertices[i].uv = *reinterpret_cast<const glm::vec2*>(&uv_buffer.data[uv_byteoffset + uv_bytestride * i]);
}
}
bool has_tangents = false;
if (prim.attributes.contains("TANGENT")) {
has_tangents = true;
}
// copy everything except tangents and uvs
for (size_t i = 0; i < vertices.size(); ++i) {
vertices[i].pos = *reinterpret_cast<const glm::vec3*>(&pos_buffer.data[pos_byteoffset + pos_bytestride * i]);
vertices[i].norm = *reinterpret_cast<const glm::vec3*>(&norm_buffer.data[norm_byteoffset + norm_bytestride * i]);
}
if (has_tangents) {
const tg::Accessor& tangent_accessor = gl_model.accessors.at(prim.attributes.at("TANGENT"));
if (tangent_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) throw std::runtime_error("Tangent att. must be float!");
if (tangent_accessor.type != 4) throw std::runtime_error("Tangent att. dim. must be 4!");
const tg::BufferView& tangent_bufferview = gl_model.bufferViews.at(tangent_accessor.bufferView);
const size_t tangent_byteoffset = tangent_accessor.byteOffset + tangent_bufferview.byteOffset;
const size_t tangent_bytestride = tangent_accessor.ByteStride(tangent_bufferview);
const tg::Buffer& tangent_buffer = gl_model.buffers.at(tangent_bufferview.buffer);
for (size_t i = 0; i < vertices.size(); ++i) {
vertices[i].tangent = *reinterpret_cast<const glm::vec4*>(&tangent_buffer.data[tangent_byteoffset + tangent_bytestride * i]);
}
}
else {
// generate tangents if they're not in the file
struct MeshData {
engine::Vertex* vertices;
const uint32_t* indices;
size_t count;
std::vector<uint32_t> new_indices;
};
MeshData meshData{};
meshData.vertices = vertices.data();
meshData.indices = indices->data();
meshData.count = indices->size();
meshData.new_indices.reserve(meshData.count);
SMikkTSpaceInterface mts_interface{};
mts_interface.m_getNumFaces = [](const SMikkTSpaceContext* pContext) -> int {
const MeshData* meshData = static_cast<const MeshData*>(pContext->m_pUserData);
assert(meshData->count % 3 == 0);
return meshData->count / 3;
};
mts_interface.m_getNumVerticesOfFace = [](const SMikkTSpaceContext*, const int) -> int { return 3; };
mts_interface.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) -> void {
const MeshData* const meshData = static_cast<const MeshData*>(pContext->m_pUserData);
const glm::vec3 pos = meshData->vertices[meshData->indices[iFace * 3 + iVert]].pos;
fvPosOut[0] = pos.x;
fvPosOut[1] = pos.y;
fvPosOut[2] = pos.z;
};
mts_interface.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) -> void {
const MeshData* const meshData = static_cast<const MeshData*>(pContext->m_pUserData);
const glm::vec3 norm = meshData->vertices[meshData->indices[iFace * 3 + iVert]].norm;
fvNormOut[0] = norm.x;
fvNormOut[1] = norm.y;
fvNormOut[2] = norm.z;
};
mts_interface.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) -> void {
const MeshData* const meshData = static_cast<const MeshData*>(pContext->m_pUserData);
const glm::vec2 uv = meshData->vertices[meshData->indices[iFace * 3 + iVert]].uv;
fvTexcOut[0] = uv.x;
fvTexcOut[1] = uv.y;
};
mts_interface.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace,
const int iVert) -> void {
MeshData* const meshData = static_cast<MeshData*>(pContext->m_pUserData);
glm::vec4& tangent = meshData->vertices[meshData->indices[iFace * 3 + iVert]].tangent;
tangent.x = fvTangent[0];
tangent.y = fvTangent[1];
tangent.z = fvTangent[2];
tangent.w = fSign;
};
SMikkTSpaceContext mts_context{};
mts_context.m_pInterface = &mts_interface;
mts_context.m_pUserData = &meshData;
bool tan_result = genTangSpaceDefault(&mts_context);
if (tan_result == false) throw std::runtime_error("Failed to generate tangents!");
}
auto mesh_comp = app_scene.AddComponent<engine::MeshRenderableComponent>(entity);
mesh_comp->mesh = std::make_unique<engine::Mesh>(app_scene.app()->renderer()->GetDevice(), vertices, *indices);
// now get material
mesh_comp->material = std::make_unique<engine::Material>(app_scene.app()->renderer(), app_scene.app()->GetResource<Shader>("builtin.fancy"));
mesh_comp->material->SetAlbedoTexture(app_scene.app()->GetResource<Texture>("builtin.white"));
mesh_comp->material->SetNormalTexture(app_scene.app()->GetResource<Texture>("builtin.normal"));
if (prim.material >= 0) {
const tg::Material& mat = gl_model.materials.at(prim.material);
if (mat.alphaMode != "OPAQUE") {
LOG_WARN("Non-opaque alpha modes are not supported yet");
}
if (mat.doubleSided == true) {
LOG_WARN("Double-sided materials are not supported yet");
}
if (mat.normalTexture.index != -1) {
if (mat.normalTexture.texCoord == 0) {
if (mat.normalTexture.scale == 1.0) {
const tg::Texture& norm_texture = gl_model.textures.at(mat.normalTexture.index);
if (norm_texture.source != -1) {
const tg::Image& norm_image = gl_model.images.at(norm_texture.source);
if (norm_image.as_is == false && norm_image.bits == 8 && norm_image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
// create texture on GPU
mesh_comp->material->SetNormalTexture(std::make_unique<Texture>(app_scene.app()->renderer(), norm_image.image.data(),
norm_image.width, norm_image.height,
Texture::Filtering::kAnisotropic, false));
}
}
}
else {
LOG_WARN("Normal texture has scaling which is unsupported. Ignoring normal map.");
}
}
else {
LOG_WARN("Normal texture doesn't specify UV0. Ignoring normal map.");
}
}
if (mat.pbrMetallicRoughness.baseColorTexture.index != -1) {
if (mat.pbrMetallicRoughness.baseColorTexture.texCoord == 0) {
const tg::Texture& texture = gl_model.textures.at(mat.pbrMetallicRoughness.baseColorTexture.index);
if (texture.source != -1) {
const tg::Image& image = gl_model.images.at(texture.source);
if (image.as_is == false && image.bits == 8 && image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
// create texture on GPU
mesh_comp->material->SetAlbedoTexture(std::make_unique<Texture>(app_scene.app()->renderer(), image.image.data(), image.width,
image.height, Texture::Filtering::kAnisotropic, true));
}
}
}
else {
LOG_WARN("Color texture doesn't specify UV0. Ignoring.");
}
}
}
}
for (const int node : node.children) {
CreateNodes(app_scene, gl_scene, gl_model, entity, gl_model.nodes.at(node));
}
}
engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
{ {
@ -333,13 +89,90 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
const tg::Scene& s = model.scenes.at(scene_index); const tg::Scene& s = model.scenes.at(scene_index);
/* load all textures found in the model */
std::vector<std::shared_ptr<Texture>> textures{};
textures.reserve(model.textures.size());
for (const tg::Texture& texture : model.textures) {
// find the image first
// use missing texture image by default
textures.emplace_back(scene.app()->GetResource<Texture>("builtin.white"));
if (texture.source == -1) continue;
gfx::SamplerInfo samplerInfo{};
// default to trilinear filtering even if mipmaps are not specified
samplerInfo.minify = gfx::Filter::kLinear;
samplerInfo.magnify = gfx::Filter::kLinear;
samplerInfo.mipmap = gfx::Filter::kLinear;
if (texture.sampler != -1) {
const tg::Sampler& sampler = model.samplers.at(texture.sampler);
switch (sampler.minFilter) {
case TINYGLTF_TEXTURE_FILTER_NEAREST:
case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR:
samplerInfo.minify = gfx::Filter::kNearest;
samplerInfo.mipmap = gfx::Filter::kLinear;
break;
case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST:
samplerInfo.minify = gfx::Filter::kNearest;
samplerInfo.mipmap = gfx::Filter::kNearest;
break;
case TINYGLTF_TEXTURE_FILTER_LINEAR:
case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR:
samplerInfo.minify = gfx::Filter::kLinear;
samplerInfo.mipmap = gfx::Filter::kLinear;
break;
case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST:
samplerInfo.minify = gfx::Filter::kLinear;
samplerInfo.mipmap = gfx::Filter::kNearest;
break;
default:
break;
}
switch (sampler.magFilter) {
case TINYGLTF_TEXTURE_FILTER_NEAREST:
samplerInfo.magnify = gfx::Filter::kNearest;
break;
case TINYGLTF_TEXTURE_FILTER_LINEAR:
samplerInfo.magnify = gfx::Filter::kLinear;
break;
default:
break;
}
}
// use aniso if min filter is LINEAR_MIPMAP_LINEAR
samplerInfo.anisotropic_filtering = (samplerInfo.minify == gfx::Filter::kLinear && samplerInfo.mipmap == gfx::Filter::kLinear);
const tg::Image& image = model.images.at(texture.source);
if (image.as_is == false && image.bits == 8 && image.component == 4 && image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
// create texture on GPU
textures.back() = std::make_shared<Texture>(scene.app()->renderer(), image.image.data(), image.width,
image.height, samplerInfo, true);
}
}
/* load all materials found in model */
std::vector<std::shared_ptr<Material>> materials{};
materials.reserve(model.materials.size());
for (const tg::Material& material : model.materials) {
// use default material unless a material is found
materials.emplace_back(scene.app()->GetResource<Material>("builtin.default"));
}
/* load all meshes found in model */
std::vector<std::shared_ptr<Mesh>> meshes{};
meshes.reserve(model.meshes.size());
for (const tg::Mesh& mesh : model.meshes) {
// placeholder mesh for now
}
const Entity parent = const Entity parent =
scene.CreateEntity("test_node", 0, glm::vec3{}, glm::quat{glm::one_over_root_two<float>(), glm::one_over_root_two<float>(), 0.0f, 0.0f}); scene.CreateEntity("test_node", 0, glm::vec3{}, glm::quat{glm::one_over_root_two<float>(), glm::one_over_root_two<float>(), 0.0f, 0.0f});
for (int node : s.nodes) {
CreateNodes(scene, s, model, parent, model.nodes.at(node));
}
return parent; return parent;
} }

View File

@ -165,7 +165,7 @@ Entity LoadMeshFromFile(Scene* parent, const std::string& path, bool is_static)
absPath = absPath.parent_path(); absPath = absPath.parent_path();
absPath /= texPath.C_Str(); absPath /= texPath.C_Str();
try { try {
textures[i] = LoadTextureFromFile(absPath.string(), Texture::Filtering::kTrilinear, parent->app()->renderer()); textures[i] = LoadTextureFromFile(absPath.string(), gfx::SamplerInfo{}, parent->app()->renderer());
} }
catch (const std::runtime_error&) { catch (const std::runtime_error&) {
textures[i] = parent->app()->GetResource<Texture>("builtin.white"); textures[i] = parent->app()->GetResource<Texture>("builtin.white");

View File

@ -178,9 +178,9 @@ void PlayGame(GameSettings settings)
wall_renderable->material = std::make_unique<engine::Material>(app.renderer(), app.GetResource<engine::Shader>("builtin.fancy")); wall_renderable->material = std::make_unique<engine::Material>(app.renderer(), app.GetResource<engine::Shader>("builtin.fancy"));
std::shared_ptr<engine::Texture> albedo_texture = std::shared_ptr<engine::Texture> albedo_texture =
engine::LoadTextureFromFile(app.GetResourcePath("textures/brickwall_albedo.jpg"), engine::Texture::Filtering::kAnisotropic, app.renderer()); engine::LoadTextureFromFile(app.GetResourcePath("textures/brickwall_albedo.jpg"), engine::gfx::SamplerInfo{}, app.renderer());
std::shared_ptr<engine::Texture> normal_texture = std::shared_ptr<engine::Texture> normal_texture =
engine::LoadTextureFromFile(app.GetResourcePath("textures/brickwall_normal.jpg"), engine::Texture::Filtering::kAnisotropic, app.renderer(), false); engine::LoadTextureFromFile(app.GetResourcePath("textures/brickwall_normal.jpg"), engine::gfx::SamplerInfo{}, app.renderer(), false);
wall_renderable->material->SetAlbedoTexture(albedo_texture); wall_renderable->material->SetAlbedoTexture(albedo_texture);
wall_renderable->material->SetNormalTexture(normal_texture); wall_renderable->material->SetNormalTexture(normal_texture);