Link

GLTF๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ž‘์—…์„ ๋‘ ๋‹จ๊ณ„๋กœ ๋‚˜๋ˆŒ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ๋…ธ๋“œ์™€ ๋ฉ”์‹œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๊ณ , ๋‘ ๋ฒˆ์งธ๋Š” ๋จธํ…Œ๋ฆฌ์–ผ๊ณผ ํ…์Šค์ณ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ธฐ์กด์˜ GLTF ๋กœ๋”ฉ ํ•จ์ˆ˜๋Š” ์ œ๊ฑฐ๋˜๊ณ , ๋ฉ”์‹œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์”ฌ ๋…ธ๋“œ๋ฅผ ์ง์ ‘ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋กœ ๋Œ€์ฒด๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์ด์ „ ํ•จ์ˆ˜๋Š” ๋ณต์‚ฌ/๋ถ™์—ฌ๋„ฃ๊ธฐ๋‚˜ ๋””๋ฒ„๊น… ์šฉ๋„๋กœ ๋‚จ๊ฒจ๋‘˜ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€๋Š” ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋จผ์ € ํ•ด์•ผ ํ•  ์ผ์€ LoadedGLTF ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ GLTF ํŒŒ์ผ์— ํฌํ•จ๋œ ๋ชจ๋“  ์ž์›์„ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๊ฐ€ ์†Œ๋ฉธ๋  ๋•Œ ๋ฉ”์‹œ ๋ฒ„ํผ์™€ ํ…์Šค์ณ๋„ ํ•จ๊ป˜ ํ•ด์ œ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ณ„ ๋ฆฌ์†Œ์Šค๋ฅผ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ , ๋ณด๋‹ค ๊ฐ„๋‹จํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ทธ๋ฃน ๋‹จ์œ„๋กœ ์ž์›์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์˜ ๋ชฉํ‘œ๋Š” GLTF ํŒŒ์ผ ํ•˜๋‚˜๋ฅผ ์ „์ฒด ๋ ˆ๋ฒจ์— ํ•„์š”ํ•œ ํ…์Šค์ณ, ๋ฉ”์‹œ, ๊ฐ์ฒด๋“ค์„ ํฌํ•จํ•˜๋Š” ๋‹จ์œ„๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๋˜ ๋‹ค๋ฅธ GLTF ํŒŒ์ผ์„ ์บ๋ฆญํ„ฐ๋‚˜ ์˜ค๋ธŒ์ ํŠธ ์šฉ๋„๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ์ด๋ฅผ ๊ฒŒ์ž„ ๋„์ค‘ ์œ ์ง€ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ์”ฌ์„ LoadedGLTF ํด๋ž˜์Šค ํ•˜๋‚˜๋กœ ๋ถˆ๋Ÿฌ์˜จ๋‹ค๊ณ  ํ•ด์„œ, ๊ทธ ์•ˆ์— ์žˆ๋Š” ๊ฐœ๋ณ„ ๋…ธ๋“œ๋“ค์˜ Draw() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์˜๋ฏธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค.

vk_loader.h์— ๋‹ค์Œ ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

struct LoadedGLTF : public IRenderable {

    // storage for all the data on a given glTF file
    std::unordered_map<std::string, std::shared_ptr<MeshAsset>> meshes;
    std::unordered_map<std::string, std::shared_ptr<Node>> nodes;
    std::unordered_map<std::string, AllocatedImage> images;
    std::unordered_map<std::string, std::shared_ptr<GLTFMaterial>> materials;

    // nodes that dont have a parent, for iterating through the file in tree order
    std::vector<std::shared_ptr<Node>> topNodes;

    std::vector<VkSampler> samplers;

    DescriptorAllocatorGrowable descriptorPool;

    AllocatedBuffer materialDataBuffer;

    VulkanEngine* creator;

    ~LoadedGLTF() { clearAll(); };

    virtual void Draw(const glm::mat4& topMatrix, DrawContext& ctx);

private:

    void clearAll();
};

