Construction
One defines with some 3D points the center curve and for some angles the orthogonal radial distances of the hull from these points.
By specifying the maximum values, one determines the vertical and radial fineness of the representation.
With a definition string of three characters one determines whether caps are to be generated and whether these are to be flat or smooth. The third character
allows radial connection without seam or symmetrical geometry. For symmetrical geometry the last angle must be 180°.
3 characters ‘string’ = ‘withTop withBottom conncted/symmetric’.
withTop/Bottom: x, f, s = no, flat, smooth
conncted/symmetric: x, c, s = no, connected, symmetric
By specifying deviations from the defined points of the center curve, a very organic movement of the geometry can be optionally created.
The parameters and values are summarized in a design matrix. The first row of this matrix is an array of the parameters, the further rows contain the values in an array each.
This way you can arrange the values in the columns exactly under the parameters.
const design = [
// first0,.. angles.., max angle
[ maxVert., 'string', maxRadial, 0, ϕ1, ϕ2, ... ϕmax ],
// center x,y,z distance to center orthogonal-radial
[ x, y, z, r0, r1, r2, ... r ], // top
...
[ ] // bottom
];
Example:
Since an exact match is not possible for arbitrarily defined centers and angles and likewise arbitrary maximum values of fineness, the closest values are determined and used for the calculation.
The optional values of the deviations are to be noted in corresponding structure to the center points. From these values further points are generated on a curve. Their number is to be specified in a separate parameter.
The motion is created using the geometry.morph( geometry.cPts[ ] ) method.
g.cPts = []; // array of morphed g.cPoints
where
g.cPoints = new THREE.CatmullRomCurve3( g.dsgnCenters, false ).getSpacedPoints( g.vertical );
Try it out: Curved2Geometry
on GitHub - hofk/threejsResources: Resources for three.js
<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/curved2geometry-a-twofold-curved-geometry/25580 -->
<head>
<title> Curved2Geometry </title>
<meta charset="utf-8" />
<style>
body{
overflow: hidden;
margin: 0;
}
</style>
</head>
<body></body>
<script type="module">
// @author hofk
import * as THREE from "../jsm/three.module.127.js";
import { OrbitControls } from "../jsm/OrbitControls.127.js";
import { VertexNormalsHelper } from "../jsm/VertexNormalsHelper.127.js";
const scene = new THREE.Scene( );
const camera = new THREE.PerspectiveCamera( 55, innerWidth / innerHeight, 0.01, 1000 );
camera.position.set( 8, 12, 22 );
const renderer = new THREE.WebGLRenderer( );
renderer.setSize( innerWidth, innerHeight );
renderer.setClearColor( 0x111111 );
document.body.appendChild(renderer.domElement);
const light = new THREE.AmbientLight( 0x404040, 1.2 ); // soft white light
scene.add( light );
const pointLight1 = new THREE.PointLight( 0xffffff, 1.1 );
pointLight1.position.set( -2, 10, 5 );
scene.add( pointLight1 );
new OrbitControls( camera, renderer.domElement );
scene.add( new THREE.GridHelper( 20, 20 ) );
const matT = new THREE.MeshBasicMaterial( { wireframe: false, side: THREE.DoubleSide, map: new THREE.TextureLoader( ).load( 'uvgrid01.png' ) } );
const matT1 = new THREE.MeshBasicMaterial( { wireframe: false, side: THREE.DoubleSide, map: new THREE.TextureLoader( ).load( 'fisch1Tex.png' ) } );
const matT2 = new THREE.MeshBasicMaterial( { wireframe: false, side: THREE.DoubleSide, map: new THREE.TextureLoader( ).load( 'fisch2Tex.png' ) } );
const mata = new THREE.MeshPhongMaterial( { wireframe: false, side: THREE.DoubleSide, color: 0xffffbb } );
const matb = new THREE.MeshPhongMaterial( { wireframe: false, side: THREE.DoubleSide, color: 0xffaaff } );
const matc = new THREE.MeshPhongMaterial( { wireframe: true, side: THREE.DoubleSide, color: 0xffffff } );
const mat = [ matT, mata, matb ];
const matw = [ matc, mata, matb ];
const mats = [ matb, mata, matb ];
const matf1 = [ matT1, mata, matb ];
const matf2 = [ matT2, mata, matb ];
// no flat smooth symmetric (180°)
// string - with top: x, f, s | with bottom: x, f, s | connected: x, c, s
const design = [
//max vert,str,radial,0 first, ... , max angle
[ 96, 'ffc', 36, 0, 80, 188, 260, 290 ],
// center x, y, z, distance to center orthogonal-radial
[-0.5, 5.0, -2, 0.3, 0.3, 0.4, 0.3, 0.3 ], // top, max vert. ( y )
[ 0.3, 4.5, 0, 1.8, 1.6, 0.0, 1.5, 1.9 ],
[ 0.2, 1.0, 0, 0.3, 0.5, 0.1, 0.3, 0.4 ], // ... to some centers x,y,z
[ 0.1, 0.0, 0, 0.7, 0.7, 1.1, 0.7, 0.7 ],
[ 0.0, -1.5, 1, 0.3, 0.6, 0.8, 0.9, 0.6 ],
[ 0.2, -5.0, 1, 0.8, 0.3, 0.5, 0.3, 0.5 ],
[14.5, -2.5, 8, 0.1, 0.2, 0.2, 0.1, 0.1 ], // bottom
];
const centerMorph = [
// x, y, z difference to initial position
[
[ 0.1, 0.5, -0.2, ],
[ 0.1, 0.3, 0.0, ],
[ 0.0, 0.2, 0.0, ],
[-0.1, 0.0, 0.0, ],
[ 0.0, 0.0, 0.0, ],
[ 0.3, -0.9, 0.5, ],
[ 0.0, 0.0, 0.0, ],
],
[
[-0.1, 0.7, -0.4, ],
[-0.1, 0.5, 0.2, ],
[ 0.0, 0.4, 0.1, ],
[ 0.1, 0.2, 0.2, ],
[ 0.0, 0.0, 0.0, ],
[ 0.6, -1.3, 0.8, ],
[ 0.0, 0.0, 0.0, ],
],
[
[-0.2, 0.2, 0.1, ],
[-0.3, 0.1, 0.2, ],
[ 0.1, 0.1, -0.1, ],
[ 0.2, 0.3, -0.1, ],
[ 0.0, 0.0, 0.0, ],
[ 0.4, -0.8, -0.6, ],
[ 0.0, 0.1, 0.0, ],
],
];
const morphCount = 20;
const geo = Curved2Geometry( design, centerMorph, morphCount );
let mesh = new THREE.Mesh( geo, mat );
scene.add( mesh );
mesh.position.x = 12;
mesh.rotation.y = Math.PI;
// .....................................................................
const design1 = [
//max vert, str, radial, 0 first, .., max angle
[ 60, 'fxc', 95, 0, 106, 220, 280 ],
// center x, y, z, distance to center orthogonal-radial
[ 0.8, 2.4, 0.0, 1.0, 1.0, 1.2, 0.7 ], // top
[ 0.1, 1.2, 0.1, 1.2, 0.6, 0.6, 0.7 ], // to some centers x,y,z
[ 0.0, -1.0, 0.0, 0.2, 0.3, 0.2, 0.4 ],
[ 0.0, -2.5, 0.4, 0.7,-0.2, 0.7, 0.6 ], // bottom
];
const geo1 = Curved2Geometry( design1 );
let mesh1 = new THREE.Mesh( geo1, matw );
scene.add( mesh1 );
mesh1.position.x = -2;
mesh1.position.z = 12;
// .....................................................................
const design2 = [
//max vert,str, radial, 0 first, .., max angle - if '..s' 0°, 180° fixed
[ 15, 'fss', 72, 0, 18, 120, 180 ],
// center x, y, z, distance to center orthogonal-radial
[ -4, 1.2, 0.4, 1.4, 0.8, 1.2, 0.7 ], // top
[ -1, 0.8, 0.3, 1.6, 1.6, 0.8, 0.7 ], // to some centers x,y,z
[ 1, -1.1, 0.1, 1.7, 1.3, 0.8, 0.7 ],
[ 4, -1.2, 0.2, 1.6, 1.8, 0.9, 0.7 ], // bottom
];
const geo2 = Curved2Geometry( design2 );
let mesh2 = new THREE.Mesh( geo2, mats );
scene.add( mesh2 );
mesh2.position.x = -14;
mesh2.position.z = 5;
const helper =new VertexNormalsHelper( mesh2, 0.3, 0x00ff00, 1 );
scene.add( helper );
// ...................................................................
const design3 = [
//max vert, str, radial, 0 first, .. , max angle
[ 180, 'xxx', 72, 0, 45, 90, 135, 180, 225, 270 ],
// center x, y, z, distance to center orthogonal-radial
[ 1.9, 6.0, 4.0, 0.8, 0.9, 0.8, 0.8, 0.9, 0.9, 0.9 ], // top
[ 0.0, 0.0, 0.0, 0.5, 3.0, 0.3, 0.2, 0.1, 0.3, 0.3 ],
[-2.9, -3.0, 3.0, 0.2, 0.1, 0.3, 0.3, 0.6, 0.1, 0.2 ], // bottom
];
const centerMorph3 = [
// x, y, z difference to initial position
[
[ 1.0, 3.3, -0.8, ],
[ 0.0, 0.2, -0.5, ],
[ 1.0, 0.1, -0.1, ],
],
[
[ 0.6, 0.1, -0.9, ],
[-3.4, 0.8, 0.1, ],
[ 1.0, 0.5, -0.5, ],
],
[
[ 2.5, 3.0, 0.0, ],
[ 0.0, 3.0, 0.1, ],
[ 4.5, -2.0, -0.8, ],
],
[
[ 1.5, 0.0, 1.0, ],
[ 0.0, 1.0, 0.1, ],
[ 0.9, -0.7, 1.1, ],
],
];
const morphCount3 = 80;
const geo3 = Curved2Geometry( design3, centerMorph3, morphCount3 );
let mesh3 = new THREE.Mesh( geo3, mat );
scene.add( mesh3 );
mesh3.position.x = -14;
mesh3.position.z = -4;
// .....................................................................
const designf1 = [
//max vert, str, radial, 0 first,.. , max angle - if '..s' 0°, 180° fixed
[ 16, 'ffs', 34, 0, 7, 150, 180, ],
// center x, y, z, distance to center orthogonal-radial
[ 0.0, 1.0, 0.0, 0.4, 0.4, 0.4, 0.9, ],
[ 0.0, 2.0, 0.0, 1.0, 0.8, 0.9, 2.0, ],
[ 0.0, 3.0, 0.0, 1.2, 1.1, 1.1, 2.7, ],
[ 0.0, 4.0, 0.0, 1.6, 1.3, 1.5, 3.3, ],
[ 0.0, 5.0, 0.0, 1.9, 1.5, 1.7, 3.8, ],
[ 0.0, 6.0, 0.0, 2.1, 1.7, 2.0, 4.1, ], // to some centers x,y,z
[ 0.0, 7.0, 0.0, 2.3, 1.8, 2.0, 4.3, ],
[ 0.0, 8.0, 0.0, 2.3, 1.8, 2.0, 4.3, ],
[ 0.0, 9.0, 0.0, 2.2, 1.8, 2.0, 4.0, ],
[ 0.0,10.0, 0.0, 2.1, 1.6, 1.8, 3.6, ],
[ 0.0,11.0, 0.0, 2.0, 1.4, 1.6, 3.3, ],
[ 0.0,12.0, 0.0, 1.8, 1.0, 1.2, 2.9, ],
[ 0.0,13.0, 0.0, 1.3, 0.8, 1.0, 2.5, ],
[ 0.0,14.0, 0.0, 0.6, 0.6, 0.7, 2.2, ],
[ 0.0,15.0, 0.0, 0.6, 0.3, 0.4, 2.1, ],
[ 0.0,16.0, 0.0, 0.7, 0.1, 0.2, 2.0, ],
];
const centerMorphf1 = [
// x, y, z difference to initial position
[
[ 0.0, 0.0, 0.0, ],
[ 0.0, 0.0, -0.1, ],
[ 0.0, 0.0, -0.1, ],
[ 0.0, 0.0, -0.1, ],
[ 0.0, 0.0, -0.2, ],
[ 0.0, 0.0, -0.2, ],
[0.03, 0.0, -0.2, ],
[0.03, 0.0, -0.3, ],
[0.03, 0.0, -0.3, ],
[0.03, 0.0, -0.2, ],
[0.03, 0.0, -0.1, ],
[ 0.0, 0.0, 0.1, ],
[ 0.0, 0.0, 0.2, ],
[ 0.0, 0.0, 0.3, ],
[ 0.1, 0.0, 0.5, ],
[ 0.2, 0.0, 0.7, ]
],
[
[ 0.0, 0.0, 0.0, ],
[ 0.0, 0.0, 0.1, ],
[ 0.0, 0.0, 0.1, ],
[ 0.0, 0.0, 0.1, ],
[ 0.0, 0.0, 0.2, ],
[ 0.0, -0.0, 0.2, ],
[ 0.0, 0.0, 0.2, ],
[ 0.0, 0.0, 0.3, ],
[ 0.0, 0.0, 0.3, ],
[ 0.0, 0.0, 0.2, ],
[ 0.0, 0.0, 0.1, ],
[ 0.0, 0.0, -0.1, ],
[ 0.0, 0.0, -0.2, ],
[ 0.0, -0.0, -0.3, ],
[-0.1, 0.0, -0.5, ],
[-0.2, 0.0, -0.7, ]
],
];
const morphCountf1 = 40;
const geof1 = Curved2Geometry( designf1, centerMorphf1, morphCountf1 );
let meshf1 = new THREE.Mesh( geof1, matf1 );
meshf1.rotation.z = Math. PI / 2;
meshf1.position.x = 8;
meshf1.position.z = -9;
scene.add(meshf1 );
// .....................................................................
const designf2 = [
//max vert, str,radial, 0 first,.., max angle - if '..s' 0°, 180° fixed
[ 60, 'ffs', 34, 0, 90, 180, ],
// center x, y, z, distance to center orthogonal-radial
[ 9.5, 0.0, 0.0, 0.2, 0.1, 0.2, ], // top, max vert. ( top x )
[ 9.0, 0.0, 0.0, 0.9, 0.4, 0.7, ],
[ 8.0, 0.0, 0.0, 1.2, 0.6, 1.2, ],
[ 7.0, 0.0, 0.0, 1.5, 0.8, 1.5, ],
[ 6.0, 0.0, 0.0, 1.7, 0.9, 1.8, ],
[ 5.0, 0.0, 0.0, 1.8, 1.0, 2.0, ],
[ 4.0, 0.0, 0.0, 1.9, 1.1, 2.15,],
[ 3.0, 0.0, 0.0, 2.0, 1.2, 2.2, ],
[ 2.0, 0.0, 0.0, 2.1, 1.2, 2.25,],
[ 1.0, 0.0, 0.0, 2.1, 1.3, 2.3, ],
[ 0.0, 0.0, 0.0, 2.1, 1.3, 2.2, ], // to some centers x,y,z
[ -1.0, 0.0, 0.0, 2.0, 1.2, 2.1, ],
[ -2.0, 0.0, 0.0, 1.9, 1.1, 2.0, ],
[ -3.0, 0.0, 0.1, 1.8, 1.0, 1.9, ],
[ -4.0, 0.0, 0.2, 1.6, 0.9, 1.8, ],
[ -5.0, 0.0, 0.3, 1.3, 0.8, 1.6, ],
[ -6.0, 0.0, 0.4, 1.1, 0.7, 1.4, ],
[ -7.0, 0.0, 0.5, 0.8, 0.6, 1.1, ],
[ -8.0, 0.0, 0.6, 0.6, 0.5, 0.8, ],
[ -9.0, 0.0, 0.7, 0.7, 0.4, 0.5, ],
[ -9.5, 0.0, 0.8, 0.8, 0.3, 0.3, ], // bottom
];
const centerMorphf2 = [];
for ( let i = 0; i < 4; i ++ ) {
centerMorphf2.push( [] );
for ( let j = 0; j < designf2.length - 1; j ++ ) {
centerMorphf2[ i ].push( [] );
const dy = 0.5 * Math.sin( 0.1 + i * j / 12 );
const dz = 0.1 * Math.cos( 0.1 - i + j / 3 );
centerMorphf2[ i ][ j ].push( 0, dy, dz );
}
}
const morphCountf2 = 80;
const geof2 = Curved2Geometry( designf2, centerMorphf2, morphCountf2 );
let meshf2 = new THREE.Mesh( geof2, matf2 );
scene.add(meshf2 );
let t = 0;
let t1 = 0;
animate( );
// .......................................................................................
function animate( ) {
requestAnimationFrame( animate );
geo.morph( geo.cPts[ Math.trunc( t ) % ( morphCount ) ] );
geo.attributes.position.needsUpdate = true;
geo3.morph( geo3.cPts[ Math.trunc( t ) % ( morphCount3 ) ] );
geo3.attributes.position.needsUpdate = true;
t += 0.1;
geof1.morph( geof1.cPts[ Math.trunc( t1 ) % ( morphCountf1 ) ] );
geof1.attributes.position.needsUpdate = true;
geof2.morph( geof2.cPts[ Math.trunc( t1 ) % ( morphCountf2 ) ] );
geof2.attributes.position.needsUpdate = true;
t1 += 0.9;
renderer.render( scene, camera );
}
// ...........................................................................................
function Curved2Geometry( design, centerMorph, morphCount ) { // optional: centerMorph, morphCount
const g = new THREE.BufferGeometry( );
const connected = design[ 0 ][ 1 ][2] === 'x' ? false : true;
const symmetric = design[ 0 ][ 1 ][2] === 's' ? true : false;
let dsgn = [];
if ( symmetric ) {
const len = design[ 0 ].length;
for ( let i = 0; i < design.length; i ++ ) {
dsgn.push( [] );
for ( let j = 0; j < len; j ++ ) {
dsgn[ i ].push( design[ i ][ j ] );
}
if ( i === 0 ) {
dsgn[ 0 ][ 3 ] = 0;
for ( let j = len - 2; j >= 4; j -- ) {
dsgn[ 0 ].push( 180 + 180 - design[ 0 ][ j ] );
}
dsgn[ 0 ][ len - 1 ] = 180;
} else {
for ( let j = len - 2; j >= 4; j -- ) {
dsgn[ i ].push( design[ i ][ j ] );
}
}
}
} else {
dsgn = design;
}
const rdefCount = dsgn[ 0 ].length - 3; // radial definition count
g.radial = dsgn[ 0 ][ 2 ]; // radial max
g.vertical = dsgn[ 0 ][ 0 ]; // vertical max
const wTop = dsgn[ 0 ][ 1 ][0] !== 'x' ? true : false;
const flatTop = dsgn[ 0 ][ 1 ][0] === 'f' ? true : false;
const wBtm = dsgn[ 0 ][ 1 ][1] !== 'x' ? true : false;
const flatBtm = dsgn[ 0 ][ 1 ][1] === 'f' ? true : false;
const angle = dsgn[ 0 ][ dsgn[ 0 ].length - 1 ];
const dAngle = angle / g.radial;
let angles = [ ]; // all radial angles in ° (g.radial + 1 many)
let phi = []; // closest angle to the design in radiant (rdefCount many)
for ( let j = 0; j <= g.radial; j ++ ) {
angles[ j ] = dAngle * j;
}
for ( let i = 0; i < rdefCount; i ++ ) { // angle adaptation
for ( let j = 0; j <= g.radial; j ++ ) {
if ( Math.abs( dsgn[ 0 ][ i + 3 ] - angles[ j ] ) < dAngle / 2 ) {
phi.push( angles[ j ] * Math.PI / 180 ); // with degree to radiant
}
}
}
g.morphCount = morphCount;
g.morph = function( pts ) {
// tangent( direction), normal, binormal, shape in space
let v3a = new THREE.Vector3( );
let v3b = new THREE.Vector3( );
let tangent = new THREE.Vector3( );
let normal = new THREE.Vector3( 0, 0, -1 ); // first normal to after ...
let binormal = new THREE.Vector3( );
let idx = 0;
for( let i = 0; i <= g.vertical; i ++ ) {
if ( i === 0 ) tangent.subVectors( pts[ 1 ], pts[ 0 ] );
if ( i > 0 && i < g.vertical ) tangent.subVectors( pts[ i + 1 ], pts[ i - 1 ] );
if ( i === g.vertical ) tangent.subVectors( pts[ i ], pts[ i - 1 ] );
binormal.crossVectors( normal, tangent );
normal.crossVectors( tangent, binormal );
binormal.normalize( );
normal.normalize( );
for( let j = 0; j <= g.radial; j ++ ) {
v3a.addVectors( binormal.clone( ).multiplyScalar( g.pts2D[ i ][ j ].x ), normal.clone( ).multiplyScalar( g.pts2D[ i ][ j ].z ) );
v3b.addVectors( pts[ i ], v3a );
g.attributes.position.setXYZ( idx ++, v3b.x, v3b.y, v3b.z );
}
}
idx --; // idx = ( g.radial + 1 ) * ( g.vertical + 1 ) - 1; // last index torso
if( wTop ) {
let x, y, z;
g.attributes.position.setXYZ( ++ idx, pts[ 0 ].x, pts[ 0 ].y, pts[ 0 ].z ); // center top
for( let j = 0; j <= g.radial ; j ++ ) {
x = g.attributes.position.getX( j );
y = g.attributes.position.getY( j );
z = g.attributes.position.getZ( j );
g.attributes.position.setXYZ( ++ idx, x, y, z );
}
}
if( wBtm ) {
let x, y, z, idxBtm;
g.attributes.position.setXYZ( ++ idx, pts[ g.vertical ].x, pts[ g.vertical ].y, pts[ g.vertical ].z ); // center bottom
for( let j = 0; j <= g.radial ; j ++ ) {
idxBtm = ( g.radial + 1 ) * ( g.vertical + 1 ) - 1 - g.radial + j; // last index torso - g.radial + j
x = g.attributes.position.getX( idxBtm );
y = g.attributes.position.getY( idxBtm);
z = g.attributes.position.getZ( idxBtm );
g.attributes.position.setXYZ( ++ idx, x, y, z );
}
}
g.attributes.position.needsUpdate = true;
g.computeVertexNormals( );
}
g.dsgnCenters = [];
for ( let i = 1; i < dsgn.length; i ++ ) {
g.dsgnCenters.push( new THREE.Vector3( dsgn[ i ][ 0 ], dsgn[ i ][ 1 ], dsgn[ i ][ 2 ] ) );
}
g.cPoints = new THREE.CatmullRomCurve3( g.dsgnCenters, false ).getSpacedPoints( g.vertical );
if ( centerMorph !== undefined ) {
g.centerMrph = []; // vectors (center morph) to create curve
g.cMorph = []; // morphCount many points of the curve (for each defined center)
g.cPts = []; // array of array of morphed g.cPoints
for ( let i = 0; i < dsgn.length - 1; i ++ ) {
g.centerMrph.push( [] );
g.centerMrph[ i ].push( g.dsgnCenters[ i ] ); // Vector3, from design array
for ( let j = 0; j < centerMorph.length; j ++ ) {
const x = dsgn[ i + 1 ][ 0 ] + centerMorph[ j ][ i ][ 0 ];
const y = dsgn[ i + 1 ][ 1 ] + centerMorph[ j ][ i ][ 1 ];
const z = dsgn[ i + 1 ][ 2 ] + centerMorph[ j ][ i ][ 2 ];
g.centerMrph[ i ].push( new THREE.Vector3( x, y, z ) );
}
g.cMorph.push( new THREE.CatmullRomCurve3( g.centerMrph[ i ], true ).getSpacedPoints( g.morphCount ) );
}
let cMrph; // points resorted to center lines
for ( let i = 0; i < g.morphCount; i ++ ) {
cMrph = [];
for ( let j = 0; j < dsgn.length - 1; j ++ ) {
cMrph.push( g.cMorph[ j ][ i ] )
}
g.cPts.push( [] );
g.cPts[ i ] = new THREE.CatmullRomCurve3( cMrph, false ).getSpacedPoints( g.vertical );
}
}
let dist2; // distanceToSquared
let idxCp = [ ]; // indices of nearest center points (design, calculated)
for ( let i = 0; i < g.dsgnCenters.length; i ++ ) {
dist2 = Infinity;
for ( let j = 0; j < g.cPoints.length; j ++ ) {
const d = g.dsgnCenters[ i ].distanceToSquared( g.cPoints[ j ] );
if ( d < dist2 ) {
dist2 = d;
idxCp[ i ] = j;
}
}
}
let v3 = new THREE.Vector3( );
let cLen;
let arg2 = [];
arg2[ 0 ] = 0;
for( let i = 0; i < idxCp.length - 1; i ++ ) {
cLen = 0;
for( let j = idxCp[ i ]; j < idxCp[ i + 1 ]; j ++ ) {
v3.subVectors( g.cPoints[ j + 1 ], g.cPoints[ j ] );
cLen += v3.length( );
}
arg2[ i + 1 ] = arg2[ i ] + cLen;
}
/*
let pr2 = []; // points radial, vector 2
for ( let j = 0; j < rdefCount; j ++ ) {
let V2 = [ ];
for ( let i = 0; i < g.dsgnCenters.length; i ++ ) {
V2.push( new THREE.Vector2( arg2[ i ], dsgn[ i + 1 ][ j + 3 ] ) );
}
pr2.push( new THREE.SplineCurve( V2 ).getPoints( g.vertical ) );
}
*/
let pr3 = []; // points radial, vector 3, z = 0
for ( let j = 0; j < rdefCount; j ++ ) {
let V3a = [ ];
for ( let i = 0; i < g.dsgnCenters.length; i ++ ) {
V3a.push( new THREE.Vector3( arg2[ i ], dsgn[ i + 1 ][ j + 3 ], 0 ) );
}
pr3.push( new THREE.CatmullRomCurve3( V3a ).getPoints( g.vertical ) );
}
g.pts2D = []; // all points orthogonal cut, vector 3 in plane, y is 0
let V3 = [];
for ( let i = 0; i <= g.vertical; i ++ ) {
V3 = [];
for ( let j = 0 ; j < rdefCount; j ++ ) {
//V3.push( new THREE.Vector3( -Math.cos( phi[ j ] ) * pr2[ j ][ i ].y, 0, Math.sin( phi[ j ] ) * pr2[ j ][ i ].y ) );
V3.push( new THREE.Vector3( -Math.cos( phi[ j ] ) * pr3[ j ][ i ].y, 0, Math.sin( phi[ j ] ) * pr3[ j ][ i ].y ) );
}
g.pts2D.push( new THREE.CatmullRomCurve3( V3, connected ).getSpacedPoints( g.radial ) );
}
BasicGeometry( g.radial, g.vertical, wTop, wBtm ); // create a rudimentary geometry
let idx = ( g.radial + 1 ) * ( g.vertical + 1 ) - 1; // // last index torso
if( wTop ) {
let zMax = -Infinity;
let xMax = -Infinity;
for ( let j = 0; j <= g.radial; j ++ ) {
zMax = Math.abs( g.pts2D[ 0 ][ j ].z ) > zMax ? Math.abs( g.pts2D[ 0 ][ j ].z ) : zMax;
xMax = Math.abs( g.pts2D[ 0 ][ j ].x ) > xMax ? Math.abs( g.pts2D[ 0 ][ j ].x ) : xMax;
}
idx ++; // center uv in BasicGeometry
for( let j = 0; j <= g.radial ; j ++ ) {
g.attributes.uv.setXY( ++ idx, 0.5 - g.pts2D[ 0 ][ j ].z / zMax / 2, 0.5 - g.pts2D[ 0 ][ j ].x / xMax / 2 );
}
}
if( wBtm ) {
let zMax = -Infinity;
let xMax = -Infinity;
for ( let j = 0; j <= g.radial; j ++ ) {
zMax = Math.abs( g.pts2D[ g.vertical ][ j ].z ) > zMax ? Math.abs( g.pts2D[ g.vertical ][ j ].z ) : zMax;
xMax = Math.abs( g.pts2D[ g.vertical ][ j ].x ) > xMax ? Math.abs( g.pts2D[ g.vertical ][ j ].x ) : xMax;
}
idx ++; // center uv in BasicGeometry
for( let j = 0; j <= g.radial ; j ++ ) {
g.attributes.uv.setXY( ++ idx, 0.5 + g.pts2D[ g.vertical ][ j ].z / zMax / 2, 0.5 - g.pts2D[ g.vertical ][ j ].x / xMax / 2 );
}
}
g.morph( g.cPoints ); // initial morph of BasicGeometry
g.computeVertexNormals( );
if ( connected ) { // calculate new normals at mantle seam
for( let i = 0; i <= g.vertical; i ++ ) {
smoothEdge( ( g.radial + 1 ) * i, ( g.radial + 1 ) * i + g.radial );
}
}
if ( wTop && !flatTop ) { // calculate new normals at top seam
for( let j = 0; j <= g.radial; j ++ ) {
smoothEdge( ( g.radial + 1 ) * ( g.vertical + 1 ) + 1 + j, j );
}
}
if ( wBtm && !flatBtm ) { // calculate new normals at bottom seam
for( let j = 0; j <= g.radial ; j ++ ) {
const offs = wTop ? + g.radial + 2 : 0;
smoothEdge( ( g.radial + 1 ) * g.vertical + j, ( g.radial + 1 ) * ( g.vertical + 1 ) + offs + 1 + j );
}
}
g.attributes.normal.needsUpdate = true;
return g;
// ............................................................................
function BasicGeometry( radialSegments, heightSegments, withTop, withBottom ) {
let indices = [];
let uvs = [];
let index = 0;
let indexArray = [];
let groupStart = 0;
let groupCount = 0;
for ( let y = 0; y <= heightSegments; y ++ ) {
let indexRow = [];
let v = y / heightSegments;
if ( symmetric ) { // texture mirror image
for ( let x = 0; x <= radialSegments / 2; x ++ ) {
uvs.push( x / ( radialSegments / 2 ), 1 - v );
indexRow.push( index ++ );
}
for ( let x = radialSegments / 2 + 1 ; x <= radialSegments; x ++ ) {
uvs.push( ( radialSegments - x ) / ( radialSegments / 2 ), 1 - v );
indexRow.push( index ++ );
}
} else { // only one texture
for ( let x = 0; x <= radialSegments; x ++ ) {
uvs.push( x / radialSegments, 1 - v );
indexRow.push( index ++ );
}
}
indexArray.push( indexRow );
}
let a, b, c, d;
for ( let i = 0; i < radialSegments; i ++ ) {
for ( let j = 0; j < heightSegments; j ++ ) {
a = indexArray[ j ][ i ];
b = indexArray[ j + 1 ] [ i ];
c = indexArray[ j + 1 ][ i + 1 ];
d = indexArray[ j ] [ i + 1 ];
indices.push( a, b, d );
indices.push( b, c, d );
groupCount += 6;
}
}
g.addGroup( groupStart, groupCount, 0 );
groupStart += groupCount;
let verticesCount = ( radialSegments + 1 ) * ( heightSegments + 1 )
if ( wTop ) generateCap( true );
if ( wBtm ) generateCap( false );
g.setIndex( new THREE.BufferAttribute( new Uint32Array( indices ), 1 ) );
g.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( verticesCount * 3 ), 3 ) );
g.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
function generateCap( top ) {
let groupCount = 0;
uvs.push( 0.5, 0.5 );
const centerIndex = index;
for ( let x = 0; x <= radialSegments; x ++ ) {
uvs.push( 0, 0 );
index ++;
}
index ++;
for ( let x = 1; x <= radialSegments; x ++ ) {
const c = centerIndex;
const i = centerIndex + x;
if ( top ) {
indices.push( i, i + 1, c ); // face top
} else {
indices.push( i + 1, i, c ); // face bottom
}
groupCount += 3;
}
g.addGroup( groupStart, groupCount, top ? 1 : 2 );
groupStart += groupCount;
verticesCount += radialSegments + 2; // with center
}
}
function smoothEdge( idxa, idxb ) {
let v3a = new THREE.Vector3( );
let v3b = new THREE.Vector3( );
v3a.set( g.attributes.normal.getX( idxa ), g.attributes.normal.getY( idxa ), g.attributes.normal.getZ( idxa ) );
v3b.set( g.attributes.normal.getX( idxb ), g.attributes.normal.getY( idxb ), g.attributes.normal.getZ( idxb ) );
v3.addVectors( v3a, v3b ).normalize( );
g.attributes.normal.setXYZ( idxa, v3.x, v3.y, v3.z );
g.attributes.normal.setXYZ( idxb, v3.x, v3.y, v3.z );
}
}
</script>
</html>