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
...
Check it out
(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>