Skip to content

Instantly share code, notes, and snippets.

@killroy42
Created August 15, 2016 15:56
Show Gist options
  • Save killroy42/79d7db820a354a97d6674e7e061adcb7 to your computer and use it in GitHub Desktop.
Save killroy42/79d7db820a354a97d6674e7e061adcb7 to your computer and use it in GitHub Desktop.
function extrudeProfileOnPath(profileVs, outlineVs, sharedUVs) {
/*
TODO:
- if sharedUVs == false, create multiple UV layers
- Smooth according to edge angles
- Accept Shapes/CurvePaths directly
- Don't scale UVs along profile. Reuse reference profile
*/
var upVector = new THREE.Vector3(0, 1, 0);
var backVector = new THREE.Vector3(0, 0, -1);
var outlineCount = outlineVs.length;
var profileCount = profileVs.length;
var profileVertices, verticeIndices, outlineLengths, profileLengths, uvs;
var geo = new THREE.Geometry();
var uvLayer = 0;
function capFront() {
var i, a, b, c = geo.vertices.length;
// Calculate bounds
var bounds = new THREE.Box3();
for(i = 0; i < outlineCount; i++) {
a = (i + 1) * profileCount - 1;
bounds.expandByPoint(geo.vertices[a]);
}
geo.vertices.push(bounds.center());
// Faces and UVs
var size = bounds.size();
var center = bounds.center();
var uvOffset = new THREE.Vector2(0.5, 0.5);
var centerOffset = new THREE.Vector2(center.x, - center.y);
var uvScale = new THREE.Vector2(1 / size.x, 1 / size.y);
if(sharedUVs) {
uvScale.multiplyScalar(0.5);
uvOffset.add(new THREE.Vector2(-0.25, 0.25));
}
for(i = 0; i < outlineCount; i++) {
a = (i + 1) * profileCount - 1;
b = (((i + 1) % outlineCount) + 1) * profileCount - 1;
geo.faces.push(new THREE.Face3(a, c, b));
geo.faceVertexUvs[uvLayer][geo.faces.length - 1] = [
geo.vertices[a].clone().add(centerOffset).multiply(uvScale).add(uvOffset),
geo.vertices[c].clone().add(centerOffset).multiply(uvScale).add(uvOffset),
geo.vertices[b].clone().add(centerOffset).multiply(uvScale).add(uvOffset)
];
}
}
function capBack() {
var i, a, b, c = geo.vertices.length;
// Calculate bounds
var bounds = new THREE.Box3();
for(i = 0; i < outlineCount; i++) {
a = i * profileCount;
bounds.expandByPoint(geo.vertices[a]);
}
geo.vertices.push(bounds.center());
// Faces and UVs
var size = bounds.size();
var center = bounds.center();
var uvOffset = new THREE.Vector2(0.5, 0.5);
var centerOffset = new THREE.Vector2(center.x, - center.y);
var uvScale = new THREE.Vector2(-1 / size.x, 1 / size.y);
if(sharedUVs) {
uvScale.multiplyScalar(0.5);
uvOffset.add(new THREE.Vector2(0.25, 0.25));
}
for(i = 0; i < outlineCount; i++) {
a = i * profileCount;
b = ((i + 1) % outlineCount) * profileCount;
geo.faces.push(new THREE.Face3(a, b, c));
geo.faceVertexUvs[uvLayer][geo.faces.length - 1] = [
geo.vertices[a].clone().add(centerOffset).multiply(uvScale).add(uvOffset),
geo.vertices[b].clone().add(centerOffset).multiply(uvScale).add(uvOffset),
geo.vertices[c].clone().add(centerOffset).multiply(uvScale).add(uvOffset)
];
}
}
function computeOutlineLengths() {
outlineLengths = [];
for(var pIdx = 0; pIdx < profileCount; pIdx++) {
outlineLengths[pIdx] = 0;
for(var oIdx = 0; oIdx < outlineCount; oIdx++) {
var dist = profileVertices[oIdx][pIdx]
.distanceTo(profileVertices[(oIdx+1) % outlineCount][pIdx]);
outlineLengths[pIdx] += dist;
}
}
}
function computeProfileLengths() {
profileLengths = [];
for(var oIdx = 0; oIdx < outlineCount; oIdx++) {
profileLengths[oIdx] = 0;
for(var pIdx = 0; pIdx < profileCount-1; pIdx++) {
var dist = profileVertices[oIdx][pIdx]
.distanceTo(profileVertices[oIdx][pIdx+1]);
profileLengths[oIdx] += dist;
}
}
}
function createProfileVertices() {
var oIdx, pIdx, vertex, v0, v1, v2, va, vb, vab, scaleV, ang, a, b, c;
profileVertices = [];
verticeIndices = [];
for(oIdx = 0; oIdx < outlineCount; oIdx++) {
a = (oIdx + outlineCount - 1) % outlineCount;
b = oIdx;
c = (oIdx + 1) % outlineCount;
v0 = outlineVs[a].clone(); v0.z = 0;
v1 = outlineVs[b].clone(); v1.z = 0;
v2 = outlineVs[c].clone(); v2.z = 0;
va = v1.clone().sub(v0).normalize();
vb = v2.clone().sub(v1).normalize();
vab = va.clone().add(vb).normalize();
vab.set(-vab.y, vab.x, vab.z); // Rotate by 90 degrees clockwise
ang = upVector.angleTo(vab); if(vab.x < 0) ang = -ang;
scaleV = new THREE.Vector3(1, 1 / Math.sin(vab.angleTo(va)), 1);
profileVertices[oIdx] = [];
verticeIndices[oIdx] = [];
for(pIdx = 0; pIdx < profileCount; pIdx++) {
vertex = profileVs[profileCount - pIdx - 1].clone()
.multiply(scaleV)
.applyAxisAngle(backVector, ang)
.add(outlineVs[oIdx]);
profileVertices[oIdx][pIdx] = vertex;
verticeIndices[oIdx][pIdx] = geo.vertices.push(vertex) - 1;
}
}
}
function computeUVs() {
var oIdx, pIdx, uvA, uvB, uvC, uvD, a, b, c, d;
uvs = [];
for(oIdx = 0; oIdx <= outlineCount; oIdx++) {
uvs[oIdx] = [];
for(pIdx = 0; pIdx < profileCount; pIdx++) {
uvs[oIdx][pIdx] = new THREE.Vector2(0, 0);
}
}
for(oIdx = 0; oIdx < outlineCount; oIdx++) {
for(pIdx = 0; pIdx < profileCount-1; pIdx++) {
uvA = uvs[(oIdx + 0)][(pIdx + 0)];
uvB = uvs[(oIdx + 1)][(pIdx + 0)];
uvC = uvs[(oIdx + 1)][(pIdx + 1)];
uvD = uvs[(oIdx + 0)][(pIdx + 1)];
a = verticeIndices[(oIdx + 0) % outlineCount][(pIdx + 0) % profileCount];
b = verticeIndices[(oIdx + 1) % outlineCount][(pIdx + 0) % profileCount];
c = verticeIndices[(oIdx + 1) % outlineCount][(pIdx + 1) % profileCount];
d = verticeIndices[(oIdx + 0) % outlineCount][(pIdx + 1) % profileCount];
var ab = geo.vertices[a].distanceTo(geo.vertices[b]);
uvB.x = uvA.x + ab;
var ad = geo.vertices[a].distanceTo(geo.vertices[d]);
uvD.y = uvA.y + ad;
var dc = geo.vertices[d].distanceTo(geo.vertices[c]);
var bc = geo.vertices[b].distanceTo(geo.vertices[c]);
uvC.x = uvD.x + dc;
uvC.y = uvB.y + bc;
}
}
for(oIdx = 0; oIdx <= outlineCount; oIdx++) {
for(pIdx = 0; pIdx < profileCount; pIdx++) {
uvs[oIdx][pIdx].multiply(new THREE.Vector2(
1 / outlineLengths[pIdx % profileCount],
1 / profileLengths[oIdx % outlineCount]
));
uvs[oIdx][pIdx].y = 1 - uvs[oIdx][pIdx].y;
if(sharedUVs) {
uvs[oIdx][pIdx].multiply(new THREE.Vector2(1, 0.5));
}
}
}
}
function createProfileFaces() {
var oIdx, pIdx, segA, segB, a, b, c, d;
var faceA, faceB, fIdxA, fIdxB;
var uvA, uvB, uvC, uvD;
for(oIdx = 0; oIdx < outlineCount; oIdx++) {
for(pIdx = 0; pIdx < profileCount - 1; pIdx++) {
a = verticeIndices[(oIdx + 0) % outlineCount][(pIdx + 0) % profileCount];
b = verticeIndices[(oIdx + 1) % outlineCount][(pIdx + 0) % profileCount];
c = verticeIndices[(oIdx + 1) % outlineCount][(pIdx + 1) % profileCount];
d = verticeIndices[(oIdx + 0) % outlineCount][(pIdx + 1) % profileCount];
faceA = new THREE.Face3(a, c, b);
fIdxA = geo.faces.push(faceA) - 1;
faceB = new THREE.Face3(a, d, c);
fIdxB = geo.faces.push(faceB) - 1;
// UVs
uvA = uvs[(oIdx + 0)][(pIdx + 0)];
uvB = uvs[(oIdx + 1)][(pIdx + 0)];
uvC = uvs[(oIdx + 1)][(pIdx + 1)];
uvD = uvs[(oIdx + 0)][(pIdx + 1)];
geo.faceVertexUvs[uvLayer][fIdxA] = [uvA, uvC, uvB];
geo.faceVertexUvs[uvLayer][fIdxB] = [uvA, uvD, uvC];
}
}
}
if(sharedUVs === undefined) sharedUVs = true;
geo.faceVertexUvs[uvLayer] = [];
createProfileVertices();
computeOutlineLengths();
computeProfileLengths();
computeUVs();
createProfileFaces();
capFront(profileVs, outlineVs, geo);
capBack(profileVs, outlineVs, geo);
geo.uvsNeedUpdate = true;
geo.computeFaceNormals();
return geo;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment