Hi,
I can’t calculate the Lather Geometry appear the way I wanted. It always too wide at the top . How can I calculate it and make it appear randomly around the subtracted object? Can we add the thickness to the Lathe? Also, I want to export the object to 3D printing STL file. The code that I have it doesn’t work.
I would appreciate it if you could help me to solve my problems. I spent hours for this but they didn’t work for me.
Here is the Lathe Geometry that I have and also, it doesn’t show the whole geometry , the back doesn’t show
Here is the Lathe Geometry that I wanted to have.
Here is the code
import * as THREE from 'three';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
import { MeshSurfaceSampler } from 'three/examples/jsm/math/MeshSurfaceSampler.js';
import { STLExporter } from 'three-addons';
import {
exportBinary,
Brush,
Evaluator,
ADDITION,
SUBTRACTION,
INTERSECTION,
DIFFERENCE,
} from '..';
const params = {
exportBinary: exportBinary,
operation: SUBTRACTION,
wireframe: false,
displayBrushes: false,
shadows: true,
useGroups: true,
randomize: () => {
randomizeBrushes();
updateCSG();
}
};
let renderer, camera, scene, controls, gui, outputContainer;
let cut, brushes;
let material, surfaceSampler;
let resultObject, wireframeResult, light;
let csgEvaluator = new Evaluator();
csgEvaluator.attributes = [ 'position', 'normal' ];
csgEvaluator.useGroups = false;
const materialMap = new Map();
init();
async function init() {
const bgColor = 0x111111;
outputContainer = document.getElementById( 'output' );
// renderer setup
renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( bgColor, 1 );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.outputEncoding = THREE.sRGBEncoding;
document.body.appendChild( renderer.domElement );
// scene setup
scene = new THREE.Scene();
// lights
light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 1, 2, 1 );
scene.add( light, new THREE.AmbientLight( 0xb0bec5, 0.1 ) );
// shadows
const shadowCam = light.shadow.camera;
light.castShadow = true;
light.shadow.mapSize.setScalar( 4096 );
light.shadow.bias = 1e-5;
light.shadow.normalBias = 1e-2;
shadowCam.left = shadowCam.bottom = - 2.5;
shadowCam.right = shadowCam.top = 2.5;
shadowCam.updateProjectionMatrix();
// camera setup
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 );
camera.position.set( 0, 0.65, 2.5 );
camera.far = 100;
camera.updateProjectionMatrix();
// controls
controls = new OrbitControls( camera, renderer.domElement );
const axesHelper = new THREE.AxesHelper( 5 );
scene.add( axesHelper );
//main object
const geometry = new THREE.BoxGeometry( 10, 0.2, 10 );
const pi = Math.PI
// initialize brushes
cut = new Brush( geometry, new THREE.MeshStandardMaterial() );
cut.position.y = - 0.5;
cut.updateMatrixWorld();
cut.receiveShadow = true;
scene.add( cut );
material = new THREE.MeshStandardMaterial();
brushes = [];
surfaceSampler = new MeshSurfaceSampler( cut );
surfaceSampler.build();
for ( let i = 0; i < 70; i ++ ) {
const b = new Brush( new THREE.BoxBufferGeometry( 1, 1, 20 ), material );
b.receiveShadow = true;
scene.add( b );
brushes.push( b );
const r = new Brush( new THREE.BoxBufferGeometry( 0.5, 0.5, 20 ), material );
r.receiveShadow = true;
r.rotateY( Math.PI );
scene.add( r );
brushes.push( r);
}
for (let i = 0; i < 70; i ++) {
const c = new Brush (new THREE.TorusBufferGeometry(5, 0.5, 8, 50), material );
c.reveiveShadow = true;
c.rotation.set( Math.PI / 2, 0, 0 );
c.position.y = Math.random() * 2 - 1;
scene.add (c);
brushes.push (c);
}
//LATHE Geometry
const points = [];
for ( let i = 0; i < 5; i ++ ) {
points.push( new THREE.Vector2( Math.sin( i * 0.02 ) * 1 + 0.5, ( i - 0.5 ) * 0.2 ) );
}
const e = new Brush (new THREE.LatheGeometry( points));
e.receiveShadow = true;
e.position.x= Math.random()
e.position.z= Math.random()
e.rotation.set( Math.PI, 0, 0 );
scene.add(e)
// initialize materials
cut.material.opacity = 0.15;
cut.material.transparent = true;
cut.material.depthWrite = false;
cut.material.polygonOffset = true;
cut.material.polygonOffsetFactor = 0.1;
cut.material.polygonOffsetUnits = 0.1;
cut.material.side = THREE.DoubleSide;
cut.material.premultipliedAlpha = true;
cut.material.color.set( 0xE0F7FA ).convertSRGBToLinear();
material.opacity = 0.15;
material.transparent = true;
material.depthWrite = false;
material.polygonOffset = true;
material.polygonOffsetFactor = 0.1;
material.polygonOffsetUnits = 0.1;
material.side = THREE.DoubleSide;
material.premultipliedAlpha = true;
material.roughness = 0.25;
material.color.set( 0x4DD0E1 ).convertSRGBToLinear();
// create solid material equivalents
let mat;
mat = cut.material.clone();
mat.opacity = 1;
mat.transparent = false;
mat.depthWrite = true;
materialMap.set( cut.material, mat );
mat = material.clone();
mat.opacity = 1;
mat.transparent = false;
mat.depthWrite = true;
materialMap.set( material, mat );
// add object displaying the result
resultObject = new THREE.Mesh( new THREE.BufferGeometry(), new THREE.MeshStandardMaterial( {
roughness: 0.1,
flatShading: false,
polygonOffset: true,
polygonOffsetUnits: 1,
polygonOffsetFactor: 1,
} ) );
resultObject.castShadow = true;
resultObject.receiveShadow = true;
scene.add( resultObject );
// gui
gui = new GUI();
gui.add( params, 'operation', { ADDITION, SUBTRACTION, INTERSECTION, DIFFERENCE } ).onChange( () => {
updateCSG();
} );
gui.add( params, 'displayBrushes' );
gui.add( params, 'shadows' );
gui.add( params, 'useGroups' ).onChange( updateCSG );
gui.add( params, 'randomize' );
gui.add (params, 'exportBinary');
gui.open()
window.addEventListener( 'resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}, false );
randomizeBrushes();
updateCSG();
render();
function updateCSG() {
const startTime = window.performance.now();
let finalBrush = brushes[ 0 ];
csgEvaluator.useGroups = false;
for ( let i = 1, l = brushes.length; i < l; i ++ ) {
const b = brushes[ i ];
finalBrush = csgEvaluator.evaluate( finalBrush, b, ADDITION );
finalBrush.material = material;
}
csgEvaluator.useGroups = params.useGroups;
csgEvaluator.evaluate( cut, finalBrush, params.operation, resultObject );
if ( params.useGroups ) {
resultObject.material = resultObject.material.map( m => materialMap.get( m ) );
} else {
resultObject.material = materialMap.get( cut.material );
}
const deltaTime = window.performance.now() - startTime;
outputContainer.innerText = `${ deltaTime.toFixed( 3 ) }ms`;
}
function randomizeBrushes() {
for ( let i = 0; i < brushes.length; i ++ ) {
const b = brushes[ i ];
surfaceSampler.sample( b.position );
b.position.applyMatrix4( cut.matrixWorld );
b.scale.setScalar( THREE.MathUtils.lerp( 0.07, 0.27, Math.random() ) );
b.updateMatrixWorld();
}
}
function render() {
requestAnimationFrame( render );
//wireframeResult.visible = params.wireframe;
cut.visible = params.displayBrushes;
brushes.forEach( b => b.visible = params.displayBrushes );
light.castShadow = params.shadows;
renderer.render( scene, camera );
}
function exportBinary() {
const result = exporter.parse( SUBTRACTION, { binary: true } );
saveArrayBuffer( result, 'landscape.stl' );
}
const 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 );
}