src/loader/S3MeshLoaderJSON.js
import S3System from "../basic/S3System.js";
import S3Mesh from "../basic/S3Mesh.js";
import S3Material from "../basic/S3Material.js";
import S3Vector from "../math/S3Vector.js";
/*
次のようなデータを入出力できます。
const sample = {
Indexes:{
body:[
[ 0, 1, 2],
[ 3, 1, 0],
[ 3, 0, 2],
[ 3, 2, 1]
]
},
Vertices:[
[ 0, 0, -5],
[ 0, 20, -5],
[ 10, 0, -5],
[ 0, 0, -20]
]
};
*/
/**
* JSON形式による3DCGメッシュデータの入出力ユーティリティ
*
* - 頂点配列(Vertices)や面インデックス配列(Indexes)を持つJSONデータを
* S3Meshインスタンスへ変換(インポート)、またはS3MeshからJSON形式で出力(エクスポート)します。
* - 三角形・四角形など複数頂点数の面、マテリアル名ごとの管理に対応。
* - テキスト文字列またはオブジェクト形式の両方をサポート。
* - メッシュローダ本体(S3MeshLoader)経由でも利用されます。
*
* @namespace S3MeshLoaderJSON
* @property {string} name - メッシュデータの入出力形式名("JSON")
* @property {function(S3System, S3Mesh, string|Object):boolean} input - JSONデータからS3Meshへ変換(インポート)
* @property {function(S3Mesh):string} output - S3MeshからJSON形式へ変換(エクスポート)
*
* @example
* // インポート
* S3MeshLoaderJSON.input(sys, mesh, '{"Vertices":[[0,0,0]],"Indexes":{"mat1":[[0,0,0]]}}');
* // エクスポート
* const json = S3MeshLoaderJSON.output(mesh);
*/
const S3MeshLoaderJSON = {
/**
* メッシュデータの入出力形式名
* @type {string}
*/
name: "JSON",
/**
* JSONデータをS3Meshへ変換(インポート)します。
*
* - 頂点配列(Vertices)、面インデックス配列(Indexes)を含むJSONデータを解析し
* S3Meshオブジェクトへ詰め替えます。
* - 文字列型なら自動的にJSON.parseします。
*
* @param {S3System} sys S3Systemインスタンス
* @param {S3Mesh} mesh メッシュインスタンス(初期化済み/空状態で渡される)
* @param {string|Object} json JSON文字列またはそのオブジェクト
* @returns {boolean} パースが成功した場合はtrue
*
* @example
* // 文字列からの直接インポート
* S3MeshLoaderJSON.input(sys, mesh, '{"Vertices":[[0,0,0]],"Indexes":{"mat1":[[0,0,0]]}}');
*/
input: function (sys, mesh, json) {
let meshdata;
if (typeof json === "string") {
meshdata = JSON.parse(json);
} else {
meshdata = json;
}
let material = 0;
// 材質名とインデックスを取得
for (const materialname in meshdata.Indexes) {
mesh.addMaterial(sys.createMaterial(materialname));
const materialindexlist = meshdata.Indexes[materialname];
for (let i = 0; i < materialindexlist.length; i++) {
const list = materialindexlist[i];
for (let j = 0; j < list.length - 2; j++) {
// 3角形と4角形に対応
const ti =
j % 2 === 0
? sys.createTriangleIndex(j, j + 1, j + 2, list, material)
: sys.createTriangleIndex(j - 1, j + 1, j + 2, list, material);
mesh.addTriangleIndex(ti);
}
}
material++;
}
// 頂点座標を取得
for (let i = 0; i < meshdata.Vertices.length; i++) {
const vector = new S3Vector(meshdata.Vertices[i][0], meshdata.Vertices[i][1], meshdata.Vertices[i][2]);
const vertex = sys.createVertex(vector);
mesh.addVertex(vertex);
}
return true;
},
/**
* S3MeshインスタンスをシンプルなJSONオブジェクトに変換(エクスポート)します。
*
* - 頂点配列・面インデックス配列・マテリアル名などを全てJSONオブジェクト形式で返します。
* - マテリアルごとのインデックスリストも整理されます。
*
* @param {S3Mesh} mesh 出力対象のメッシュ
* @returns {string} JSON形式のテキストデータ
*
* @example
* const json = S3MeshLoaderJSON.output(mesh);
* // → ファイル保存やエディタ表示などに活用できます
*/
output: function (mesh) {
const vertex = mesh.getVertexArray();
const triangleindex = mesh.getTriangleIndexArray();
const material = mesh.getMaterialArray();
/**
* デフォルトのマテリアル情報(必要時に参照される)
*
* @typedef {Object} S3MeshLoaderMaterial
* @property {string} name 名前
* @property {S3Vector} color 拡散反射色
* @property {number} diffuse 拡散係数
* @property {S3Vector} emission 自己照明色
* @property {S3Vector} specular 鏡面反射色
* @property {number} power 鏡面反射強度
* @property {S3Vector} ambient 環境光色
* @property {number} reflect 環境マッピング反射率
* @property {null} textureColor 拡散テクスチャ
* @property {null} textureNormal 法線マップ
*/
/**
* @typedef {Object} S3MeshLoaderMaterialListEntry
* @property {S3MeshLoaderMaterial|S3Material} material マテリアル情報(S3Material型またはDefaultMaterialオブジェクト)
* @property {Array<Array<number>>} list そのマテリアルに属する三角形インデックス配列
*/
/**
* @type {S3MeshLoaderMaterial}
*/
const DefaultMaterial = {
name: "s3default",
color: new S3Vector(1.0, 1.0, 1.0, 1.0),
diffuse: 0.8,
emission: new S3Vector(0.0, 0.0, 0.0),
specular: new S3Vector(0.0, 0.0, 0.0),
power: 5.0,
ambient: new S3Vector(0.6, 0.6, 0.6),
reflect: 0.0,
textureColor: null,
textureNormal: null
};
/**
* @type {Array<S3MeshLoaderMaterialListEntry>}
*/
const material_vertexlist = [];
const material_length = material.length !== 0 ? material.length : 1;
const default_material = DefaultMaterial;
// 材質リストを取得
for (let i = 0; i < material_length; i++) {
material_vertexlist[i] = {
material: material[i] ? material[i] : default_material,
list: []
};
}
// 材質名に合わせて、インデックスリストを取得
for (let i = 0; i < triangleindex.length; i++) {
const ti = triangleindex[i];
material_vertexlist[ti.materialIndex].list.push(ti.index);
}
const output = [];
output.push("{");
output.push("\tIndexes:{");
for (let i = 0; i < material_vertexlist.length; i++) {
const mv = material_vertexlist[i];
output.push("\t\t" + mv.material.name + ":[");
for (let j = 0; j < mv.list.length; j++) {
const vi = mv.list[j];
output.push(
"\t\t\t[" + vi[0] + " " + vi[1] + " " + vi[2] + "]" + (j === mv.list.length - 1 ? "" : ",")
);
}
output.push("\t\t]" + (i === material_vertexlist.length - 1 ? "" : ","));
}
output.push("\t},");
output.push("\tVertices:[");
for (let i = 0; i < vertex.length; i++) {
const vp = vertex[i].position;
output.push("\t\t[" + vp.x + " " + vp.y + " " + vp.z + "]" + (i === vertex.length - 1 ? "" : ","));
}
output.push("\t]");
output.push("}");
return output.join("\n");
}
};
export default S3MeshLoaderJSON;