|
22 | 22 | "imports": {
|
23 | 23 | "three": "../build/three.module.js",
|
24 | 24 | "three/addons/": "./jsm/",
|
25 |
| - "three-subdivide": "https://unpkg.com/three-subdivide@1.0.2/build/index.module.js" |
| 25 | + "three-subdivide": "https://unpkg.com/three-subdivide@1.1.2/build/index.module.js" |
26 | 26 | }
|
27 | 27 | }
|
28 | 28 | </script>
|
|
36 | 36 |
|
37 | 37 | let renderer, scene, camera;
|
38 | 38 | let texture;
|
39 |
| - let meshMaterial, meshNormal, meshSmooth; |
40 |
| - let wireMaterial, wireNormal, wireSmooth; |
| 39 | + let meshNormal, meshSmooth; |
| 40 | + let wireNormal, wireSmooth; |
| 41 | + let wireMaterial; |
41 | 42 |
|
42 | 43 | const params = {
|
43 | 44 | geometry: 'Box',
|
44 | 45 | iterations: 3,
|
45 | 46 | split: true,
|
46 | 47 | uvSmooth: false,
|
| 48 | + preserveEdges: false, |
47 | 49 | flatOnly: false,
|
48 | 50 | maxTriangles: 25000,
|
| 51 | + flatShading: false, |
| 52 | + textured: true, |
49 | 53 | wireframe: false
|
50 | 54 | };
|
51 | 55 |
|
|
70 | 74 | controls.target.set( 0, 0, 0 );
|
71 | 75 | controls.update();
|
72 | 76 |
|
| 77 | + scene.add( new THREE.HemisphereLight( 0xffffff, 0x737373, 1 ) ); |
| 78 | + |
| 79 | + const frontLight = new THREE.DirectionalLight( 0xffffff, 0.5 ); |
| 80 | + const backLight = new THREE.DirectionalLight( 0xffffff, 0.5 ); |
| 81 | + frontLight.position.set( 0, 1, 1 ); |
| 82 | + backLight.position.set( 0, 1, - 1 ); |
| 83 | + scene.add( frontLight, backLight ); |
| 84 | + |
73 | 85 | texture = new THREE.TextureLoader().load( './textures/uv_grid_opengl.jpg', () => {
|
74 | 86 |
|
75 | 87 | texture.wrapS = THREE.RepeatWrapping;
|
|
79 | 91 |
|
80 | 92 | } );
|
81 | 93 |
|
82 |
| - meshMaterial = new THREE.MeshBasicMaterial( { |
83 |
| - map: texture, |
84 |
| - polygonOffset: true, |
85 |
| - polygonOffsetFactor: 1, // positive value pushes polygon further away |
86 |
| - polygonOffsetUnits: 1, |
87 |
| - side: THREE.DoubleSide |
88 |
| - } ); |
89 |
| - meshNormal = new THREE.Mesh( new THREE.BufferGeometry(), meshMaterial ); |
90 |
| - meshSmooth = new THREE.Mesh( new THREE.BufferGeometry(), meshMaterial ); |
| 94 | + meshNormal = new THREE.Mesh( new THREE.BufferGeometry(), new THREE.MeshBasicMaterial() ); |
| 95 | + meshSmooth = new THREE.Mesh( new THREE.BufferGeometry(), new THREE.MeshBasicMaterial() ); |
91 | 96 | meshNormal.position.set( - 0.7, 0, 0 );
|
92 | 97 | meshSmooth.position.set( 0.7, 0, 0 );
|
93 | 98 | scene.add( meshNormal, meshSmooth );
|
|
114 | 119 |
|
115 | 120 | const geom = params.geometry.toLowerCase();
|
116 | 121 |
|
117 |
| - params.split = geom === 'box' || geom === 'ring'; |
| 122 | + params.split = geom === 'box' || geom === 'ring' || geom === 'plane'; |
118 | 123 | params.uvSmooth = geom === 'circle' || geom === 'plane' || geom === 'ring';
|
119 | 124 |
|
120 | 125 | refreshDisplay();
|
|
123 | 128 |
|
124 | 129 | folder1.add( params, 'iterations' ).min( 0 ).max( 5 ).step( 1 ).onFinishChange( updateMeshes );
|
125 | 130 | const splitController = folder1.add( params, 'split' ).onFinishChange( updateMeshes );
|
126 |
| - const smoothController = folder1.add( params, 'uvSmooth' ).onFinishChange( updateMeshes ); |
127 |
| - folder1.add( params, 'flatOnly' ).onFinishChange ( updateMeshes ); |
| 131 | + const uvSmoothController = folder1.add( params, 'uvSmooth' ).onFinishChange( updateMeshes ); |
| 132 | + const preserveController = folder1.add( params, 'preserveEdges' ).onFinishChange( updateMeshes ); |
| 133 | + folder1.add( params, 'flatOnly' ).onFinishChange( updateMeshes ); |
128 | 134 | folder1.add( params, 'maxTriangles' ).onFinishChange( updateMeshes );
|
129 | 135 |
|
130 |
| - const folder2 = gui.addFolder( 'View' ); |
131 |
| - folder2.add( params, 'wireframe' ).onFinishChange( updateMeshes ); |
| 136 | + const folder2 = gui.addFolder( 'Material' ); |
| 137 | + folder2.add( params, 'flatShading' ).onFinishChange( updateMaterial ); |
| 138 | + folder2.add( params, 'textured' ).onFinishChange( updateMaterial ); |
| 139 | + folder2.add( params, 'wireframe' ).onFinishChange( updateWireframe ); |
132 | 140 |
|
133 | 141 | function refreshDisplay() {
|
134 | 142 |
|
135 | 143 | geomController.updateDisplay();
|
136 | 144 | splitController.updateDisplay();
|
137 |
| - smoothController.updateDisplay(); |
| 145 | + uvSmoothController.updateDisplay(); |
| 146 | + preserveController.updateDisplay(); |
138 | 147 |
|
139 | 148 | updateMeshes();
|
140 | 149 |
|
|
159 | 168 | return new THREE.ConeGeometry( 0.6, 1.5, 5, 3 );
|
160 | 169 |
|
161 | 170 | case 'cylinder':
|
162 |
| - return new THREE.CylinderGeometry( 0.5, 0.5, 1, 5, 5 ); |
| 171 | + return new THREE.CylinderGeometry( 0.5, 0.5, 1, 5, 4 ); |
163 | 172 |
|
164 | 173 | case 'dodecahedron':
|
165 | 174 | return new THREE.DodecahedronGeometry( 0.6 );
|
|
175 | 184 |
|
176 | 185 | for ( let i = 0; i < 65; i += 5 ) {
|
177 | 186 |
|
178 |
| - let x = ( Math.sin( i * 0.2 ) * Math.sin( i * 0.1 ) * 15 + 50 ) * 1.2; |
179 |
| - let y = ( i - 5 ) * 3; |
| 187 | + const x = ( Math.sin( i * 0.2 ) * Math.sin( i * 0.1 ) * 15 + 50 ) * 1.2; |
| 188 | + const y = ( i - 5 ) * 3; |
180 | 189 | points.push( new THREE.Vector2( x * 0.0075, y * 0.005 ) );
|
181 | 190 |
|
182 | 191 | }
|
|
214 | 223 | function updateMeshes() {
|
215 | 224 |
|
216 | 225 | const normalGeometry = getGeometry();
|
217 |
| - const smoothGeometry = LoopSubdivision.modify( |
218 |
| - normalGeometry, |
219 |
| - params.iterations, |
220 |
| - params.split, |
221 |
| - params.uvSmooth, |
222 |
| - params.flatOnly, |
223 |
| - params.maxTriangles |
224 |
| - ); |
| 226 | + |
| 227 | + const smoothGeometry = LoopSubdivision.modify( normalGeometry, params.iterations, params ); |
225 | 228 |
|
226 | 229 | meshNormal.geometry.dispose();
|
227 | 230 | meshSmooth.geometry.dispose();
|
|
233 | 236 | wireNormal.geometry = normalGeometry.clone();
|
234 | 237 | wireSmooth.geometry = smoothGeometry.clone();
|
235 | 238 |
|
| 239 | + updateMaterial(); |
| 240 | + |
| 241 | + } |
| 242 | + |
| 243 | + function disposeMaterial( material ) { |
| 244 | + |
| 245 | + const materials = Array.isArray( material ) ? material : [ material ]; |
| 246 | + |
| 247 | + for ( let i = 0; i < materials.length; i ++ ) { |
| 248 | + |
| 249 | + if ( materials[ i ].dispose ) materials[ i ].dispose(); |
| 250 | + |
| 251 | + } |
| 252 | + |
| 253 | + } |
| 254 | + |
| 255 | + function updateMaterial() { |
| 256 | + |
| 257 | + disposeMaterial( meshNormal.material ); |
| 258 | + disposeMaterial( meshSmooth.material ); |
| 259 | + |
| 260 | + const materialParams = { |
| 261 | + color: ( params.textured ) ? 0xffffff : 0x808080, |
| 262 | + flatShading: params.flatShading, |
| 263 | + map: ( params.textured ) ? texture : null, |
| 264 | + polygonOffset: true, |
| 265 | + polygonOffsetFactor: 1, // positive value pushes polygon further away |
| 266 | + polygonOffsetUnits: 1 |
| 267 | + }; |
| 268 | + |
| 269 | + switch ( params.geometry.toLowerCase() ) { |
| 270 | + |
| 271 | + case 'circle': |
| 272 | + case 'lathe': |
| 273 | + case 'plane': |
| 274 | + case 'ring': |
| 275 | + |
| 276 | + materialParams.side = THREE.DoubleSide; |
| 277 | + break; |
| 278 | + |
| 279 | + case 'box': |
| 280 | + case 'capsule': |
| 281 | + case 'cone': |
| 282 | + case 'cylinder': |
| 283 | + case 'dodecahedron': |
| 284 | + case 'icosahedron': |
| 285 | + case 'octahedron': |
| 286 | + case 'sphere': |
| 287 | + case 'tetrahedron': |
| 288 | + case 'torus': |
| 289 | + case 'torusknot': |
| 290 | + |
| 291 | + materialParams.side = THREE.FrontSide; |
| 292 | + break; |
| 293 | + |
| 294 | + } |
| 295 | + |
| 296 | + meshNormal.material = meshSmooth.material = new THREE.MeshStandardMaterial( materialParams ); |
| 297 | + |
| 298 | + render(); |
| 299 | + |
| 300 | + } |
| 301 | + |
| 302 | + function updateWireframe() { |
| 303 | + |
236 | 304 | wireNormal.visible = wireSmooth.visible = params.wireframe;
|
237 | 305 |
|
238 | 306 | render();
|
|
0 commit comments