ProfiledContourGeometry MultiMaterial

materials
buffergeometry
#1

With reference to
ProfiledContourGeometry and Simple Rectangular Geometry Extrusion (Anyone?)
here another variant of the creation of such a geometry. An indexed BufferGeometry will be created.

Look there: http://threejs.hofk.de/ProfiledContourGeometryMM/ProfiledContourGeometryMM.html

The definition is done without using any other three.js objects except THREE.BufferGeometry( ). This makes it very easy to use MultiMaterial. There are two different modes to choose from.

In 7 :rainbow: colors:

Setting UV’s doesn’t make much sense in the general form. However, if you have a concrete example like a simple rectangular door frame, the uv values can be easily determined and added.

I will add this geometry to the THREEg addon. Addon to create special / extended geometries


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

<script>
'use strict'

// @author hofk

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

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

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

var detail = 7
var profileShape1 = [];

for ( var i = 0; i < detail + 1; i ++ ){
	
	profileShape1.push ( 0.5 * Math.cos( i / detail * Math.PI * 2 ), 0.5 * Math.sin( i / detail * Math.PI * 2 ) );
	
}

var contour1 = [
	-3,  4,
	 0,  4,
	 4,  4,
	 2,  1,
	 4, -2,
	 0, -3,
	-4, -3,
	-4,  0
];

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

var contour2 = [
	 4, 0,
	 4, 8,
	 8, 8,
	12, 8,
	12, 2, // here only to show that angle of 180° vertikal works  
	12, 0,
];

var materials = [ // rainbow-colored
	
	new THREE.MeshPhongMaterial( { color: 0xfa0001, side: THREE.DoubleSide } ),
	new THREE.MeshPhongMaterial( { color: 0xff7b00, side: THREE.DoubleSide } ),
	new THREE.MeshPhongMaterial( { color: 0xf9f901, side: THREE.DoubleSide } ),
	new THREE.MeshPhongMaterial( { color: 0x008601, side: THREE.DoubleSide } ),
	new THREE.MeshPhongMaterial( { color: 0x01bbbb, side: THREE.DoubleSide } ),
	new THREE.MeshPhongMaterial( { color: 0x250290, side: THREE.DoubleSide } ),	
	new THREE.MeshPhongMaterial( { color: 0xfc4ea5, side: THREE.DoubleSide } ),
	new THREE.MeshPhongMaterial( { color: 0x83058a, side: THREE.DoubleSide } ),
	new THREE.MeshPhongMaterial( { color: 0x83058a, side: THREE.DoubleSide } )
	
]

var material = materials[ 2 ].clone();
material.wireframe = true;

//..................................................... contourClosed, openEnded, profileMaterial
var geometry1 = ProfiledContourMMgeometry(profileShape1, contour1, false, false, true );
var fullProfile1 = new THREE.Mesh( geometry1, materials );
fullProfile1.position.z = 6;
scene.add( fullProfile1 );

var fullProfile12 = new THREE.Mesh( geometry1, material );
fullProfile12.position.z = -8;
scene.add( fullProfile12 );

var geometry13 = ProfiledContourMMgeometry(profileShape1, contour1 );
var fullProfile13 = new THREE.Mesh( geometry13, materials );
fullProfile13.position.x = -5;
scene.add( fullProfile13 );

var geometry2 = ProfiledContourMMgeometry(profileShape2, contour2, false, true );
var fullProfile2 = new THREE.Mesh( geometry2, materials );
fullProfile2.scale.set( 0.5, 0.5, 0.5 );
scene.add( fullProfile2 );

render();

