Runnable version in bl.ocks.org
Last active
April 7, 2022 22:01
-
-
Save scmanjarrez/f51ff1c7a3d50a6051d392a98c2533b6 to your computer and use it in GitHub Desktop.
Export threejs json to stl (used with thigiverse js models: example: https://cdn.thingiverse.com/threejs_json/16/3f/4e/a9/e6/large.js)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>STL viewer</title> | |
<script src="https://metalop.com/threejs/scripts/threejs/three.min.js"></script> | |
<script src="https://metalop.com/threejs/scripts/threejs/controls/OrbitControls.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/lil-gui@0.16"></script> | |
<script src="https://cdn.jsdelivr.net/npm/three-stlexporter@1.0.0"></script> | |
</head> | |
<body> | |
<script> | |
"use strict"; | |
//Globals | |
var clock, renderer, effect, scene, model, material, camera, controls, gui, name; | |
var exporter = new THREE.STLExporter(); | |
var browseModel = function() { | |
var browseButton; | |
return function() { | |
browseButton = document.createElement("input"); | |
Object.assign(browseButton, { | |
type: "file", | |
multiple: false, | |
style: "display: none", | |
accept: ".js, .json", | |
onchange: function(e) { | |
let file = browseButton.files[0]; | |
let parts = file.name.split("."); | |
name = parts[0]; | |
let reader = new FileReader(); | |
reader.onload = function(event) { | |
let result = event.target.result; | |
let loader = new THREE.JSONLoader(); | |
if (model) scene.remove(model); | |
let data = JSON.parse(result); | |
let geometry = loader.parse(data); | |
addFromGeometry(geometry); | |
} | |
reader.readAsText(file); | |
} | |
}); | |
browseButton.click(); | |
}; | |
}(); | |
function addFromGeometry(geometry) { | |
if (material) material.dispose(); | |
if (geometry.hasColors) { | |
console.log("color attribute found. Trying to enable vertex colors."); | |
material = new THREE.MeshPhongMaterial({ | |
transparent: true, | |
opacity: geometry.alpha || 1, | |
vertexColors: THREE.VertexColors | |
}); | |
} else { | |
console.log("No color attribute found. Using default color."); | |
material = new THREE.MeshLambertMaterial({ | |
transparent: true, | |
opacity: geometry.alpha || 1, | |
color: 0x26f7f4 | |
}); | |
} | |
if (model) scene.remove(model); | |
model = new THREE.Mesh(geometry.geometry, material); | |
model.rotation.x = true * (-0.5 * Math.PI); | |
scene.add(model); | |
adjustCameraToModel(model); | |
} | |
function adjustCameraToModel(mesh) { | |
var middle = new THREE.Vector3; | |
var geometry = mesh.geometry; | |
geometry.computeBoundingBox(); | |
middle.x = (geometry.boundingBox.max.x + geometry.boundingBox.min.x) / 2; | |
middle.y = (geometry.boundingBox.max.y + geometry.boundingBox.min.y) / 2; | |
middle.z = (geometry.boundingBox.max.z + geometry.boundingBox.min.z) / 2; | |
mesh.localToWorld(middle); | |
let bb = new THREE.Box3().setFromObject(model).expandByPoint(scene.position); | |
let diag = bb.max.clone().sub(bb.min).length(); | |
let factor = Math.max(1, 1 / camera.aspect) / Math.tan(0.5 * (Math.PI * camera.fov / 180)); | |
camera.position.normalize().multiplyScalar(diag * factor); | |
camera.near = Math.min(0.5 * factor, 1); | |
camera.far = 4 * diag * factor; //wrong? | |
(bb.max.x < bb.min.x) || (bb.max.y < bb.min.y) || (bb.max.z < bb.min.z) ? controls.target.set(0, 0, 0): controls.target.addVectors(bb.min, bb.max).multiplyScalar(0.5); | |
camera.lookAt(controls.target); | |
camera.updateProjectionMatrix(); | |
requestAnimationFrame(render); | |
} | |
(function main() { | |
//Renderer setup | |
clock = new THREE.Clock( /*false*/ ); //false vil skru av autostart | |
document.body.style = "overflow: hidden;"; | |
var container = document.createElement("div"); | |
container.style = "position: absolute; top: 0; left: 0;" | |
document.body.appendChild(container); | |
renderer = new THREE.WebGLRenderer({ | |
antialias: true | |
}); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
renderer.setClearColor(0xb3b3e5); | |
container.appendChild(renderer.domElement); | |
//Scene setup: | |
scene = new THREE.Scene(); | |
var sun = new THREE.HemisphereLight(0xFFFFFF, 0x000000, 0.5); | |
scene.add(sun); | |
//Camera setup | |
camera = new THREE.PerspectiveCamera(26, window.innerWidth / window.innerHeight, 1, 100000); | |
window.addEventListener('resize', function() { | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
requestAnimationFrame(render); | |
}, false); | |
camera.position.set(50, 50, -100); | |
scene.add(camera); | |
controls = new THREE.OrbitControls(camera, renderer.domElement); | |
controls.addEventListener("change", () => requestAnimationFrame(render)); | |
//Menu | |
gui = new lil.GUI(); | |
gui.add({ | |
browse: function() { | |
browseModel(); | |
} | |
}, "browse"); | |
gui.add({ | |
opacity: 1 | |
}, "opacity", 0, 1).onChange(function(value) { | |
if (material !== undefined) { | |
material.opacity = value; | |
} else { | |
if (model) { | |
model.traverse((obj) => { | |
if (obj.isMesh) { | |
obj.material.opacity = value; | |
} | |
}); | |
} | |
} | |
requestAnimationFrame(render); | |
}); | |
gui.addColor({ | |
color: "#26f7f4" | |
}, "color").onChange(function(value) { | |
if (material !== undefined) { | |
material.color = new THREE.Color(value); | |
} else { | |
if (model) { | |
model.traverse((obj) => { | |
if (obj.isMesh) { | |
obj.material.color = new THREE.Color(value); | |
} | |
}); | |
} | |
} | |
requestAnimationFrame(render); | |
}); | |
gui.add({ | |
zUp: true | |
}, "zUp").onChange(function(value) { | |
if (model) { | |
model.rotation.x = value * (-0.5 * Math.PI); | |
adjustCameraToModel(model); | |
} | |
}); | |
gui.addColor({ | |
background: "#b3b3e5" | |
}, "background").onChange(value => { | |
renderer.setClearColor(value); | |
requestAnimationFrame(render); | |
}); | |
gui.add({ | |
exportSTL: function() { | |
exportSTL(); | |
} | |
}, "exportSTL"); | |
requestAnimationFrame(render); | |
const link = document.createElement('a'); | |
link.style.display = 'none'; | |
document.body.appendChild(link); | |
function exportSTL() { | |
console.log(camera.position); | |
if (model) { | |
const result = exporter.parse(model); | |
saveString(result, name + '.stl'); | |
} | |
}; | |
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 render() { | |
renderer.render(scene, camera); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment