1- import GUI from "https://cdn.jsdelivr.net/npm/lil-gui@0.21/+esm" ;
21import * as THREE from "three" ;
32import { OrbitControls } from "three/addons/controls/OrbitControls.js" ;
43import { STLLoader } from "three/addons/loaders/STLLoader.js" ;
54import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js" ;
6- import { OutputPass } from ' three/addons/postprocessing/OutputPass.js' ;
5+ import { OutputPass } from " three/addons/postprocessing/OutputPass.js" ;
76import { RenderPass } from "three/addons/postprocessing/RenderPass.js" ;
87import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js" ;
98
109const loader = new STLLoader ( ) ;
1110const container = document . getElementById ( "viewer" ) ;
12- const deg2rad = ( degrees ) => ( degrees * ( Math . PI / 180 ) ) ;
11+ const deg2rad = ( degrees ) => degrees * ( Math . PI / 180 ) ;
12+ const lights = {
13+ key : undefined ,
14+ fill : undefined ,
15+ rim : undefined ,
16+ } ;
1317
14- let renderer ,
15- composer ,
16- bloomPass ,
17- mesh ,
18- camera ,
19- spotLight ,
20- scene ,
21- controls ;
22-
23- const lights = { key : undefined , fill : undefined , rim : undefined } ;
18+ let renderer , composer , mesh , material , camera , scene , controls , bloomPass ;
2419
2520function frameObject ( object , camera , controls , fitOffset = 1.2 ) {
2621 const box = new THREE . Box3 ( ) . setFromObject ( object ) ;
@@ -55,10 +50,10 @@ function setupRenderer() {
5550 renderer . setClearColor ( 0xffffff , 0 ) ;
5651
5752 renderer . autoClear = true ;
58- renderer . toneMappingExposure = 1.0 ;
5953 renderer . physicallyCorrectLights = true ;
6054 renderer . outputColorSpace = THREE . SRGBColorSpace ;
6155 renderer . toneMapping = THREE . ACESFilmicToneMapping ;
56+ renderer . toneMappingExposure = 0.85 ;
6257
6358 container . appendChild ( renderer . domElement ) ;
6459}
@@ -82,16 +77,22 @@ function setupResizeHandler() {
8277}
8378
8479function setupPostProcessing ( { strength, radius, threshold } ) {
85- const resolution = new THREE . Vector2 ( container . clientWidth , container . clientHeight ) ;
80+ const resolution = new THREE . Vector2 (
81+ container . clientWidth ,
82+ container . clientHeight ,
83+ ) ;
8684 const renderScene = new RenderPass ( scene , camera ) ;
8785 // const outputPass = new OutputPass();
8886
8987 bloomPass = new UnrealBloomPass ( resolution , strength , radius , threshold ) ;
90- composer = new EffectComposer ( renderer , new THREE . WebGLRenderTarget (
91- container . clientWidth ,
92- container . clientHeight ,
93- { format : THREE . RGBAFormat } // alpha channel
94- ) ) ;
88+ composer = new EffectComposer (
89+ renderer ,
90+ new THREE . WebGLRenderTarget (
91+ container . clientWidth ,
92+ container . clientHeight ,
93+ { format : THREE . RGBAFormat } , // alpha channel
94+ ) ,
95+ ) ;
9596
9697 composer . addPass ( renderScene ) ;
9798 // composer.addPass(bloomPass);
@@ -108,7 +109,7 @@ function setupCameraAndControls() {
108109 frustumSize / 2 ,
109110 frustumSize / - 2 ,
110111 0.1 ,
111- 5000
112+ 5000 ,
112113 ) ;
113114
114115 // camera.position.set(x, y, z);
@@ -141,49 +142,33 @@ function setupScene() {
141142/**
142143 * Load the model into the scene
143144 *
144- * @param {string } URL to the STL model file
145+ * @param {string } stlUrl to the STL model file
145146 */
146147function loadSTL ( stlUrl ) {
147- // const material = new THREE.MeshPhongMaterial({ color: 0x5588ff });
148- const material = new THREE . MeshStandardMaterial ( {
149- color : 0x5588ff ,
150- emissive : 0x3366ff ,
151- emissiveIntensity : 0.3 ,
152- metalness : 0.3 ,
153- roughness : 0.2
154- } ) ;
155-
156148 loader . load ( stlUrl , ( geometry ) => {
157149 const center = new THREE . Vector3 ( ) ;
150+ // edges highlight / darken
151+ const edges = new THREE . EdgesGeometry ( geometry , 25 ) ;
152+ const lines = new THREE . LineSegments (
153+ edges ,
154+ new THREE . LineBasicMaterial ( { color : 0x111111 } ) ,
155+ ) ;
158156
159157 geometry . computeVertexNormals ( ) ;
160158 geometry . computeBoundingBox ( ) ;
161- geometry
162- . boundingBox
163- . getCenter ( center )
164- . negate ( ) ;
159+ geometry . boundingBox . getCenter ( center ) . negate ( ) ;
165160 geometry . translate ( center . x , center . y , center . z ) ;
166161
167162 mesh = new THREE . Mesh ( geometry , material ) ;
168163 mesh . rotateX ( deg2rad ( - 90 ) ) ;
164+ mesh . add ( lines ) ;
169165
170166 scene . add ( mesh ) ;
171167
172168 frameObject ( mesh , camera , controls , 2 ) ;
173-
174- // if (showEdges) {
175- // // optional: edges overlay for instant readability
176- // const edges = new THREE.EdgesGeometry(mesh.geometry, 20);
177- // const line = new THREE.LineSegments(
178- // edges,
179- // new THREE.LineBasicMaterial({ color: 0x111111 }),
180- // );
181- // mesh.add(line);
182- // }
183169 } ) ;
184170}
185171
186-
187172function renderScene ( ) {
188173 // renderer.render(scene, camera);
189174 composer . render ( ) ;
@@ -195,42 +180,58 @@ function animate() {
195180 renderScene ( ) ;
196181}
197182
198- function setupGUI ( initialState ) {
199- const gui = new GUI ( ) ;
200- const bloomFolder = gui . addFolder ( "Bloom" ) ;
201- const { bloomPass } = initialState ;
202-
203- // gui.onChange((event) => {
204- // console.log("Saving: %O", gui.save());
205- // });
206-
207- bloomFolder . add ( bloomPass , "radius" , 0 , 1 , 0.1 ) . onChange ( v => {
208- bloomPass . radius = Number ( v ) ;
209- } ) ;
210- bloomFolder . add ( bloomPass , "strength" , 0 , 3 , 0.1 ) . onChange ( v => {
211- bloomPass . strength = Number ( v ) ;
212- } ) ;
213- bloomFolder . add ( bloomPass , "threshold" , 0 , 1 , 0.1 ) . onChange ( v => {
214- bloomPass . threshold = Number ( v ) ;
215- } ) ;
216- }
183+ /**
184+ * Initializes the STL renderer.
185+ *
186+ * @param {string } url - URL to the STL file.
187+ * @param {RenderOptions } state - Renderer configuration options.
188+ */
189+ function init ( url , state ) {
190+ material = state . material ;
217191
218- function init ( ) {
219- const initialState = {
220- bloomPass : {
221- strength : 1 ,
222- radius : .5 ,
223- threshold : .5
224- }
225- } ;
192+ setupResizeHandler ( ) ;
226193 setupRenderer ( ) ;
227194 setupScene ( ) ;
228195 setupCameraAndControls ( ) ;
229- setupPostProcessing ( initialState . bloomPass ) ;
230- setupGUI ( initialState ) ;
231- setupResizeHandler ( ) ;
232- loadSTL ( window . STL_URL ) ; // STL_URL is defined in the <head>
196+ setupPostProcessing ( state . bloomPass ) ;
197+ loadSTL ( url ) ;
233198 animate ( ) ;
234199}
235200
236- init ( ) ;
201+ /**
202+ * Initialize the three.js renderer with the initial state for the gui
203+ *
204+ * `window.STL_URL` is defined in the <head> from the `scad.renderer.js` template
205+ */
206+ init ( window . STL_URL , {
207+ bloomPass : {
208+ strength : 1 ,
209+ radius : .2 ,
210+ threshold : .5
211+ } ,
212+ material : new THREE . MeshStandardMaterial ( {
213+ color : 0x5588ff ,
214+ emissive : 0x3366ff ,
215+ emissiveIntensity : 0.1 ,
216+ metalness : 0.1 ,
217+ roughness : 0.1 ,
218+ } ) ,
219+ } ) ;
220+
221+ /**
222+ * @typedef {Object } MaterialProps
223+ *
224+ * @property {number } [metalness] - Material metalness value (0 = goop, 1 = titanium).
225+ * @property {number } [roughness] - Material roughness value (0 = smooth, 1 = rough).
226+ */
227+
228+ /**
229+ * @typedef {Object } RenderOptions
230+ *
231+ * @property {Object } [bloomPass] - Bloom post-processing configuration.
232+ * @property {number } [bloomPass.strength] - Intensity of the bloom effect.
233+ * @property {number } [bloomPass.radius] - Radius of the bloom blur.
234+ * @property {number } [bloomPass.threshold] - Luminance threshold for bloom activation.
235+ *
236+ * @property {import("three").Material } [material] - Material configuration overrides.
237+ */
0 commit comments