Amazon Sumerain(またはunity)でobjファイルの動的読み込みをしてみる

Amazon Sumerain(またはunity)でobjファイルの動的読み込みをしてみる:

Amazon Sumerianでobjファイルを動的に読み込んで3Dオブジェクトとして表示するのをやってみたので内容をメモしておく。



sumerianObjImporterCapture.PNG



参考にしたUnityスクリプト

以下のスクリプトを利用してみた(ひとまずUnity Editor上だけで動作確認)。
ObjImporter - Unify Community Wiki

テクスチャの読み込み処理はやっていないが、そこらへんはRuntime OBJ Importer Assetあたりを参照したら良さそう。


obsolete対応

以下のメソッドがobsoleteでエラーになる。

mesh.Optimize(); 
次のサイトを参考にして以下のように対応した。
Mesh.Optimize() obsolete in Unity 5.5.05b - Unity Answers

#if UNITY_EDITOR 
        UnityEditor.MeshUtility.Optimize(mesh); 
#endif 


大きいobjファイルの読み込み

mesh.verticesで設定した値の65536番目より大きい値を参照すると値がおかしくなり3Dオブジェクトの形が崩れる。

このため、次のようなサイトを参照して以下のように対応した。
c# - Issue With setting Mesh.triangles in Unity Script - Game Development Stack Exchange

mesh.verticesの配列数が2 ** 32を超えるようだとメッシュを分割した方がよさそう。

mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; 


Sumerianでの作成手順

  • エンティティの作成でHTMLエンティティを追加する。
  • HTMLコンポーネントを以下のようにする。
<input id="infile" type="file" value="obj"> 
  • スクリプトコンポーネントを以下のようにしてtextureパラメータに適用したいテクスチャを指定する。
'use strict'; 
 
// objファイル読み込みクラス 
class ObjImporter { 
    constructor() { 
    } 
 
    loadFromFile(file, finishFunc) { 
        let vertices = []; 
        let normals = []; 
        let uv = []; 
        let triangles = []; 
        let faceData = []; 
 
        this.readLine(file, (line) => { 
            let bstring = line.trim().split(/\s+/); 
            switch (bstring[0]) { 
                case 'v': 
                    vertices.push([parseFloat(bstring[1]),  
                                   parseFloat(bstring[2]),  
                                   parseFloat(bstring[3])]); 
                    break; 
                case 'vt': 
                    uv.push([parseFloat(bstring[1]), 
                             parseFloat(bstring[2])]); 
                    break; 
                case 'vn': 
                    normals.push([parseFloat(bstring[1]), 
                                  parseFloat(bstring[2]), 
                                  parseFloat(bstring[3])]); 
                    break; 
                case 'f': 
                    let intArray = []; 
                    for (let j = 1; j < bstring.length && bstring[j].length > 0; j++) { 
                        let bbstring = bstring[j].split("/", 3); 
                        faceData.push([parseInt(bbstring[0]),  
                                       parseInt(bbstring[1]),  
                                       parseInt(bbstring[2])]); 
                        intArray.push(faceData.length - 1); 
                    } 
                    for (let j = 1; j + 2 < bstring.length; j++) { 
                        triangles.push(intArray[0]); 
                        triangles.push(intArray[j]); 
                        triangles.push(intArray[j + 1]); 
                    } 
                    break; 
            } 
        }, 
        () => { 
            let newVerts = []; 
            let newUVs = []; 
            let newNormals = []; 
            for (let i = 0; i < faceData.length; i++) { 
                newVerts[i] = vertices[faceData[i][0] - 1]; 
                if (faceData[i][1] != NaN) { 
                    newUVs[i] = uv[faceData[i][1] - 1]; 
                } 
                if (faceData[i][2] != NaN) { 
                    newNormals[i] = normals[faceData[i][2] - 1]; 
                } 
            } 
 
            let attributes = [sumerian.MeshData.POSITION, sumerian.MeshData.NORMAL, sumerian.MeshData.TEXCOORD0]; 
            let attributeMap = sumerian.MeshData.defaultMap(attributes); 
            let vertexCount = faceData.length; 
            let indexCount = triangles.length; 
            let meshData = new sumerian.MeshData(attributeMap, vertexCount, indexCount); 
            meshData.getAttributeBuffer(sumerian.MeshData.POSITION).set( 
                Array.prototype.concat.apply([], newVerts) 
            ); 
            meshData.getAttributeBuffer(sumerian.MeshData.NORMAL).set( 
                Array.prototype.concat.apply([], newNormals) 
            ); 
            meshData.getAttributeBuffer(sumerian.MeshData.TEXCOORD0).set( 
                Array.prototype.concat.apply([], newUVs) 
            ); 
            meshData.getIndexBuffer().set(triangles); 
            finishFunc(meshData); 
        }); 
    } 
 
    // テキストファイルから少しずつ読み込んで1行ずつ処理する 
    readLine(file, callback, finishfunc, chunk_size = 1024) { 
        let offset = 0; 
        let text = ""; 
        let fr = new FileReader(); 
 
        fr.onload = (event) => { 
            if(typeof fr.result === "string") { 
                let lines = (text + fr.result).split(/\r\n|\r|\n/); 
                for (let i=0; i<lines.length;i++) 
                  callback(lines[i] + "\n"); 
                finishfunc(); 
                return true; 
            } 
 
            let view = new Uint8Array(fr.result); 
            text += String.fromCharCode(...view); 
            let lines = text.split(/\r\n|\r|\n/); 
            for (let i = 0; i < lines.length - 1; i++) 
                callback(lines[i] + "\n"); 
            text = lines[lines.length - 1]; 
 
            seek(); 
        }; 
 
        fr.onerror = () => { 
            callback(null); 
        }; 
 
        let seek = () => { 
            if (offset + chunk_size >= file.size) { 
                let slice = file.slice(offset); 
                fr.readAsText(slice); 
            } else { 
                let slice = file.slice(offset, offset + chunk_size); 
                fr.readAsArrayBuffer(slice); 
            } 
 
            offset += chunk_size; 
        } 
        seek(); 
    } 
} 
 
// プレイモード開始時に呼び出される 
function setup(args, ctx) { 
    let input = document.getElementById("infile"); 
    input.addEventListener('change', (e) => { 
        let file = e.target.files[0]; 
        let objimporter = new ObjImporter(); 
        objimporter.loadFromFile(file, (meshData) => { 
            var material = new sumerian.Material(sumerian.ShaderLib.textured); 
            material.setTexture('DIFFUSE_MAP', args.texture); 
            var quadEntity = ctx.world.createEntity(meshData, material).addToWorld(); 
            ctx.myentity = quadEntity; 
        }); 
    }); 
} 
 
// プレイモード終了時に呼び出される 
function cleanup(args, ctx) { 
    if (ctx.myentity) { 
        ctx.world.removeEntity(ctx.myentity); 
    } 
} 
 
var parameters = [ 
    { 
        name : "texture", 
        key : "texture", 
        type : "texture", 
    } 
]; 
 

コメント

このブログの人気の投稿

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-04-30 23:37:32 RSSフィード2021-04-30 23:00 分まとめ(42件)

投稿時間:2023-02-05 02:09:04 RSSフィード2023-02-05 02:00 分まとめ(9件)