ProfiledContourGeometry MultiMaterial

Here Modify Extruded Geometry
there was a question about textures on frames.

I am just finishing the frame profiles for my showroom and have modified the example above. The frame is disassembled into frame strips. These are combined to a group. For the material you can choose between two variants.

   if ( matPerSquare ) { // MultiMaterial support (for each square)
    ...

    } else {  // material per frame strip
    ...

2020-06-15 20.36.46

Check it out

https://hofk.de/main/discourse.threejs/2020/ProfiledContourGeometryUVs/ProfiledContourGeometryUVs.html

(Update: There was a typo in the link. Directory …GeometrUVs without y)

<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/profiledcontourgeometry-multimaterial/5801 -->
<head>
	<title> ProfiledContourGeometryUVs  </title>
	<meta charset="utf-8" />
	<style>
	body {
		margin: 0;
	}
	</style>
</head>
<body> </body>
<script src="../js/three.min.117.js"></script>
<script src="../js/OrbitControls.117.js"></script>

<script>
'use strict'

// @author hofk

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( -6, 18, 20 );
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const controls = new THREE.OrbitControls( camera, renderer.domElement );

const light = new THREE.DirectionalLight( 0xffffff, 0.6);
light.position.setScalar( 10 );
scene.add( light );
scene.add(new THREE.AmbientLight( 0xffffff, 0.8));

const helper = new THREE.GridHelper( 20, 20);
scene.add( helper );

const detail = 5;
let profileShape1 = [];

for ( let i = detail; i > -1; i --){
	
	profileShape1.push ( 1.8 * Math.cos( i / detail * Math.PI * 2 ), 1.8 * Math.sin( i / detail * Math.PI * 2 ) );
	
}

const contour1 = [
	-3,  4,
	 0,  4,// here only to show that angle of 180° horizontal works  
	 4,  4,
	 2,  1,
	 4, -2,
	 0, -3,
	-4, -3,
	-4,  0
];

const profileShape2 = [ -1,1, 1,1, 1,-1, -1,-1 ];

const contour2 = [
	 2,-2,
	 2, 0,
	 4, 4,
	 9, 4,
	 9, 2, // here only to show that angle of 180° vertikal works  
	 9, 0,
];


const  tex1 = new THREE.TextureLoader().load( 'uvgrid01.png' );
const  tex2 = new THREE.TextureLoader().load( 'beech.jpg' ); // License: free, Non-commercial use
const  tex3 = new THREE.TextureLoader().load( 'pngwing-com-water.png' );// License:  free, Non-commercial use

//const m0 = new THREE.MeshPhongMaterial( { color: 0xfa0001, side: THREE.DoubleSide } );
//const m1 = new THREE.MeshPhongMaterial( { color: 0xff7b00, side: THREE.DoubleSide } );
//const m2 = new THREE.MeshPhongMaterial( { color: 0xf9f901, side: THREE.DoubleSide } );

const m0 = new THREE.MeshPhongMaterial( { map: tex3, side: THREE.DoubleSide } );
const m1 = new THREE.MeshPhongMaterial( { map: tex2, side: THREE.DoubleSide } );
const m2 = new THREE.MeshPhongMaterial( { map: tex1, side: THREE.DoubleSide } );
const m3 = new THREE.MeshPhongMaterial( { color: 0x008601, side: THREE.DoubleSide } );
const m4 = new THREE.MeshPhongMaterial( { color: 0x01bbbb, side: THREE.DoubleSide } );
const m5 = new THREE.MeshPhongMaterial( { color: 0x250290, side: THREE.DoubleSide } );

//const m3 = new THREE.MeshPhongMaterial( { map: tex1, side: THREE.DoubleSide } );
//const m4 = new THREE.MeshPhongMaterial( { map: tex1, side: THREE.DoubleSide } );
//const m5 = new THREE.MeshPhongMaterial( { map: tex1, side: THREE.DoubleSide } );

const m6 = new THREE.MeshPhongMaterial( { color: 0xfc4ea5, side: THREE.DoubleSide } );
const m7 = new THREE.MeshPhongMaterial( { color: 0x83058a, side: THREE.DoubleSide } );
const m8 = new THREE.MeshPhongMaterial( { color: 0x83058a, side: THREE.DoubleSide } );

const materials = [ m0, m1, m2, m3, m4, m5, m6, m7, m8, m0, m1, m2, m3, m4, m5, m6, m7, m8, m0, m1, m2, m3, m4, m5, m6, m7, m8, m0, m1, m2, m3, m4, m5, m6, m7, m8, m0, m1, m2, m3, m4, m5, m6, m7, m8, m0, m1, m2, m3, m4, m5, m6, m7, m8, m0, m1, m2, m3, m4, m5, m6, m7, m8, m0, m1, m2, m3, m4, m5, m6, m7, m8, m0, m1, m2, m3, m4, m5, m6, m7, m8 ];

//............................................................ matPerSquare, contourClosed 

const frame1 = ProfiledContourUV( profileShape1, contour1, materials, false , true );
scene.add( frame1 );
frame1.position.x = -5;
frame1.position.z = 3;
frame1.rotation.y = 2.8;

const frame2 = ProfiledContourUV( profileShape2, contour2, materials, true, false );
frame2.position.z = -4;
scene.add( frame2 );

render();

function render() {
	
	requestAnimationFrame(render);
	renderer.render(scene, camera);
	
}

function ProfiledContourUV( profileShape, contour, materials, matPerSquare, contourClosed ) {
	
	// creates group of frame strips, non indexed BufferGeometry
	
	const len = ( x, y, z ) => Math.sqrt( x * x + y * y + z * z );
	const dot = (x1, y1, z1, x2, y2, z2) => ( x1 * x2 + y1 * y2 + z1 * z2 );
	
	matPerSquare = matPerSquare!== undefined ? matPerSquare : false;
	contourClosed = contourClosed !== undefined ? contourClosed : true;
	
	if( contourClosed ) contour.push( contour[ 0 ], contour[ 1 ] );
	
	const hs1 = contour.length / 2;
	const rs1 = profileShape.length / 2;
	const hs = hs1 - 1; // height segments
	const rs = rs1 - 1; // radius segments
	
	let	vtx = [];    // rs1 many vertex colums
	let	frmpos = []; // hs many geometries and meshes
	let	frmuvs = []; // hs many uv's' 
	
	for ( let j = 0; j < rs; j ++ ) {
		
		vtx.push( [] );
		frmpos.push( [] );
		frmuvs.push( [] );
		
	}
	
	vtx.push( [] ); // last colum
	
	let gFrame = []; // geometries of frame strips
	let frame = [];  // meshes of frame strips
	
	const fGroup = new THREE.Group( );
	
	let i1, i2, i3, i6, j1, j3;
	let xc0, yc0, xc1, yc1, xc2, yc2, xSh, xDiv;
	let dx, dy, dx0, dy0, dx2, dy2;
	let e0x, e0y,e0Length, e2x, e2y, e2Length, ex, ey, eLength;
	let xd, phi, bend;
	let x, y, z, x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4;
	let a, u1, u2, u3, u4, d2, d3;
	const epsilon = 0.000001;
 	
	for ( let j = 0; j < rs1; j ++ ) {
		
		for ( let i = 0; i < hs1; i ++ ) {
		
			i2 = 2 * i; 
			
			xc1 = contour[ i2 ];
			yc1 = contour[ i2 + 1 ];
			
			if ( i === 0 ) {
				
				xc0 = contour[ ( hs - 1 ) * 2 ]; // penultimate point
				yc0 = contour[ ( hs - 1 ) * 2 + 1 ];
				
			} else {
						
				xc0 = contour[ i2 - 2 ]; 	// previous point
				yc0 = contour[ i2 - 1 ];
				
			}
			
			if ( i === hs ) {
				
				xc2 = contour[ 2 ];			// second point
				yc2 = contour[ 3 ];
				
			} else {
				
				xc2 = contour[ i2 + 2 ]; 	// next point
				yc2 = contour[ i2 + 3 ];
				
			}	
			
			if ( !contourClosed ) {
				
				if ( i === 0 ) {
					
					// direction
					dx2 = xc2 - xc1;
					dy2 = yc2 - yc1;
					
					// unit vector
					e2Length = Math.sqrt( dx2 * dx2 + dy2 * dy2 );
					
					e2x = dx2 / e2Length;
					e2y = dy2 / e2Length;
					
					// orthogonal
					ex = e2y;
					ey = -e2x;
					
				}
				
				if ( i === hs ) {
					
					// direction
					
					dx0 = xc1 - xc0;
					dy0 = yc1 - yc0;
					
					// unit vector
					e0Length = Math.sqrt( dx0 * dx0 + dy0 * dy0 );
					
					e0x = dx0 / e0Length;
					e0y = dy0 / e0Length;
					
					// orthogonal
					ex = e0y;
					ey = -e0x;
					
				}
				
				xDiv = 1;
				bend = 1;
				
			}
			
			if ( ( i > 0 && i < hs ) || contourClosed ) {
				
				// directions
				
				dx0 = xc0 - xc1;
				dy0 = yc0 - yc1;
				
				dx2 = xc2 - xc1;
				dy2 = yc2 - yc1;
				
				if( Math.abs( ( dy2 / dx2 ) - ( dy0 / dx0 ) ) < epsilon ) { // prevent 0
					
					dy0 += epsilon;
					
				}
				
				if( Math.abs( ( dx2 / dy2 ) - ( dx0 / dy0 ) ) < epsilon ) { // prevent 0
					
					dx0 += epsilon;
					
				}  
				
				// unit vectors
				
				e0Length = Math.sqrt( dx0 * dx0 + dy0 * dy0 );
				
				e0x = dx0 / e0Length;
				e0y = dy0 / e0Length;
				
				e2Length = Math.sqrt( dx2 * dx2 + dy2 * dy2 );
				
				e2x = dx2 / e2Length;
				e2y = dy2 / e2Length;
				
				// direction transformed 
				
				ex = e0x + e2x;
				ey = e0y + e2y;
				
				eLength = Math.sqrt( ex * ex + ey * ey );
				
				ex = ex / eLength;
				ey = ey / eLength;
				
				phi = Math.acos( e2x * e0x + e2y * e0y ) / 2;
				
				bend = Math.sign( dx0 * dy2 - dy0 * dx2 ); // z cross -> curve bending
				
				xDiv = Math.sin( phi );
				
			}
			
			xSh = profileShape[ j * 2 ];
			
			xd = xSh / xDiv;
			
			dx = xd * bend * ex;
			dy = xd * bend * ey;
			
			x = xc1 + dx; 
			y = yc1 + dy;
			z = profileShape[ j * 2 + 1 ];	// ySh
			
			vtx[ j ].push( x, y, z );	// store vertex
			
			//dApex = xd * Math.cos( phi );
		
		}
		
	}
	
	for ( let j = 0; j < rs; j ++ ) {
		
		j1 = j + 1;
		j3 = 3 * j;
		
		for ( let i = 0; i < hs; i ++ ) {
			
			i3 = 3 * i;
			i6 = i3 + 3;
			
			x1 = vtx[ j ][ i3 ];
			y1 = vtx[ j ][ i3 + 1 ];
			z1 = vtx[ j ][ i3 + 2 ] ;
			
			x2 = vtx[ j1 ][ i3 ];
			y2 = vtx[ j1 ][ i3 + 1 ];
			z2 = vtx[ j1 ][ i3 + 2 ];
			
			x3 = vtx[ j1 ][ i6 ];
			y3 = vtx[ j1 ][ i6 + 1 ];
			z3 = vtx[ j1 ][ i6 + 2 ];
			
			x4 = vtx[ j ][ i6 ];
			y4 = vtx[ j ][ i6 + 1 ];
			z4 = vtx[ j ][ i6 + 2 ];
			
			frmpos[ j ].push( x1, y1, z1, x2, y2, z2, x4, y4, z4, x2, y2, z2, x3, y3, z3, x4, y4, z4 );
			
			a = len( x4 - x1, y4 - y1, z4 - z1 );
			
			d2 = dot( x4 - x1, y4 - y1, z4 - z1, x2 - x1, y2 - y1, z2 - z1 ) / a;
			d3 = dot( x1 - x4, y1 - y4, z1 - z4, x3 - x4, y3 - y4, z3 - z4, ) / a;
			
			if ( d2 >= 0 && d3 >= 0 ) {
				
				u1 = 0;
				u2 = d2 / a;
				u3 = 1 - d3 / a;
				u4 = 1;
				
			}
			
			if ( d2 >= 0 && d3 < 0 ) {
				
				u1 = 0;
				u2 = d2 / ( a - d3 );
				u3 = 1;
				u4 = 1 + d3 / ( a - d3 ); 
				
			}
			
			if ( d2 < 0 && d3 < 0 ) {
				
				u1 = -d2 / ( a - d2 - d3 );
				u2 = 0;
				u3 = 1;
				u4 = 1 + d3 / ( a - d2 - d3  );
				
			}
			
			if ( d2 < 0 && d3 >= 0 ) {
				
				u1 = -d2 / ( a - d2  );
				u2 = 0;
				u3 = 1 - d3 / ( a - d2  );
				u4 = 1;
				
			}
			
			frmuvs[ j ].push( u1, 1, u2, 0, u4, 1, u2, 0, u3, 0, u4, 1 );
			
		}
		
	}
	
	for ( let j = 0; j < rs; j ++ ) {
		
		gFrame[ j ] = new THREE.BufferGeometry( );
		gFrame[ j ].setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( frmpos[ j ] ), 3 ) );
		gFrame[ j ].setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( frmuvs[ j ] ), 2 ) );
		
		if ( matPerSquare ) { // MultiMaterial support (for each square)
			
			for ( let i = 0; i < hs; i ++ ) {
				
				gFrame[ j ].addGroup( i * 6, 6, j * hs + i ); 
				
			}
			
			frame[ j ] = new THREE.Mesh( gFrame[ j ], materials );
			
		} else { // material per frame strip
			
			frame[ j ] = new THREE.Mesh( gFrame[ j ], materials[ j ] );
			
		}
		
		gFrame[ j ].computeVertexNormals( );
		
		fGroup.add( frame[ j ] )
		
	}
	
	return fGroup; // group of frame strips
	
}

</script>
</html>
2 Likes