1
0
mirror of https://github.com/taigrr/skyline synced 2025-01-18 04:33:13 -08:00

Add STL Exporter

This commit is contained in:
Martin Woodward 2020-12-15 14:54:37 +00:00
parent ad399b8899
commit 3fbb8dc96c
6 changed files with 333 additions and 25 deletions

2
.gitignore vendored
View File

@ -104,3 +104,5 @@ dist
# TernJS port file
.tern-port
.vercel

View File

@ -9,6 +9,10 @@
</style>
</head>
<body>
<div id="info">
<button id="exportASCII">export ASCII</button> <button id="exportBinary">export binary</button>
</div>
<script type="module" src="js/contributions.js"></script>
</body>
</html>

210
js/STLExporter.js Normal file
View File

@ -0,0 +1,210 @@
import {
BufferGeometry,
Vector3
} from './three.module.js';
/**
* Usage:
* var exporter = new STLExporter();
*
* // second argument is a list of options
* var data = exporter.parse( mesh, { binary: true } );
*
*/
var STLExporter = function () {};
STLExporter.prototype = {
constructor: STLExporter,
parse: function ( scene, options ) {
if ( options === undefined ) options = {};
var binary = options.binary !== undefined ? options.binary : false;
//
var objects = [];
var triangles = 0;
scene.traverse( function ( object ) {
if ( object.isMesh ) {
var geometry = object.geometry;
if ( geometry.isGeometry ) {
geometry = new BufferGeometry().fromGeometry( geometry );
}
var index = geometry.index;
var positionAttribute = geometry.getAttribute( 'position' );
triangles += ( index !== null ) ? ( index.count / 3 ) : ( positionAttribute.count / 3 );
objects.push( {
object3d: object,
geometry: geometry
} );
}
} );
var output;
var offset = 80; // skip header
if ( binary === true ) {
var bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4;
var arrayBuffer = new ArrayBuffer( bufferLength );
output = new DataView( arrayBuffer );
output.setUint32( offset, triangles, true ); offset += 4;
} else {
output = '';
output += 'solid exported\n';
}
var vA = new Vector3();
var vB = new Vector3();
var vC = new Vector3();
var cb = new Vector3();
var ab = new Vector3();
var normal = new Vector3();
for ( var i = 0, il = objects.length; i < il; i ++ ) {
var object = objects[ i ].object3d;
var geometry = objects[ i ].geometry;
var index = geometry.index;
var positionAttribute = geometry.getAttribute( 'position' );
if ( index !== null ) {
// indexed geometry
for ( var j = 0; j < index.count; j += 3 ) {
var a = index.getX( j + 0 );
var b = index.getX( j + 1 );
var c = index.getX( j + 2 );
writeFace( a, b, c, positionAttribute, object );
}
} else {
// non-indexed geometry
for ( var j = 0; j < positionAttribute.count; j += 3 ) {
var a = j + 0;
var b = j + 1;
var c = j + 2;
writeFace( a, b, c, positionAttribute, object );
}
}
}
if ( binary === false ) {
output += 'endsolid exported\n';
}
return output;
function writeFace( a, b, c, positionAttribute, object ) {
vA.fromBufferAttribute( positionAttribute, a );
vB.fromBufferAttribute( positionAttribute, b );
vC.fromBufferAttribute( positionAttribute, c );
if ( object.isSkinnedMesh === true ) {
object.boneTransform( a, vA );
object.boneTransform( b, vB );
object.boneTransform( c, vC );
}
vA.applyMatrix4( object.matrixWorld );
vB.applyMatrix4( object.matrixWorld );
vC.applyMatrix4( object.matrixWorld );
writeNormal( vA, vB, vC );
writeVertex( vA );
writeVertex( vB );
writeVertex( vC );
if ( binary === true ) {
output.setUint16( offset, 0, true ); offset += 2;
} else {
output += '\t\tendloop\n';
output += '\tendfacet\n';
}
}
function writeNormal( vA, vB, vC ) {
cb.subVectors( vC, vB );
ab.subVectors( vA, vB );
cb.cross( ab ).normalize();
normal.copy( cb ).normalize();
if ( binary === true ) {
output.setFloat32( offset, normal.x, true ); offset += 4;
output.setFloat32( offset, normal.y, true ); offset += 4;
output.setFloat32( offset, normal.z, true ); offset += 4;
} else {
output += '\tfacet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
output += '\t\touter loop\n';
}
}
function writeVertex( vertex ) {
if ( binary === true ) {
output.setFloat32( offset, vertex.x, true ); offset += 4;
output.setFloat32( offset, vertex.y, true ); offset += 4;
output.setFloat32( offset, vertex.z, true ); offset += 4;
} else {
output += '\t\t\tvertex ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
}
}
}
};
export { STLExporter };