GLTF์˜ ์˜ค๋ธŒ์ ํŠธ๋“ค์€ ์ด๋ฆ„์„ ๊ฐ–๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ๋ชจ๋“  ์˜ค๋ธŒ์ ํŠธ๋ฅผ unordered map์— ์ €์žฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ํ•ญ๋ชฉ์€ shared_ptr๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด๋ฆ„์ด ์—†๋Š” ๋…ธ๋“œ๊ฐ€ ์กด์žฌํ•˜๋”๋ผ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „์ฒด GLTF ํŒŒ์ผ์€ shared_ptr๊ฐ„์˜ ์—ฐ๊ฒฐ๋กœ ๊ตฌ์„ฑ๋œ ๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ๋ฅผ ํ˜•์„ฑํ•˜๋ฉฐ, ์ฐธ์กฐ๊ฐ€ ์œ ์ง€๋˜๋Š” ๋™์•ˆ ์ž์ฒด์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ์‚ด์•„์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์šฐ์„  ๋ฉ”์‹œ ๋ฒ„ํผ๋ฅผ ๋‹ด๋Š” mesh์˜ ๋งต์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ฉ”์‹œ๋งŒ์„ ๋ถˆ๋Ÿฌ์˜ค๋˜ ์ด์ „ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋˜ ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ, ํŒŒ์ผ ๋‚ด ๋ณ€ํ™˜ ํŠธ๋ฆฌ๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ sceneNode ๋งต์ด ์žˆ์œผ๋ฉฐ, ๊ฐ๊ฐ์˜ ๋…ธ๋“œ๋Š” ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์™ธ์—๋„ texture, material ๋งต์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ…์Šค์ณ์™€ ๋จธํ…Œ๋ฆฌ์–ผ์€ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋Š”๋ฐ, ๋™์ผํ•œ ํ…์Šค์ณ๊ฐ€ ์—ฌ๋Ÿฌ ๋จธํ…Œ๋ฆฌ์–ผ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. GLTF ํŒŒ์ผ ํฌ๋งท ์—ญ์‹œ ์ด ๋‘˜์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์ •์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ถ€๋ชจ ๋…ธ๋“œ๊ฐ€ ์—†๋Š” ๋ฃจํŠธ ๋…ธ๋“œ๋“ค์„ ๋ณ„๋„์˜ ๋ฒกํ„ฐ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด LoadedGLTF ํด๋ž˜์Šค์˜ Draw ํ•จ์ˆ˜์—์„œ ์ด ๋ฃจํŠธ ๋…ธ๋“œ๋“ค๋กœ๋ถ€ํ„ฐ ์žฌ๊ท€์ ์œผ๋กœ Draw()๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ์—๋””ํ„ฐ์—์„œ ์”ฌ ๋…ธ๋“œ๋ฅผ ๊ณ„์ธต์ ์œผ๋กœ ํ‘œ์‹œํ•  ๋•Œ๋„ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

GLTF ํฌ๋งท์— ๋งž์ถฐ VkSamplers ๋ฐฐ์—ด๋„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฐ์—ด์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์†Œ์ˆ˜์˜ ํ•ญ๋ชฉ๋งŒ ๊ฐ€์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ƒ˜ํ”Œ๋Ÿฌ๋ฅผ ์ „์—ญ์œผ๋กœ ํ•ด์‹ฑํ•˜์—ฌ ์—”์ง„์—์„œ ๊ณต์œ ํ•˜๋„๋ก ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, GLTF ํ•˜๋‚˜๊ฐ€ Vulkan ์ž์›์„ ์ „๋ถ€ ๊ด€๋ฆฌํ•œ๋‹ค๋Š” ๊ฐœ๋…์„ ์œ ์ง€ํ•˜๋ฉด ์—”์ง„์ธก ๋กœ์ง์ด ๋” ๋‹จ์ˆœํ•ด์ง‘๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  GLTF ํŒŒ์ผ ์ „์šฉ์˜ ๋””์Šคํฌ๋ฆฝํ„ฐ ํ’€๋„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐœ๋ณ„ ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹์„ ์ง์ ‘ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๋˜๋ฉฐ, ํŒŒ์ผ์— ์†ํ•œ ๋จธํ…Œ๋ฆฌ์–ผ์˜ ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹๋“ค์„ ํ•œ๊บผ๋ฒˆ์— ํ•ด์ œํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ„๋‹จํ•ด์ง‘๋‹ˆ๋‹ค.

materialDataBuffer๋Š” GLTFMetallicRoughness ๋จธํ…Œ๋ฆฌ์–ผ ๋ฐ์ดํ„ฐ์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์ „์ฒด ๋จธํ…Œ๋ฆฌ์–ผ ์ •๋ณด๋ฅผ ๋‹ด์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋ฒ„ํผ๋Š” ๋ชจ๋“  ๋จธํ…Œ๋ฆฌ์–ผ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์˜ ํฐ ๋ฒ„ํผ์— ๋‹ด์•„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ์™ธ์—๋Š” ์†Œ๋ฉธ์ž ํ•จ์ˆ˜, Draw ํ•จ์ˆ˜, ๊ทธ๋ฆฌ๊ณ  VulkanEngine ์ธ์Šคํ„ด์Šค๋ฅผ ๋‚ด๋ถ€์— ์ €์žฅํ•˜์—ฌ clearAll() ํ˜ธ์ถœ ์‹œ ์ž์›์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด์ œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํฌ์ธํ„ฐ ์ €์žฅ์„ ํ”ผํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

vk_loader.cpp์— ์ƒˆ๋กœ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ์‹œ๋‹ค. ์ฒ˜์Œ๋ถ€ํ„ฐ ์ƒˆ๋กœ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๋ฉฐ, ๋จธํ…Œ๋ฆฌ์–ผ ๊ด€๋ จ ์„ค์ •์€ ์ผ๋‹จ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค.

