# Round-edged box 3

This is the last approach of how to create a round-edged box, as I’m finally out of ideas.

Six transformed buffer indexed planes, merged in one single buffer indexed geometry.

The advantage of this approach is that we can apply a material to each side. Mostly it’s based on how they build a box geometry.

https://jsfiddle.net/prisoner849/1fhu48mj/

Criticism is very welcomed

``````  function RoundEdgedBox(w, h, d, r, wSegs, hSegs, dSegs, rSegs) {

w = w || 1;
h = h || 1;
d = d || 1;
let minimum = Math.min(Math.min(w, h), d);
r = r || minimum * .25;
r = r > minimum * .5 ? minimum * .5 : r;
wSegs = Math.floor(wSegs) || 1;
hSegs = Math.floor(hSegs) || 1;
dSegs = Math.floor(dSegs) || 1;
rSegs = Math.floor(rSegs) || 1;

let fullGeometry = new THREE.BufferGeometry();

let fullPosition = [];
let fullUvs = [];
let fullIndex = [];
let fullIndexStart = 0;

let groupStart = 0;

bendedPlane(w, h, r, wSegs, hSegs, rSegs, d * .5, 'y', 0, 0);
bendedPlane(w, h, r, wSegs, hSegs, rSegs, d * .5, 'y', Math.PI, 1);
bendedPlane(d, h, r, dSegs, hSegs, rSegs, w * .5, 'y', Math.PI * .5, 2);
bendedPlane(d, h, r, dSegs, hSegs, rSegs, w * .5, 'y', Math.PI * -.5, 3);
bendedPlane(w, d, r, wSegs, dSegs, rSegs, h * .5, 'x', Math.PI * -.5, 4);
bendedPlane(w, d, r, wSegs, dSegs, rSegs, h * .5, 'x', Math.PI * .5, 5);

fullGeometry.setIndex(fullIndex);

fullGeometry.computeVertexNormals();

return fullGeometry;

function bendedPlane(width, height, radius, widthSegments, heightSegments, smoothness, offset, axis, angle, materialIndex) {

let halfWidth = width * .5;
let halfHeight = height * .5;
let widthChunk = width / (widthSegments + smoothness * 2);
let heightChunk = height / (heightSegments + smoothness * 2);

let planeGeom = new THREE.PlaneBufferGeometry(width, height, widthSegments + smoothness * 2, heightSegments + smoothness * 2);

let v = new THREE.Vector3(); // current vertex
let cv = new THREE.Vector3(); // control vertex for bending
let cd = new THREE.Vector3(); // vector for distance
let position = planeGeom.attributes.position;
let uv = planeGeom.attributes.uv;
let widthShrinkLimit = widthChunk * smoothness;
let widthShrinkRatio = radius / widthShrinkLimit;
let heightShrinkLimit = heightChunk * smoothness;
let heightShrinkRatio = radius / heightShrinkLimit;
let widthInflateRatio = (halfWidth - radius) / (halfWidth - widthShrinkLimit);
let heightInflateRatio = (halfHeight - radius) / (halfHeight - heightShrinkLimit);
for (let i = 0; i < position.count; i++) {
v.fromBufferAttribute(position, i);
if (Math.abs(v.x) >= halfWidth - widthShrinkLimit) {
v.setX((halfWidth - (halfWidth - Math.abs(v.x)) * widthShrinkRatio) * Math.sign(v.x));
} else {
v.x *= widthInflateRatio;
}// lr
if (Math.abs(v.y) >= halfHeight - heightShrinkLimit) {
v.setY((halfHeight - (halfHeight - Math.abs(v.y)) * heightShrinkRatio) * Math.sign(v.y));
} else {
v.y *= heightInflateRatio;
}// tb

//re-calculation of uvs
uv.setXY(
i,
(v.x - (-halfWidth)) / width,
1 - (halfHeight - v.y) / height
);

// bending
let widthExceeds = Math.abs(v.x) >= halfWidth - radius;
let heightExceeds = Math.abs(v.y) >= halfHeight - radius;
if (widthExceeds || heightExceeds) {
cv.set(
widthExceeds ? (halfWidth - radius) * Math.sign(v.x) : v.x,
cd.subVectors(v, cv).normalize();
};

position.setXYZ(i, v.x, v.y, v.z);
}

planeGeom.translate(0, 0, offset);
switch (axis) {
case 'y':
planeGeom.rotateY(angle);
break;
case 'x':
planeGeom.rotateX(angle);
}

// merge positions
position.array.forEach(function(p){
fullPosition.push(p);
});

// merge uvs
uv.array.forEach(function(u){
fullUvs.push(u);
});

// merge indices
planeGeom.index.array.forEach(function(a) {
fullIndex.push(a + fullIndexStart);
});
fullIndexStart += position.count;

// set the groups
groupStart += planeGeom.index.count;
}
}``````
4 Likes

Again super and fast.

Just a little letter twister in the fiddle.
`.. MeshBasicMaterial({colro:"purple"..`

And if you exaggerate, there is no error message. But an interesting structure. That’s what I like.

``````var width = 5;
var height = 3;
var depth = 2;
var widthSegments = 2;
var heightSegments = 2;
var depthSegments = 1;
var smoothness = 20;
``````

Yeah, overlooked )) Thanks!

That’s funny. I have to investigate this

Finally, I got what’s going on
Radius can’t be greater than a half of length of the minimal side.

I don’t understand the need for this solution… Doesn’t the Subdivision modifier allow you to create rounded boxes/shapes?

Does it allow you to control the radius?

Errr… no it doesn’t. Interesting.

Here’s another interesting approach:
https://gam0022.net/webgl/#misc_superellipsoid

1 Like

Thanks for the link. The approach is really interesting.

Hello @prisoner849, which is your preferred approach of the 3 for most cases, and why?

Hi, @trusktr
I’d use first and third approaches. The first one because it’s short and simple. The third one because it supports groups for materials

You can but you’d need to make a different box with additional edge loops, also it wouldn’t be perfect.

1 Like