View File

@ -2,6 +2,7 @@ import * as THREE from "./three.module.js";
import { GLTFLoader } from './GLTFLoader.js';
import { OrbitControls } from './OrbitControls.js';
import { GUI } from './dat.gui.module.js';
import { STLExporter } from './STLExporter.js';
const BASE_LENGTH = 0.834
const BASE_WIDTH = 0.167
@ -10,8 +11,8 @@ const CUBE_SIZE = 0.0143
const MAX_HEIGHT = 0.14
const FACE_ANGLE = 104.79
let username = "jasonlong"
let year = "2018"
let username = "nat"
let year = "2020"
let json = {}
let font = undefined
let fontSize = 0.025
@ -20,6 +21,8 @@ let fontHeight = 0.00658 // Extrusion thickness
let camera, scene, renderer
let bronzeMaterial
var exporter = new STLExporter();
var urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('username')) {
@ -224,12 +227,33 @@ const init = () => {
controls.autoRotate = false
controls.screenSpacePanning = true
controls.addEventListener('change', render);
var buttonExportASCII = document.getElementById( 'exportASCII' );
buttonExportASCII.addEventListener( 'click', exportASCII );
var buttonExportBinary = document.getElementById( 'exportBinary' );
buttonExportBinary.addEventListener( 'click', exportBinary );
}
const render = () => {
renderer.render(scene, camera)
}
function exportASCII() {
var result = exporter.parse( bronzeMaterial );
saveString( result, username + '-' + year + '.stl' );
}
function exportBinary() {
var result = exporter.parse( scene, { binary: true } );
saveArrayBuffer( result, username + '-' + year + '.stl' );
}
//
// Event listeners
//
@ -243,3 +267,28 @@ const onWindowResize = () => {
}
window.addEventListener('resize', onWindowResize, false)
var link = document.createElement( 'a' );
link.style.display = 'none';
document.body.appendChild( link );
function save( blob, filename ) {
link.href = URL.createObjectURL( blob );
link.download = filename;
link.click();
}
function saveString( text, filename ) {
save( new Blob( [ text ], { type: 'text/plain' } ), filename );
}
function saveArrayBuffer( buffer, filename ) {
save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
}

85
package-lock.json generated
View File

@ -1355,6 +1355,14 @@
"graceful-fs": "^4.1.2",
"jsonfile": "^3.0.0",
"universalify": "^0.1.0"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
}
}
},
"fsevents": {
@ -2131,13 +2139,20 @@
"graceful-fs": "~1.2.0",
"inherits": "1",
"minimatch": "~0.2.11"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
}
}
},
"graceful-fs": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
"integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=",
"dev": true
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
},
"inherits": {
"version": "1.0.2",
@ -2172,12 +2187,6 @@
"sparkles": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"dev": true
},
"gulp": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz",
@ -2827,6 +2836,15 @@
"dev": true,
"requires": {
"graceful-fs": "^4.1.6"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true,
"optional": true
}
}
},
"kind-of": {
@ -2877,6 +2895,14 @@
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0",
"strip-bom": "^2.0.0"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
}
}
},
"localtunnel": {
@ -3223,12 +3249,6 @@
"to-regex": "^3.0.1"
}
},
"natives": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz",
"integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==",
"dev": true
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@ -3576,6 +3596,14 @@
"graceful-fs": "^4.1.2",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
}
}
},
"pify": {
@ -3729,6 +3757,14 @@
"graceful-fs": "^4.1.11",
"micromatch": "^3.1.10",
"readable-stream": "^2.0.2"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
}
}
},
"rechoir": {
@ -4841,6 +4877,12 @@
"vinyl": "^1.1.0"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"vinyl": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz",
@ -4877,13 +4919,10 @@
"dev": true
},
"graceful-fs": {
"version": "3.0.12",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.12.tgz",
"integrity": "sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==",
"dev": true,
"requires": {
"natives": "^1.1.3"
}
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"isarray": {
"version": "0.0.1",

View File

@ -2,6 +2,7 @@
"name": "3d-contributions",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"preinstall": "npx npm-force-resolutions",
"start": "node node_modules/gulp/bin/gulp.js auto"
},
"devDependencies": {
@ -11,5 +12,8 @@
},
"dependencies": {
"three": "^0.118.1"
},
"resolutions": {
"graceful-fs": "^4.2.4"
}
}