three-rounded-box
Version:
Box geometry for three.js with filleted edges
555 lines (370 loc) • 11.4 kB
JavaScript
/**
* @author pailhead / http://dusanbosnjak.com
*/
module.exports = function( THREE ){
RoundedBoxGeometry = function (
width ,
height ,
depth ,
radius,
radiusSegments
) {
THREE.BufferGeometry.call( this );
this.type = 'RoundedBoxGeometry';
//validate params ===================================
radiusSegments = !isNaN( radiusSegments ) ? Math.max( 1, Math.floor( radiusSegments ) ) : 1 ;
width = !isNaN(width) ? width : 1;
height = !isNaN(height) ? height : 1;
depth = !isNaN(depth) ? depth : 1;
radius = !isNaN(radius) ? radius : .15;
radius = Math.min( radius , Math.min( width , Math.min( height , Math.min( depth ) ) ) / 2 );
var edgeHalfWidth = width / 2 - radius;
var edgeHalfHeight = height / 2 - radius;
var edgeHalfDepth = depth / 2 - radius;
//not sure why this is needed, for querying? ========
this.parameters = {
width: width ,
height: height ,
depth: depth ,
radius: radius,
radiusSegments: radiusSegments
};
//calculate vertices count ==========================
var rs1 = radiusSegments + 1; //radius segments + 1
var totalVertexCount = ( rs1 * radiusSegments + 1 ) << 3;
//make buffers ======================================
var positions = new THREE.BufferAttribute( new Float32Array( totalVertexCount * 3 ), 3 );
var normals = new THREE.BufferAttribute( new Float32Array( totalVertexCount * 3 ), 3 );
//some vars =========================================
var
cornerVerts = [],
cornerNormals = [],
normal = new THREE.Vector3(),
vertex = new THREE.Vector3(),
vertexPool = [],
normalPool = [],
indices = []
;
var
lastVertex = rs1 * radiusSegments,
cornerVertNumber = rs1 * radiusSegments + 1
;
doVertices();
doFaces();
doCorners();
doHeightEdges();
doWidthEdges();
doDepthEdges()
// calculate vert positions =========================
function doVertices(){
//corner offsets
var cornerLayout = [
new THREE.Vector3( 1 , 1 , 1 ),
new THREE.Vector3( 1 , 1 , -1 ),
new THREE.Vector3( -1 , 1 , -1 ),
new THREE.Vector3( -1 , 1 , 1 ),
new THREE.Vector3( 1 , -1 , 1 ),
new THREE.Vector3( 1 , -1 , -1 ),
new THREE.Vector3( -1 , -1 , -1 ),
new THREE.Vector3( -1 , -1 , 1 )
];
//corner holder
for ( var j = 0 ; j < 8 ; j ++ ){
cornerVerts.push([]);
cornerNormals.push([]);
}
//construct 1/8 sphere ==============================
var PIhalf = Math.PI / 2;
var cornerOffset = new THREE.Vector3( edgeHalfWidth , edgeHalfHeight , edgeHalfDepth );
for ( var y = 0; y <= radiusSegments; y ++ ) {
var v = y / radiusSegments;
var va = v * PIhalf; //arrange in 90 deg
var cosVa = Math.cos( va ); //scale of vertical angle
var sinVa = Math.sin( va );
if( y == radiusSegments ){
vertex.set( 0 , 1 , 0 );
var vert = vertex.clone().multiplyScalar( radius ).add( cornerOffset );
cornerVerts[0].push( vert );
vertexPool.push( vert );
var norm = vertex.clone();
cornerNormals[0].push( norm );
normalPool.push( norm );
continue; //skip row loop
}
for ( var x = 0; x <= radiusSegments; x ++ ) {
var u = x / radiusSegments;
var ha = u * PIhalf;
//make 1/8 sphere points
vertex.x = cosVa * Math.cos( ha );
vertex.y = sinVa;
vertex.z = cosVa * Math.sin( ha );
//copy sphere point, scale by radius, offset by half whd
var vert = vertex.clone().multiplyScalar( radius ).add( cornerOffset );
cornerVerts[0].push( vert );
vertexPool.push( vert );
//sphere already normalized, just clone
var norm = vertex.clone().normalize();
cornerNormals[0].push( norm );
normalPool.push( norm );
}
}
//distribute corner verts ===========================
for ( var i = 1 ; i < 8 ; i ++ ){
for( var j = 0 ; j < cornerVerts[0].length ; j ++ ){
var vert = cornerVerts[0][j].clone().multiply( cornerLayout[i] );
cornerVerts[i].push( vert );
vertexPool.push( vert );
var norm = cornerNormals[0][j].clone().multiply( cornerLayout[i] );
cornerNormals[i].push( norm );
normalPool.push( norm );
}
}
}
// weave corners ====================================
function doCorners(){
var indexInd = 0;
var flips = [
true,
false,
true,
false,
false,
true,
false,
true
];
var lastRowOffset = rs1 * ( radiusSegments - 1 );
for ( var i = 0 ; i < 8 ; i ++ ){
var cornerOffset = cornerVertNumber * i;
for ( var v = 0 ; v < radiusSegments - 1 ; v ++ ){
var r1 = v * rs1; //row offset
var r2 = (v + 1) * rs1; //next row
for ( var u = 0 ; u < radiusSegments ; u ++ ){
var u1 = u + 1;
var a = cornerOffset + r1 + u;
var b = cornerOffset + r1 + u1;
var c = cornerOffset + r2 + u;
var d = cornerOffset + r2 + u1;
if( !flips[i] ){
indices.push( a );
indices.push( b );
indices.push( c );
indices.push( b );
indices.push( d );
indices.push( c );
} else {
indices.push( a );
indices.push( c );
indices.push( b );
indices.push( b );
indices.push( c );
indices.push( d );
}
}
}
for ( var u = 0 ; u < radiusSegments ; u ++ ){
var a = cornerOffset + lastRowOffset + u;
var b = cornerOffset + lastRowOffset + u + 1;
var c = cornerOffset + lastVertex;
if( !flips[i] ){
indices.push( a );
indices.push( b );
indices.push( c );
} else {
indices.push( a );
indices.push( c );
indices.push( b );
}
}
}
}
//plates ============================================
//fix this loop matrices find pattern something
function doFaces(){
//top
var a = lastVertex;// + cornerVertNumber * 0;
var b = lastVertex + cornerVertNumber;// * 1;
var c = lastVertex + cornerVertNumber * 2;
var d = lastVertex + cornerVertNumber * 3;
indices.push( a );
indices.push( b );
indices.push( c );
indices.push( a );
indices.push( c );
indices.push( d );
//bottom
a = lastVertex + cornerVertNumber * 4;// + cornerVertNumber * 0;
b = lastVertex + cornerVertNumber * 5;// * 1;
c = lastVertex + cornerVertNumber * 6;
d = lastVertex + cornerVertNumber * 7;
indices.push( a );
indices.push( c );
indices.push( b );
indices.push( a );
indices.push( d );
indices.push( c );
//left
a = 0;
b = cornerVertNumber;
c = cornerVertNumber * 4;
d = cornerVertNumber * 5;
indices.push( a );
indices.push( c );
indices.push( b );
indices.push( b );
indices.push( c );
indices.push( d );
//right
a = cornerVertNumber * 2;
b = cornerVertNumber * 3;
c = cornerVertNumber * 6;
d = cornerVertNumber * 7;
indices.push( a );
indices.push( c );
indices.push( b );
indices.push( b );
indices.push( c );
indices.push( d );
//front
a = radiusSegments;
b = radiusSegments + cornerVertNumber * 3;
c = radiusSegments + cornerVertNumber * 4;
d = radiusSegments + cornerVertNumber * 7;
indices.push( a );
indices.push( b );
indices.push( c );
indices.push( b );
indices.push( d );
indices.push( c );
//back
a = radiusSegments + cornerVertNumber;
b = radiusSegments + cornerVertNumber * 2;
c = radiusSegments + cornerVertNumber * 5;
d = radiusSegments + cornerVertNumber * 6;
indices.push( a );
indices.push( c );
indices.push( b );
indices.push( b );
indices.push( c );
indices.push( d );
}
// weave edges ======================================
function doHeightEdges(){
for ( var i = 0 ; i < 4 ; i ++ ){
var cOffset = i * cornerVertNumber;
var cRowOffset = 4 * cornerVertNumber + cOffset;
var needsFlip = i & 1 === 1;
for ( var u = 0 ; u < radiusSegments ; u ++ ){
var u1 = u + 1;
var a = cOffset + u;
var b = cOffset + u1;
var c = cRowOffset + u;
var d = cRowOffset + u1;
if( !needsFlip ){
indices.push( a );
indices.push( b );
indices.push( c );
indices.push( b );
indices.push( d );
indices.push( c );
} else {
indices.push( a );
indices.push( c );
indices.push( b );
indices.push( b );
indices.push( c );
indices.push( d );
}
}
}
}
function doDepthEdges(){
var cStarts = [ 0 , 2 , 4 , 6 ];
var cEnds = [ 1 , 3 , 5 , 7 ];
for ( var i = 0 ; i < 4 ; i ++ ){
var cStart = cornerVertNumber * cStarts[ i ];
var cEnd = cornerVertNumber * cEnds[ i ];
var needsFlip = 1 >= i;
for ( var u = 0 ; u < radiusSegments ; u ++ ){
var urs1 = u * rs1;
var u1rs1 = (u+1) * rs1;
var a = cStart + urs1;
var b = cStart + u1rs1;
var c = cEnd + urs1;
var d = cEnd + u1rs1
if( needsFlip ){
indices.push( a );
indices.push( c );
indices.push( b );
indices.push( b );
indices.push( c );
indices.push( d );
} else {
indices.push( a );
indices.push( b );
indices.push( c );
indices.push( b );
indices.push( d );
indices.push( c );
}
}
}
}
function doWidthEdges(){
var end = radiusSegments - 1;
var cStarts = [ 0 , 1 , 4 , 5 ];
var cEnds = [ 3 , 2 , 7 , 6 ];
var needsFlip = [0,1,1,0];
for ( var i = 0 ; i < 4 ; i ++ ){
var cStart = cStarts[i] * cornerVertNumber;
var cEnd = cEnds[i] * cornerVertNumber;
for ( var u = 0 ; u <= end ; u ++ ){
// var dInd = u != end ? radiusSegments + u * rs1 : cornerVertNumber - 1;
var a = cStart + radiusSegments + u * rs1;
var b = cStart + (u != end ? radiusSegments + (u + 1) * rs1 : cornerVertNumber - 1);
var c = cEnd + radiusSegments + u * rs1;
var d = cEnd + (u != end ? radiusSegments + (u + 1) * rs1 : cornerVertNumber - 1);
if( !needsFlip[i] ){
indices.push( a );
indices.push( b );
indices.push( c );
indices.push( b );
indices.push( d );
indices.push( c );
}
else {
indices.push( a );
indices.push( c );
indices.push( b );
indices.push( b );
indices.push( c );
indices.push( d );
}
}
}
}
//fill buffers ======================================
var index = 0;
for ( var i = 0 ; i < vertexPool.length ; i ++ ){
positions.setXYZ(
index ,
vertexPool[i].x ,
vertexPool[i].y ,
vertexPool[i].z
);
normals.setXYZ(
index ,
normalPool[i].x ,
normalPool[i].y ,
normalPool[i].z
);
index++;
}
this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ) , 1 ) );
this.addAttribute( 'position', positions );
this.addAttribute( 'normal', normals );
};
RoundedBoxGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
RoundedBoxGeometry.constructor = RoundedBoxGeometry;
return RoundedBoxGeometry;
}