std::optional<std::shared_ptr<LoadedGLTF>> loadGltf(VulkanEngine* engine,std::string_view filePath)
{
    fmt::print("Loading GLTF: {}", filePath);

    std::shared_ptr<LoadedGLTF> scene = std::make_shared<LoadedGLTF>();
    scene->creator = engine;
    LoadedGLTF& file = *scene.get();

    fastgltf::Parser parser {};

    constexpr auto gltfOptions = fastgltf::Options::DontRequireValidAssetMember | fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | fastgltf::Options::LoadExternalBuffers;
    // fastgltf::Options::LoadExternalImages;

    fastgltf::GltfDataBuffer data;
    data.loadFromFile(filePath);

    fastgltf::Asset gltf;

    std::filesystem::path path = filePath;

    auto type = fastgltf::determineGltfFileType(&data);
    if (type == fastgltf::GltfType::glTF) {
        auto load = parser.loadGLTF(&data, path.parent_path(), gltfOptions);
        if (load) {
            gltf = std::move(load.get());
        } else {
            std::cerr << "Failed to load glTF: " << fastgltf::to_underlying(load.error()) << std::endl;
            return {};
        }
    } else if (type == fastgltf::GltfType::GLB) {
        auto load = parser.loadBinaryGLTF(&data, path.parent_path(), gltfOptions);
        if (load) {
            gltf = std::move(load.get());
        } else {
            std::cerr << "Failed to load glTF: " << fastgltf::to_underlying(load.error()) << std::endl;
            return {};
        }
    } else {
        std::cerr << "Failed to determine glTF container" << std::endl;
        return {};
    }
}

ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ์—๋Š” ๋‹จ์ˆœํžˆ ๋ฉ”์‹œ๋งŒ์„ ๋ถˆ๋Ÿฌ์˜ค๋˜ ์ด์ „ ์ฝ”๋“œ๋ณด๋‹ค ๋” ๋ฒ”์šฉ์ ์ธ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์งˆ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, GLTF์™€ GLB๊ฐ„ ํ˜•์‹์„ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ˜•์‹ ํ™•์ธ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์™ธ์˜ ๋Œ€๋ถ€๋ถ„์˜ ๋กœ์ง์€ ๊ธฐ์กด์˜ ๋ฉ”์‹œ ์ „์šฉ ์ฝ”๋“œ์™€ ๊ฑฐ์˜ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

    // we can stimate the descriptors we will need accurately
    std::vector<DescriptorAllocatorGrowable::PoolSizeRatio> sizes = { { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3 },
        { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3 },
        { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1 } };

    file.descriptorPool.init(engine->_device, gltf.materials.size(), sizes);

๋‹ค์Œ์œผ๋กœ๋Š” ํ•„์š”ํ•œ ๋””์Šคํฌ๋ฆฝํ„ฐ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋Œ€๋žต์ ์œผ๋กœ ์ถ”์‚ฐํ•˜์—ฌ ๋””์Šคํฌ๋ฆฝํ„ฐ ํ’€์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ํ’€์˜ ์šฉ๋Ÿ‰์„ ์ดˆ๊ณผํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ํ’€์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š”์— ๋”ฐ๋ผ VkDescriptorPool์„ ์ถ”๊ฐ€๋กœ ์ƒ์„ฑํ•˜์—ฌ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ์ƒ˜ํ”Œ๋Ÿฌ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค. GLTF ์ƒ˜ํ”Œ๋Ÿฌ๋Š” Vulkan๊ณผ๋Š” ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” OpenGL ๊ธฐ๋ฐ˜์˜ ์ˆซ์ž ๋ฐ ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์ด๋ฅผ Vulkan ํ˜•์‹์— ๋งž๊ฒŒ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

VkFilter extract_filter(fastgltf::Filter filter)
{
    switch (filter) {
    // nearest samplers
    case fastgltf::Filter::Nearest:
    case fastgltf::Filter::NearestMipMapNearest:
    case fastgltf::Filter::NearestMipMapLinear:
        return VK_FILTER_NEAREST;

    // linear samplers
    case fastgltf::Filter::Linear:
    case fastgltf::Filter::LinearMipMapNearest:
    case fastgltf::Filter::LinearMipMapLinear:
    default:
        return VK_FILTER_LINEAR;
    }
}

VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter)
{
    switch (filter) {
    case fastgltf::Filter::NearestMipMapNearest:
    case fastgltf::Filter::LinearMipMapNearest:
        return VK_SAMPLER_MIPMAP_MODE_NEAREST;

    case fastgltf::Filter::NearestMipMapLinear:
    case fastgltf::Filter::LinearMipMapLinear:
    default:
        return VK_SAMPLER_MIPMAP_MODE_LINEAR;
    }
}

