mirror of
https://github.com/taigrr/skyline
synced 2025-01-18 04:33:13 -08:00
Add STL Exporter
This commit is contained in:
parent
ad399b8899
commit
3fbb8dc96c
2
.gitignore
vendored
2
.gitignore
vendored
@ -104,3 +104,5 @@ dist
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
.vercel
|
||||
|
@ -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
210
js/STLExporter.js
Normal 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 };
|
@ -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
85
package-lock.json
generated
@ -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",
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user