2019-07-18 00:02:07 +08:00
|
|
|
(function() {
|
|
|
|
var _obj_export;
|
|
|
|
function getMtlFace(obj, index) {
|
2020-10-03 02:06:13 +08:00
|
|
|
//if (index % 2 == 1) index--;
|
|
|
|
var key = Canvas.face_order[index];
|
2019-07-18 00:02:07 +08:00
|
|
|
var tex = obj.faces[key].getTexture()
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
if (tex === null) {
|
|
|
|
return false
|
|
|
|
} else if (!tex || typeof tex === 'string') {
|
|
|
|
return 'usemtl none\n'
|
|
|
|
} else {
|
2020-07-16 15:32:59 +08:00
|
|
|
return 'usemtl m_' + tex.id + '\n';
|
2019-07-18 00:02:07 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var codec = new Codec('obj', {
|
|
|
|
name: 'OBJ Wavefront Model',
|
|
|
|
extension: 'obj',
|
|
|
|
compile(options) {
|
|
|
|
if (!options) options = 0;
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-19 23:31:22 +08:00
|
|
|
var old_scene_position = new THREE.Vector3().copy(scene.position);
|
2019-07-18 00:02:07 +08:00
|
|
|
scene.position.set(0,0,0)
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
var output = '# Made in Blockbench '+appVersion+'\n';
|
|
|
|
var materials = {};
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
var indexVertex = 0;
|
|
|
|
var indexVertexUvs = 0;
|
|
|
|
var indexNormals = 0;
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
output += 'mtllib ' + (options.mtl_name||'materials.mtl') +'\n';
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2020-01-24 01:53:36 +08:00
|
|
|
var scale = 1/16;
|
|
|
|
|
2018-10-18 01:50:25 +08:00
|
|
|
var parseMesh = function ( mesh ) {
|
|
|
|
|
|
|
|
var nbVertex = 0;
|
|
|
|
var nbVertexUvs = 0;
|
|
|
|
var nbNormals = 0;
|
|
|
|
|
|
|
|
var geometry = mesh.geometry;
|
2020-10-04 17:13:38 +08:00
|
|
|
var element = OutlinerElement.uuids[mesh.name];
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
if (!element) return;
|
2018-10-18 01:50:25 +08:00
|
|
|
if (element.export === false) return;
|
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
output += 'o ' + element.name + '\n';
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
var vertices = geometry.vertices;
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
for ( var i = 0, l = vertices.length; i < l; i ++ ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
var vertex = vertices[ i ].clone();
|
|
|
|
vertex.applyMatrix4( mesh.matrixWorld );
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2020-01-24 01:53:36 +08:00
|
|
|
output += 'v ' + (vertex.x*scale) + ' ' + (vertex.y*scale) + ' ' + (vertex.z*scale) + '\n';
|
2019-08-01 06:01:47 +08:00
|
|
|
nbVertex ++;
|
|
|
|
}
|
|
|
|
// uvs
|
|
|
|
var faces = geometry.faces;
|
|
|
|
var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
|
|
|
|
var hasVertexUvs = faces.length === faceVertexUvs.length;
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
if ( hasVertexUvs ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
for ( var i = 0, l = faceVertexUvs.length; i < l; i ++ ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
var vertexUvs = faceVertexUvs[ i ];
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
for ( var j = 0, jl = vertexUvs.length; j < jl; j ++ ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
var uv = vertexUvs[ j ];
|
|
|
|
output += 'vt ' + uv.x + ' ' + uv.y + '\n';
|
|
|
|
nbVertexUvs ++;
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
|
|
|
}
|
2019-08-01 06:01:47 +08:00
|
|
|
}
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
// normals
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
var normalMatrixWorld = new THREE.Matrix3();
|
|
|
|
normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
var face = faces[ i ];
|
|
|
|
var vertexNormals = face.vertexNormals;
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
if ( vertexNormals.length === 3 ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
for ( var j = 0, jl = vertexNormals.length; j < jl; j ++ ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
var normal = vertexNormals[ j ].clone();
|
|
|
|
normal.applyMatrix3( normalMatrixWorld );
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
nbNormals ++;
|
|
|
|
}
|
|
|
|
} else {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
var normal = face.normal.clone();
|
|
|
|
normal.applyMatrix3( normalMatrixWorld );
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
for ( var j = 0; j < 3; j ++ ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
|
|
|
|
nbNormals ++;
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
|
|
|
}
|
2019-08-01 06:01:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// material
|
|
|
|
for (var face in element.faces) {
|
|
|
|
var tex = element.faces[face].getTexture()
|
|
|
|
if (tex && tex.uuid && !materials[tex.id]) {
|
|
|
|
materials[tex.id] = tex
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
2019-08-01 06:01:47 +08:00
|
|
|
}
|
2018-10-18 01:50:25 +08:00
|
|
|
|
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
for ( var i = 0, j = 1, l = faces.length; i < l; i ++, j += 3 ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2020-10-03 02:06:13 +08:00
|
|
|
var face = faces[ i ];
|
|
|
|
var f_mat = getMtlFace(element, face.materialIndex)
|
2019-08-01 06:01:47 +08:00
|
|
|
if (f_mat) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
if (i % 2 === 0) {
|
|
|
|
output += f_mat
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
2019-08-01 06:01:47 +08:00
|
|
|
output += 'f ';
|
|
|
|
output += ( indexVertex + face.a + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ) + ' ';
|
|
|
|
output += ( indexVertex + face.b + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 1 ) : '' ) + '/' + ( indexNormals + j + 1 ) + ' ';
|
|
|
|
output += ( indexVertex + face.c + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 2 ) : '' ) + '/' + ( indexNormals + j + 2 ) + '\n';
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// update index
|
|
|
|
indexVertex += nbVertex;
|
|
|
|
indexVertexUvs += nbVertexUvs;
|
|
|
|
indexNormals += nbNormals;
|
|
|
|
};
|
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
scene.traverse( function ( child ) {
|
2018-10-18 01:50:25 +08:00
|
|
|
|
|
|
|
if ( child instanceof THREE.Mesh ) parseMesh( child );
|
|
|
|
} );
|
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
// mtl output
|
|
|
|
|
|
|
|
var mtlOutput = '# Made in Blockbench '+appVersion+'\n';;
|
|
|
|
|
|
|
|
for (var key in materials) {
|
|
|
|
if (materials.hasOwnProperty(key) && materials[key]) {
|
|
|
|
var tex = materials[key];
|
2020-07-16 15:32:59 +08:00
|
|
|
mtlOutput += 'newmtl m_' +key+ '\n'
|
2019-07-18 00:02:07 +08:00
|
|
|
mtlOutput += `map_Kd ${tex.name} \n`;
|
|
|
|
}
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
mtlOutput += 'newmtl none'
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-19 23:31:22 +08:00
|
|
|
scene.position.copy(old_scene_position)
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
_obj_export = {
|
|
|
|
obj: output,
|
|
|
|
mtl: mtlOutput,
|
|
|
|
images: materials
|
|
|
|
}
|
2020-03-05 03:56:17 +08:00
|
|
|
this.dispatchEvent('compile', {model: output, mtl: mtlOutput, images: materials});
|
2019-07-18 00:02:07 +08:00
|
|
|
return options.all_files ? _obj_export : output;
|
|
|
|
},
|
|
|
|
write(content, path) {
|
2019-07-19 23:31:22 +08:00
|
|
|
var scope = this;
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
var mtl_path = path.replace(/\.obj$/, '.mtl')
|
|
|
|
content = this.compile({mtl_name: pathToName(mtl_path, true)})
|
|
|
|
Blockbench.writeFile(path, {content}, path => scope.afterSave(path));
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
Blockbench.writeFile(mtl_path, {content: _obj_export.mtl});
|
|
|
|
|
|
|
|
for (var key in _obj_export.images) {
|
|
|
|
var texture = _obj_export.images[key]
|
|
|
|
if (texture && !texture.error) {
|
|
|
|
|
|
|
|
var name = texture.name;
|
|
|
|
if (name.substr(-4) !== '.png') {
|
|
|
|
name += '.png';
|
|
|
|
}
|
|
|
|
var image_path = path.split(osfs)
|
|
|
|
image_path.splice(-1, 1, texture.name)
|
|
|
|
Blockbench.writeFile(image_path.join(osfs), {
|
|
|
|
content: texture.source,
|
|
|
|
savetype: 'image'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
export() {
|
|
|
|
var scope = this;
|
|
|
|
if (isApp) {
|
|
|
|
Blockbench.export({
|
2020-04-26 02:25:07 +08:00
|
|
|
resource_id: 'obj',
|
2019-07-18 00:02:07 +08:00
|
|
|
type: this.name,
|
|
|
|
extensions: [this.extension],
|
|
|
|
name: this.fileName(),
|
|
|
|
custom_writer: (a, b) => scope.write(a, b),
|
|
|
|
})
|
|
|
|
|
|
|
|
} else {
|
|
|
|
var archive = new JSZip();
|
|
|
|
var content = this.compile()
|
|
|
|
|
|
|
|
archive.file((Project.name||'model')+'.obj', content)
|
|
|
|
archive.file('materials.mtl', _obj_export.mtl)
|
|
|
|
|
|
|
|
for (var key in _obj_export.images) {
|
|
|
|
var texture = _obj_export.images[key]
|
|
|
|
if (texture && !texture.error && texture.mode === 'bitmap') {
|
|
|
|
archive.file(pathToName(texture.name) + '.png', texture.source.replace('data:image/png;base64,', ''), {base64: true});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
archive.generateAsync({type: 'blob'}).then(content => {
|
|
|
|
Blockbench.export({
|
|
|
|
type: 'Zip Archive',
|
|
|
|
extensions: ['zip'],
|
|
|
|
name: 'assets',
|
|
|
|
content: content,
|
|
|
|
savetype: 'zip'
|
|
|
|
}, path => scope.afterDownload(path));
|
|
|
|
})
|
|
|
|
}
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
BARS.defineActions(function() {
|
|
|
|
codec.export_action = new Action({
|
|
|
|
id: 'export_obj',
|
|
|
|
icon: 'icon-objects',
|
|
|
|
category: 'file',
|
|
|
|
click: function () {
|
|
|
|
codec.export()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
})()
|