์ด ๋‘ ํ•จ์ˆ˜๋Š” vk_loader.cpp ํŒŒ์ผ ๋‚ด์˜ ์ „์—ญ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์™ธ๋ถ€์—์„œ๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. Vulkan์—์„œ๋Š” ์ƒ˜ํ”Œ๋Ÿฌ ํ•„ํ„ฐ์™€ ๋ฐ‰๋งต ๋ชจ๋“œ๊ฐ€ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, GLTF์˜ ํ•„ํ„ฐ ์˜ต์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ๊ฐ์„ ๋”ฐ๋กœ ์ถ”์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. extract_filter()๋Š” VK_FILTER_NEAREST ํ˜น์€ VK_FILTER_LINEAR์™€ ๊ฐ™์€ ํ•„ํ„ฐ๋ง ๋ฐฉ์‹์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. extract_mipmap_mode()์—์„œ๋Š” VK_SAMPLER_MIPMAP_MODE_NEAREST ํ˜น์€ VK_SAMPLER_MIPMAP_MODE_LINEAR์™€ ๊ฐ™์€ ๋ฐ‰๋งต ๋ฐฉ์‹์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. NEAREST๋Š” ๋‹จ์ผ ๋ฐ‰๋งต ๋ ˆ๋ฒจ์„ ์„ ํƒํ•˜์—ฌ ๋ธ”๋ Œ๋”ฉ์ด ์—†์ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด๋ฉฐ, Linear๋Š” ์—ฌ๋Ÿฌ ๋ฐ‰๋งต ๋ ˆ๋ฒจ์„ ๋ธ”๋ Œ๋”ฉํ•˜์—ฌ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ GLTF ํŒŒ์ผ๋กœ๋ถ€ํ„ฐ ์ƒ˜ํ”Œ๋Ÿฌ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


    // load samplers
    for (fastgltf::Sampler& sampler : gltf.samplers) {

        VkSamplerCreateInfo sampl = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr};
        sampl.maxLod = VK_LOD_CLAMP_NONE;
        sampl.minLod = 0;

        sampl.magFilter = extract_filter(sampler.magFilter.value_or(fastgltf::Filter::Nearest));
        sampl.minFilter = extract_filter(sampler.minFilter.value_or(fastgltf::Filter::Nearest));

        sampl.mipmapMode= extract_mipmap_mode(sampler.minFilter.value_or(fastgltf::Filter::Nearest));

        VkSampler newSampler;
        vkCreateSampler(engine->_device, &sampl, nullptr, &newSampler);

        file.samplers.push_back(newSampler);
    }

VkSamplerCreateInfo๊ตฌ์กฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , maxLOD์™€ minLOD๊ฐ’์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„ค์ •ํ•œ ๋’ค, ์•ž์„œ ์ •์˜ํ•œ extract ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ๊ด€๋ จ ์„ค์ •์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ง€๋‚œ ์ฑ•ํ„ฐ์—์„œ์˜ ๊ธฐ๋ณธ ์ƒ˜ํ”Œ๋Ÿฌ์™€๋Š” ๋‹ฌ๋ฆฌ, ์—ฌ๊ธฐ์„œ๋Š” LOD ๋ฒ”์œ„๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜์—ฌ ๋ฐ‰๋งต์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑ๋œ VkSamplers๋“ค์€ LoadedGLTF ๊ตฌ์กฐ์ฒด์— ์ง์ ‘ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ๋ฉ”์‹œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์ „์—, ๊ตฌ์กฐ์ฒด๋ฅผ ๋‹ด๊ธฐ ์œ„ํ•œ ๋ฐฐ์—ด๋“ค์„ ๋จผ์ € ์ƒ์„ฑํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. GLTF ํŒŒ์ผ์—์„œ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์ž‘์—…์ด ์ธ๋ฑ์Šค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ด๋ฃจ์–ด์ง€๋ฏ€๋กœ, ์ธ๋ฑ์Šค๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ถ”์ ํ•˜๊ณ  ์ €์žฅํ•  ์ˆ˜๋‹จ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์–ด๋–ค ๋ฉ”์‹œ๋…ธ๋“œ๊ฐ€ ํŠน์ • ๋ฉ”์‹œ๋ฅผ ์ฐธ์กฐํ•  ๋•Œ์—๋Š” ๋ฉ”์‹œ ์ด๋ฆ„์ด ์•„๋‹Œ ์ธ๋ฑ์Šค๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

    // temporal arrays for all the objects to use while creating the GLTF data
    std::vector<std::shared_ptr<MeshAsset>> meshes;
    std::vector<std::shared_ptr<Node>> nodes;
    std::vector<AllocatedImage> images;
    std::vector<std::shared_ptr<GLTFMaterial>> materials;

์ด์ œ ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ์— ๋”ฐ๋ผ ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. MeshNode๋Š” ๋ฉ”์‹œ์—, mesh๋Š” ๋จธํ…Œ๋ฆฌ์–ผ์—, ๋จธํ…Œ๋ฆฌ์–ผ์€ ํ…์Šค์ณ์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ…์Šค์ณ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ๋กœ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ…์Šค์ณ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ฉ์‹œ๋‹ค. ํ…์Šค์ณ์˜ ๊ฒฝ์šฐ ์‹ค์ œ ์ด๋ฏธ์ง€๋Š” ์ถ”ํ›„ ๋ถˆ๋Ÿฌ์˜ฌ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์—”์ง„์— ์กด์žฌํ•˜๋Š” ๊ธฐ๋ณธ ํ…์Šค์ฒ˜๋ฅผ ๋ณต์‚ฌํ•ด ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ ์‹คํŒจํ–ˆ์„ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด ์ฒดํฌ๋ณด๋“œ ํ…์Šค์ณ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

// load all textures
for (fastgltf::Image& image : gltf.images) {
   
    images.push_back(engine->_errorCheckerboardImage);
}