function render() {

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

function ProfiledContourMMgeometry( profileShape, contour, contourClosed, openEnded, profileMaterial ) {
	
	contourClosed = contourClosed !== undefined ? contourClosed : true;
	openEnded = openEnded !== undefined ? openEnded : false;
	openEnded = contourClosed === true ? false : openEnded;
	profileMaterial = profileMaterial !== undefined ? profileMaterial : false;
	
	if( contourClosed ) contour.push( contour[ 0 ], contour[ 1 ] );
	
	var hs1 = contour.length / 2;
	var rs1 = profileShape.length / 2;
	var hs = hs1 - 1; // height segments 
	var rs = rs1 - 1; // radius segments
	
	var faceCount = hs * rs * 2 + ( openEnded ? 0 : rs * 2 );
	var posCount = hs1 * rs1 + ( openEnded ? 0 : 2 );
	
	var g = new THREE.BufferGeometry( );
	g.indices = new Uint32Array( faceCount * 3 );
	g.positions = new Float32Array( posCount * 3 );
	
	g.setIndex( new THREE.BufferAttribute( g.indices, 1 ) );
	g.addAttribute( 'position', new THREE.BufferAttribute( g.positions, 3 ) );
	
	var a, b1, c1, b2, c2;
	var i1, i2;
	var xc0, yc0, xc1, yc1, xc2, yc2, xSh, xDiv;
	var dx0, dy0, dx2, dy2;
	var e0x, e0y,e0Length, e2x, e2y, e2Length, ex, ey, eLength;
	var phi, bend;
	var x, y, z;
	var vIdx, posIdx;
	var epsilon = 0.000001;
	var idx = 0;
	
	for ( var i = 0; i < hs; i ++ ) {
		
		if ( profileMaterial ) g.addGroup( idx, rs * 6, i ); // MultiMaterial support
		
		i1 = i + 1;
		
		for ( var j = 0; j < rs; j ++ ) {
			
			// 2 faces / segment,  3 vertex indices
			a =  rs1 * i + j;
			c1 = rs1 * i1 + j; // left 
			b1 = c1 + 1;
			//c2 = b1;         // right
			b2 = a + 1;
			
			g.indices[ idx     ] = a; // left 
			g.indices[ idx + 1 ] = b1;
			g.indices[ idx + 2 ] = c1; 
			
			g.indices[ idx + 3 ] = a; // right 
			g.indices[ idx + 4 ] = b2,
			g.indices[ idx + 5 ] = b1; // = c2
			
			if ( !profileMaterial ) g.addGroup( idx, 6, j ); // MultiMaterial support
			
			idx += 6;
			
		}
		
	}
	
	if( !openEnded ) {
		
		g.addGroup( idx, rs * 3, rs ); // MultiMaterial support
		
		a = hs1 * rs1;
		
		for ( var j = 0; j < rs; j ++ ) {
			
			g.indices[ idx     ] = a; 
			g.indices[ idx + 1 ] = j + 1;
			g.indices[ idx + 2 ] = j;
			
			idx += 3;
			
		}
		
		g.addGroup( idx, rs * 3, rs + 1 ); // MultiMaterial support
		
		a += 1; 
		
		for ( var j = rs1 + 1; j > 1; j -- ) {
			
			g.indices[ idx     ] = a; 
			g.indices[ idx + 1 ] = a - j;
			g.indices[ idx + 2 ] = a - j + 1;
			
			idx += 3;
			
		}
		
	}
	
	for ( var 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 );
			
		}
		
		for ( var j = 0; j < rs1; j ++ ) {
			
			xSh = profileShape[ j * 2 ];
			
			x = xc1 + xSh / xDiv * bend * ex; 
			y = yc1 + xSh / xDiv * bend * ey;
			z = profileShape[ j * 2 + 1 ];	 // ySh
			
			vIdx = rs1 * i + j;
			
			posIdx = vIdx * 3;
			
			g.positions[ posIdx ] = x;
			g.positions[ posIdx + 1 ] = y;
			g.positions[ posIdx + 2 ] = z;
			
		}
		
	}
	
	if( !openEnded ) {
		
		g.positions[ hs1 * rs1 * 3 ] = contour[ 0 ];
		g.positions[ hs1 * rs1 * 3 + 1 ] = contour[ 1 ];
		g.positions[ hs1 * rs1 * 3 + 2 ] = 0;
		
		g.positions[ hs1 * rs1 * 3 + 3 ] = contour[ hs * 2 ];
		g.positions[ hs1 * rs1 * 3 + 4 ] = contour[ hs * 2 + 1 ];
		g.positions[ hs1 * rs1 * 3 + 5 ] = 0;
		
	}
	
	g.computeVertexNormals();
	
	return g;
	
}

</script>
</html>
2 Likes
How to create a Three.js 3D line series with width and thickness?