objファイルをgltf, glbファイルに変換する

2024/12/30に公開

概要

objファイルをgltf, glbファイルに変換する方法の備忘録です。

対象データ

「菊池市/デジタルアーカイブ」の「石淵家地球儀」を対象とします。

https://adeac.jp/kikuchi-city/catalog/e0001

objファイルは以下のURLからアクセスできます。

https://adeac.jp/viewitem/kikuchi-city/viewer/3d/dc-e0097/models/Kikuchi_Globe_180820.obj

対象データのダウンロード

ライブラリをダウンロードします。

dwn.js
npm i axios

以下のファイルを用意します。

const axios = require('axios');
const fs = require('fs');
const path = require('path');

// 指定されたURLからファイルをダウンロードする関数
async function downloadFile(url, outputPath) {
    const writer = fs.createWriteStream(outputPath);

    const response = await axios({
        url,
        method: 'GET',
        responseType: 'stream',
    });

    response.data.pipe(writer);

    return new Promise((resolve, reject) => {
        writer.on('finish', resolve);
        writer.on('error', reject);
    });
}

// .obj ファイルをロードし、関連ファイルをダウンロードする
async function processObjFile(objUrl, outputDir) {
    try {
        // .obj ファイルをダウンロードして内容を取得
        const objResponse = await axios.get(objUrl);
        const objContent = objResponse.data;

        // .obj ファイルを保存
        const objFileName = path.basename(objUrl);
        const objFilePath = path.join(outputDir, objFileName);
        fs.writeFileSync(objFilePath, objContent);
        console.log(`Downloaded OBJ file: ${objFilePath}`);

        // .mtl ファイルのパスを検索
        const mtlMatch = objContent.match(/^mtllib\s+(.+)$/m);
        if (mtlMatch) {
            const mtlFileName = mtlMatch[1];
            const mtlUrl = new URL(mtlFileName, objUrl).href;
            const mtlFilePath = path.join(outputDir, mtlFileName);

            // .mtl ファイルをダウンロード
            await downloadFile(mtlUrl, mtlFilePath);
            console.log(`Downloaded MTL file: ${mtlFilePath}`);

            // .mtl ファイルの内容を取得して関連ファイルを探す
            const mtlContent = fs.readFileSync(mtlFilePath, 'utf-8');
            const textureMatches = [...mtlContent.matchAll(/^map_Kd\s+(.+)$/gm)];

            for (const match of textureMatches) {
                const textureFileName = match[1];
                const textureUrl = new URL(textureFileName, objUrl).href;
                const textureFilePath = path.join(outputDir, path.basename(textureFileName));

                // テクスチャ画像をダウンロード
                await downloadFile(textureUrl, textureFilePath);
                console.log(`Downloaded texture file: ${textureFilePath}`);
            }
        } else {
            console.log('No MTL file referenced in the OBJ file.');
        }
    } catch (error) {
        console.error(`Error processing OBJ file: ${error.message}`);
    }
}

// 使用例
const objUrl = 'https://adeac.jp/viewitem/kikuchi-city/viewer/3d/dc-e0097/models/Kikuchi_Globe_180820.obj';
const outputDir = './downloads';

if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
}

processObjFile(objUrl, outputDir);

実行します。

node dwn.js

downloadsフォルダに、.obj.mtl.jpgがダウンロードされます。

gltfファイルへの変換

ライブラリをダウンロードします。

npm i obj2gltf

以下のファイルを用意します。

convert.js
const obj2gltf = require("obj2gltf");
const fs = require("fs");

obj2gltf("./downloads/Kikuchi_Globe_180820.obj").then(function (gltf) {
    const data = Buffer.from(JSON.stringify(gltf));
    fs.writeFileSync("./model.gltf", data);
});

実行します。

node convert.js

model.gltfファイルが作成されます。

glbファイルへの変換

ライブラリをダウンロードします。

npm i gltf-pipeline

gltfファイルをglbファイルに変換します。

gltf-pipeline -i model.gltf -o model.glb

まとめ

他にも適切な変換方法があるかもしれませんが、参考になりましたら幸いです。

Discussion