๋จธํ…Œ๋ฆฌ์–ผ์€ ๋จธํ…Œ๋ฆฌ์–ผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‹ด๋Š” ๋ฒ„ํผ์˜ ํฌ๊ธฐ๋ฅผ ์‚ฌ์ „์— ๊ณ„์‚ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” ํ•˜๋‚˜์˜ ๋จธํ…Œ๋ฆฌ์–ผ ํƒ€์ž…๋งŒ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ํŠน๋ณ„ํ•œ ๋ฌธ์ œ๋Š” ์—†์œผ๋ฉฐ, ์œ ๋‹ˆํผ ๋ฒ„ํผ ๊ตฌ์กฐ์ฒด์˜ ํฌ๊ธฐ์— ๋จธํ…Œ๋ฆฌ์–ผ ๊ฐœ์ˆ˜๋ฅผ ๊ณฑํ•ด ํ•„์š”ํ•œ ์ „์ฒด ๋ฒ„ํผ ํฌ๊ธฐ๋ฅผ ๊ตฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    // create buffer to hold the material data
    file.materialDataBuffer = engine->create_buffer(sizeof(GLTFMetallic_Roughness::MaterialConstants) * gltf.materials.size(),
        VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
    int data_index = 0;
    GLTFMetallic_Roughness::MaterialConstants* sceneMaterialConstants = (GLTFMetallic_Roughness::MaterialConstants*)file.materialDataBuffer.info.pMappedData;

๋งคํ•‘๋œ ํฌ์ธํ„ฐ๋ฅผ sceneMaterialConstants์— ์ €์žฅํ•˜์—ฌ ๋จธํ…Œ๋ฆฌ์–ผ ๋ฐ์ดํ„ฐ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋จธํ…Œ๋ฆฌ์–ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ˜๋ณต๋ฌธ์„ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

    for (fastgltf::Material& mat : gltf.materials) {
        std::shared_ptr<GLTFMaterial> newMat = std::make_shared<GLTFMaterial>();
        materials.push_back(newMat);
        file.materials[mat.name.c_str()] = newMat;

        GLTFMetallic_Roughness::MaterialConstants constants;
        constants.colorFactors.x = mat.pbrData.baseColorFactor[0];
        constants.colorFactors.y = mat.pbrData.baseColorFactor[1];
        constants.colorFactors.z = mat.pbrData.baseColorFactor[2];
        constants.colorFactors.w = mat.pbrData.baseColorFactor[3];

        constants.metal_rough_factors.x = mat.pbrData.metallicFactor;
        constants.metal_rough_factors.y = mat.pbrData.roughnessFactor;
        // write material parameters to buffer
        sceneMaterialConstants[data_index] = constants;

        MaterialPass passType = MaterialPass::MainColor;
        if (mat.alphaMode == fastgltf::AlphaMode::Blend) {
            passType = MaterialPass::Transparent;
        }

        GLTFMetallic_Roughness::MaterialResources materialResources;
        // default the material textures
        materialResources.colorImage = engine->_whiteImage;
        materialResources.colorSampler = engine->_defaultSamplerLinear;
        materialResources.metalRoughImage = engine->_whiteImage;
        materialResources.metalRoughSampler = engine->_defaultSamplerLinear;

        // set the uniform buffer for the material data
        materialResources.dataBuffer = file.materialDataBuffer.buffer;
        materialResources.dataBufferOffset = data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants);
        // grab textures from gltf file
        if (mat.pbrData.baseColorTexture.has_value()) {
            size_t img = gltf.textures[mat.pbrData.baseColorTexture.value().textureIndex].imageIndex.value();
            size_t sampler = gltf.textures[mat.pbrData.baseColorTexture.value().textureIndex].samplerIndex.value();

            materialResources.colorImage = images[img];
            materialResources.colorSampler = file.samplers[sampler];
        }
        // build material
        newMat->data = engine->metalRoughMaterial.write_material(engine->_device, passType, materialResources, file.descriptorPool);

        data_index++;
    }

๋จผ์ € MaterialConstants๋ฅผ ์ฑ„์šฐ๊ณ , GLTF์˜ ๋จธํ…Œ๋ฆฌ์–ผ ์ •๋ณด์—์„œ ๊ธฐ๋ณธ ์ƒ‰์ƒ, metallic, roughness ์š”์†Œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.

์ดํ›„ MaterialResources ๊ตฌ์กฐ์ฒด๋ฅผ ์ฑ„์›๋‹ˆ๋‹ค. ํ…์Šค์ณ์™€ ์ƒ˜ํ”Œ๋Ÿฌ๋Š” ๊ธฐ๋ณธ ํฐ์ƒ‰ ํ…์Šค์ณ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  materialDataBuffer๋ฅผ ์˜ฌ๋ฐ”๋ฅธ ๋ฐ์ดํ„ฐ ์˜คํ”„์…‹์œผ๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. GLTF ๋จธํ…Œ๋ฆฌ์–ผ์—์„œ ํ…์Šค์ณ๋Š” ์„ ํƒ ์‚ฌํ•ญ์ด๋ฏ€๋กœ, ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ณดํ†ต ํฐ์ƒ‰์ด ๊ธฐ๋ณธ๊ฐ’์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ƒ‰์ƒ ํ…์Šค์ณ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ํ…์Šค์ณ์™€ ์ƒ˜ํ”Œ๋Ÿฌ๋ฅผ ์ธ๋ฑ์Šค๋ฅผ ํ†ตํ•ด ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋จธํ…Œ๋ฆฌ์–ผ์ด ํˆฌ๋ช…ํ•œ์ง€ ํ™•์ธํ•œ ํ›„, ์ด ๊ฒฝ์šฐ MaterialPass๋ฅผ Transparent๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด, ์ธ์ž๋“ค์„ metalRoughMaterial ํด๋ž˜์Šค์— ์ „๋‹ฌํ•˜์—ฌ ๋จธํ…Œ๋ฆฌ์–ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๋ฉ”์‹œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ „์˜ ๋ฉ”์‹œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ฝ”๋“œ์™€ ๊ฑฐ์˜ ์œ ์‚ฌํ•˜์ง€๋งŒ, ์ฐจ์ด์ ์€ ๋ฉ”์‹œ๋ฅผ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์ €์žฅํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

// use the same vectors for all meshes so that the memory doesnt reallocate as
// often
std::vector<uint32_t> indices;
std::vector<Vertex> vertices;

for (fastgltf::Mesh& mesh : gltf.meshes) {
    std::shared_ptr<MeshAsset> newmesh = std::make_shared<MeshAsset>();
    meshes.push_back(newmesh);
    file.meshes[mesh.name.c_str()] = newmesh;
    newmesh->name = mesh.name;

    // clear the mesh arrays each mesh, we dont want to merge them by error
    indices.clear();
    vertices.clear();

    for (auto&& p : mesh.primitives) {
        GeoSurface newSurface;
        newSurface.startIndex = (uint32_t)indices.size();
        newSurface.count = (uint32_t)gltf.accessors[p.indicesAccessor.value()].count;

        size_t initial_vtx = vertices.size();

        // load indexes
        {
            fastgltf::Accessor& indexaccessor = gltf.accessors[p.indicesAccessor.value()];
            indices.reserve(indices.size() + indexaccessor.count);

            fastgltf::iterateAccessor<std::uint32_t>(gltf, indexaccessor,
                [&](std::uint32_t idx) {
                    indices.push_back(idx + initial_vtx);
                });
        }

        // load vertex positions
        {
            fastgltf::Accessor& posAccessor = gltf.accessors[p.findAttribute("POSITION")->second];
            vertices.resize(vertices.size() + posAccessor.count);

            fastgltf::iterateAccessorWithIndex<glm::vec3>(gltf, posAccessor,
                [&](glm::vec3 v, size_t index) {
                    Vertex newvtx;
                    newvtx.position = v;
                    newvtx.normal = { 1, 0, 0 };
                    newvtx.color = glm::vec4 { 1.f };
                    newvtx.uv_x = 0;
                    newvtx.uv_y = 0;
                    vertices[initial_vtx + index] = newvtx;
                });
        }

        // load vertex normals
        auto normals = p.findAttribute("NORMAL");
        if (normals != p.attributes.end()) {

            fastgltf::iterateAccessorWithIndex<glm::vec3>(gltf, gltf.accessors[(*normals).second],
                [&](glm::vec3 v, size_t index) {
                    vertices[initial_vtx + index].normal = v;
                });
        }

        // load UVs
        auto uv = p.findAttribute("TEXCOORD_0");
        if (uv != p.attributes.end()) {

            fastgltf::iterateAccessorWithIndex<glm::vec2>(gltf, gltf.accessors[(*uv).second],
                [&](glm::vec2 v, size_t index) {
                    vertices[initial_vtx + index].uv_x = v.x;
                    vertices[initial_vtx + index].uv_y = v.y;
                });
        }

        // load vertex colors
        auto colors = p.findAttribute("COLOR_0");
        if (colors != p.attributes.end()) {

            fastgltf::iterateAccessorWithIndex<glm::vec4>(gltf, gltf.accessors[(*colors).second],
                [&](glm::vec4 v, size_t index) {
                    vertices[initial_vtx + index].color = v;
                });
        }

        if (p.materialIndex.has_value()) {
            newSurface.material = materials[p.materialIndex.value()];
        } else {
            newSurface.material = materials[0];
        }

        newmesh->surfaces.push_back(newSurface);
    }

    newmesh->meshBuffers = engine->uploadMesh(indices, vertices);
}

์ฐจ์ด์ ์€ ๋งˆ์ง€๋ง‰์— ๋จธํ…Œ๋ฆฌ์–ผ ์ธ๋ฑ์Šค๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ๋จธํ…Œ๋ฆฌ์–ผ์ด ์—†๋‹ค๋ฉด ํŒŒ์ผ์˜ ์ฒซ ๋ฒˆ์งธ ๋จธํ…Œ๋ฆฌ์–ผ์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋น„๋ก ๋จธํ…Œ๋ฆฌ์–ผ์ด ์„ ํƒ ์‚ฌํ•ญ์ด์ง€๋งŒ, ๋จธํ…Œ๋ฆฌ์–ผ ์ธ๋ฑ์Šค๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋Š” ๋“œ๋ฌผ๊ธฐ ๋•Œ๋ฌธ์— ํŠน๋ณ„ํžˆ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ๋…ธ๋“œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค.

    // load all nodes and their meshes
    for (fastgltf::Node& node : gltf.nodes) {
        std::shared_ptr<Node> newNode;

        // find if the node has a mesh, and if it does hook it to the mesh pointer and allocate it with the meshnode class
        if (node.meshIndex.has_value()) {
            newNode = std::make_shared<MeshNode>();
            static_cast<MeshNode*>(newNode.get())->mesh = meshes[*node.meshIndex];
        } else {
            newNode = std::make_shared<Node>();
        }

        nodes.push_back(newNode);
        file.nodes[node.name.c_str()];

        std::visit(fastgltf::visitor { [&](fastgltf::Node::TransformMatrix matrix) {
                                          memcpy(&newNode->localTransform, matrix.data(), sizeof(matrix));
                                      },
                       [&](fastgltf::Node::TRS transform) {
                           glm::vec3 tl(transform.translation[0], transform.translation[1],
                               transform.translation[2]);
                           glm::quat rot(transform.rotation[3], transform.rotation[0], transform.rotation[1],
                               transform.rotation[2]);
                           glm::vec3 sc(transform.scale[0], transform.scale[1], transform.scale[2]);

                           glm::mat4 tm = glm::translate(glm::mat4(1.f), tl);
                           glm::mat4 rm = glm::toMat4(rot);
                           glm::mat4 sm = glm::scale(glm::mat4(1.f), sc);

                           newNode->localTransform = tm * rm * sm;
                       } },
            node.transform);
    }

๋…ธ๋“œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ž‘์—…์€ ๋‘ ๋‹จ๊ณ„๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ๋…ธ๋“œ๊ฐ€ ๋ฉ”์‹œ๋ฅผ ํฌํ•จํ•˜๋Š”์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๊ธฐ๋ณธ Node ํด๋ž˜์Šค ๋˜๋Š” MeshNode ํด๋ž˜์Šค๋กœ ๋…ธ๋“œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ํ›„, GLTF์˜ ๋ณ€ํ™˜ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์™€ ์ด๋ฅผ GLTF์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ตœ์ข… ๋ณ€ํ™˜ ํ–‰๋ ฌ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋กœ์ปฌ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

๋…ธ๋“œ๊ฐ€ ๋ชจ๋‘ ๋ถˆ๋Ÿฌ์™€์กŒ๋‹ค๋ฉด ์”ฌ ๊ทธ๋ž˜ํ”„๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ถ€๋ชจ ์ž์‹ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    // run loop again to setup transform hierarchy
    for (int i = 0; i < gltf.nodes.size(); i++) {
        fastgltf::Node& node = gltf.nodes[i];
        std::shared_ptr<Node>& sceneNode = nodes[i];

        for (auto& c : node.children) {
            sceneNode->children.push_back(nodes[c]);
            nodes[c]->parent = sceneNode;
        }
    }

    // find the top nodes, with no parents
    for (auto& node : nodes) {
        if (node->parent.lock() == nullptr) {
            file.topNodes.push_back(node);
            node->refreshTransform(glm::mat4 { 1.f });
        }
    }
    return scene;

๋จผ์ € ๋ชจ๋“  ๋…ธ๋“œ๋ฅผ ์ˆœํšŒํ•˜์—ฌ ์ž์‹ ๋…ธ๋“œ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋ถ€๋ชจ/์ž์‹ ํฌ์ธํ„ฐ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‹ค์‹œ ํ•œ๋ฒˆ ๋…ธ๋“œ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๋ถ€๋ชจ๋ฅผ ๊ฐ–์ง€ ์•Š๋Š” ๋…ธ๋“œ๋ฅผ ์ฐพ์•„ topNodes ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•˜๊ณ  ๋ณ€ํ™˜ ์ •๋ณด๋ฅผ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค. ์ง€๋‚œ ์ฑ•ํ„ฐ์—์„œ ์„ค๋ช…ํ–ˆ๋“ฏ์ด refreshTransform ํ•จ์ˆ˜๋Š” ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์›”๋“œ ํ–‰๋ ฌ์„ ์žฌ๊ท€์ ์œผ๋กœ ๊ณ„์‚ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์–ด๋–ค ๋…ธ๋“œ๋“  ๋ถ€๋ชจ๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ๋‹ค๋ฉด ๊ทธ ์ž์‹ ๋…ธ๋“œ๋“ค๋„ ํ•จ๊ป˜ ๊ฐฑ์‹ ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ์ „์ฒด ์”ฌ์„ ๋ถˆ๋Ÿฌ์™”์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด Draw() ํ•จ์ˆ˜๋ฅผ ์ฑ„์›Œ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

void LoadedGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx)
{
    // create renderables from the scenenodes
    for (auto& n : topNodes) {
        n->Draw(topMatrix, ctx);
    }
}

Drawํ•จ์ˆ˜๋Š” ๋‹จ์ˆœํžˆ topNodes๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๊ฐ ๋…ธ๋“œ์˜ Draw๋ฅผ ํ˜ธ์ถœํ•  ๋ฟ์ž…๋‹ˆ๋‹ค. ์ด ํ˜ธ์ถœ์€ ์ž์‹ ๋…ธ๋“œ๋“ค์—๊ฒŒ๋„ ์ „ํŒŒ๋ฉ๋‹ˆ๋‹ค.

clearAll() ํ•จ์ˆ˜๋ฅผ ์ง€๊ธˆ์€ ๋น„์›Œ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„์ง์€ ํ…์Šค์ณ ์ฒ˜๋ฆฌ๊ฐ€ ์ œ๋Œ€๋กœ ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ๋‚จ๊ฒจ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด์ œ ์ด๋ฅผ VulkanEngine ํด๋ž˜์Šค์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

๋ถˆ๋Ÿฌ์˜จ GLTF๋ฅผ ์ด๋ฆ„์„ ํ‚ค๋กœ ํ•˜์—ฌ unordered map์— ์ €์žฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 std::unordered_map<std::string, std::shared_ptr<LoadedGLTF>> loadedScenes;

์ด์ œ ๋ฌด์–ธ๊ฐ€๋ฅผ ๋ถˆ๋Ÿฌ์™€๋ด…์‹œ๋‹ค. ํ”„๋กœ์ ํŠธ์— ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ 1500๊ฐœ์˜ ๋ฉ”์‹œ๋ฅผ ๊ทธ๋ฆฌ๋Š” ์ปค๋‹ค๋ž€ ์”ฌ์„ ๊ทธ๋ ค๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋””๋ฒ„๊ทธ ๋ชจ๋“œ์—์„œ๋Š” ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ 1์ดˆ์—์„œ 2์ดˆ์ •๋„ ์†Œ์š”๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ž ์‹œ ๊ธฐ๋‹ค๋ ค ์ฃผ์„ธ์š”.

๋‹ค์Œ ์ฝ”๋“œ๋ฅผ init() ํ•จ์ˆ˜์˜ ๋์— ์ถ”๊ฐ€ํ•˜์„ธ์š”.

    std::string structurePath = { "..\\..\\assets\\structure.glb" };
    auto structureFile = loadGltf(this,structurePath);

    assert(structureFile.has_value());

    loadedScenes["structure"] = *structureFile;

ํ•ด๋‹น ์—์…‹์„ ๋ถˆ๋Ÿฌ์™€ ์ดํ›„์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์‹œ๋งต์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ดํ›„์—๋Š” update_scene() ํ•จ์ˆ˜์—์„œ Draw()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์ด ์—์…‹์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

	loadedScenes["structure"]->Draw(glm::mat4{ 1.f }, mainDrawContext);

์ด ์”ฌ์€ ํฌ๊ธฐ๊ฐ€ ํฌ๊ธฐ ๋•Œ๋ฌธ์—, ์„œ๋กœ ๋‹ค๋ฅธ ์œ„์น˜์— ์—ฌ๋Ÿฌ ๋ฒˆ ๊ทธ๋ ค ๋ฒค์น˜๋งˆํฌ ์šฉ๋„๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์”ฌ์„ ์ •๋ฆฌํ•  ๋•Œ๋Š”, ์ ์ ˆํ•œ ์‹œ์ ์— ์ดˆ๊ธฐํ™” ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. clear()ํ•จ์ˆ˜์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. vkDeviceWaitIdle ํ˜ธ์ถœ ์งํ›„ Vulkan ์ •๋ฆฌ ์ž‘์—…์˜ ์ดˆ๋ฐ˜์— ํ•ด์‹œ ๋งต์„ ์ดˆ๊ธฐํ™”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ ๋ชจ๋“  ์”ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 // make sure the gpu has stopped doing its things
 vkDeviceWaitIdle(_device);

 loadedScenes.clear();

์ด์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•ด ๋ณด๊ณ  ๋งต์„ ๋‘˜๋Ÿฌ๋ณด์„ธ์š”. ์ปค๋‹ค๋ž€ ์”ฌ์ด์ง€๋งŒ ์ด์ œ ์นด๋ฉ”๋ผ๋กœ ๋‘˜๋Ÿฌ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์นด๋ฉ”๋ผ์˜ ๊ธฐ๋ณธ ์œ„์น˜๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝํ•˜๋ฉด ์ ์ ˆํ•œ ์ดˆ๊ธฐ ์œ„์น˜๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

 mainCamera.position = glm::vec3(30.f, -00.f, -085.f);

init()ํ•จ์ˆ˜์—์„œ ์ด ๊ฐ’์„ ์„ค์ •ํ•ด์ฃผ์„ธ์š”.

๋‹ค์Œ ๊ธ€์—์„œ ํ…์Šค์ณ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์œผ๋กœ ์ด ๊ณผ์ •์„ ๋งˆ๋ฌด๋ฆฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Next: GLTF Textures