View source on GitHub | See Demo | Toggle Types
language heron:std:0.1;
module heron:geometry.mesh:0.1
{
import heron:std.array:0.1;
import heron:geometry.vector:0.1;
(Array<Float3> -> Mesh)function mesh(vertexBuffer)
= mesh(vertexBuffer, vertexBuffer.indices);
('a 'b -> 'c)function mesh(vertexBuffer, indexBuffer)
= mesh(vertexBuffer);
(Array<Float3> Array<Int> -> Mesh)function mesh(vertexBuffer, indexBuffer)
= mesh(vertexBuffer, indexBuffer, origin.repeat(0));
(Array<Float3> Array<Int> Array<Float3> -> Mesh)function mesh(vertexBuffer, indexBuffer, uvBuffer)
= mesh(vertexBuffer, indexBuffer, uvBuffer, origin.repeat(0));
(Array<Float3> Array<Int> Array<Float3> Array<Float3> -> Mesh)function mesh(vertexBuffer, indexBuffer, uvBuffer, colorBuffer)
= mesh(vertexBuffer, indexBuffer, uvBuffer, colorBuffer, origin.repeat(0))
.computeVertexNormals();
(Mesh Array<Float3> -> Mesh)function setVertices(m, points)
= mesh(points, m.indexBuffer, m.uvBuffer, m.colorBuffer, m.normalBuffer);
(Mesh Array<Float3> -> Mesh)function setVertexColors(m, colors)
= mesh(m.vertexBuffer, m.indexBuffer, m.uvBuffer, colors, m.normalBuffer);
(Mesh Array<Float3> -> Mesh)function setVertexUVs(m, uvs)
= mesh(m.vertexBuffer, m.indexBuffer, uvs, m.colorBuffer, m.normalBuffer);
(Mesh Array<Float3> -> Mesh)function setVertexNormals(m, normals)
= mesh(m.vertexBuffer, m.indexBuffer, m.uvBuffer, m.colorBuffer, normals);
var tetrahedron
= mesh(
[
1, 1, 1,
-1, -1, 1,
-1, 1, -1,
1, -1, -1
].toVectors,
[
2, 1, 0,
0, 3, 2,
1, 3, 0,
2, 3, 1
]);
var cube
= mesh(
[
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
-1.0, 1.0, -1.0
].toVectors,
[
0, 1, 2, 2, 3, 0,
1, 5, 6, 6, 2, 1,
7, 6, 5, 5, 4, 7,
4, 0, 3, 3, 7, 4,
4, 5, 1, 1, 0, 4,
3, 2, 6, 6, 7, 3
]);
var octahedron
= mesh(
[
1, 0, 0, -1, 0, 0, 0, 1, 0,
0, -1, 0, 0, 0, 1, 0, 0, -1
].toVectors,
[
0, 2, 4, 0, 4, 3, 0, 3, 5,
0, 5, 2, 1, 2, 5, 1, 5, 3,
1, 3, 4, 1, 4, 2
]);
var dodecahedron =
var t = (1 + sqrt(5)) / 2 in
var r = 1 / t in
mesh([
-1, -1, -1, -1, -1, 1,
-1, 1, -1, -1, 1, 1,
1, -1, -1, 1, -1, 1,
1, 1, -1, 1, 1, 1,
0, -r, -t, 0, -r, t,
0, r, -t, 0, r, t,
-r, -t, 0, -r, t, 0,
r, -t, 0, r, t, 0,
-t, 0, -r, t, 0, -r,
-t, 0, r, t, 0, r
].toVectors,
[
3, 11, 7, 3, 7, 15, 3, 15, 13,
7, 19, 17, 7, 17, 6, 7, 6, 15,
17, 4, 8, 17, 8, 10, 17, 10, 6,
8, 0, 16, 8, 16, 2, 8, 2, 10,
0, 12, 1, 0, 1, 18, 0, 18, 16,
6, 10, 2, 6, 2, 13, 6, 13, 15,
2, 16, 18, 2, 18, 3, 2, 3, 13,
18, 1, 9, 18, 9, 11, 18, 11, 3,
4, 14, 12, 4, 12, 0, 4, 0, 8,
11, 9, 5, 11, 5, 19, 11, 19, 7,
19, 5, 14, 19, 14, 4, 19, 4, 17,
1, 12, 14, 1, 14, 5, 1, 5, 9
]);
var icosahedron
= var t = (1 + sqrt(5)) / 2 in
mesh([
-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0,
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1
].toVectors,
[
0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,
1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,
4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1
]);
(Array<'a> Int Bool Bool -> Array<Int>)function quadStripToMeshIndices(vertices, rows, connectRows, connectCols) {
var cols = vertices.count / rows;
var nr = connectRows ? rows : rows-1;
var nc = connectCols ? cols : cols-1;
var indices = [].mutable;
for (var row in 0..nr) {
for (var col in 0..nc) {
var a = col + row * cols;
var b = (col+1) % (cols) + row * cols;
var c = (col+1) % (cols) + (row+1) % (rows) * cols;
var d = col + (row+1) % (rows) * cols;
indices = indices.pushMany([a, b, d]);
indices = indices.pushMany([b, c, d]);
}
}
return indices.immutable;
}
(Float2 -> Float3)function vector(uv: Float2)
= float3(-uv.x.cos * uv.y.sin, uv.x.cos, uv.x.sin * uv.y.sin);
('a 'a 'a -> 'a)function rescale(v, from, length)
= from + (v * length);
((Float Float -> Float3) Int Int Float Float Float Float Bool Bool -> Mesh)function meshFromUV(f, uCount, vCount, uStart, vStart, uLength, vLength, uJoin, vJoin) {
var uMax = uJoin ? uCount.float : (uCount - 1).float;
var vMax = vJoin ? vCount.float : (vCount - 1).float;
var uvs = cartesianProduct(0..uCount, 0..vCount, (u, v)
=> vector(u / uMax * uLength + uStart, v / vMax * vLength + vStart, 0));
var points = uvs.map(uvw => f(uvw.x, uvw.y));
var normals = uvs.map(uvw => normalFromUV(uvw.x, uvw.y, f));
var indices = quadStripToMeshIndices(points, vCount, uJoin, vJoin);
return mesh(points, indices, uvs, origin.repeat(0), normals);
}
((Float Float -> Float3) Int -> Mesh)function meshFromUV(f, segments)
= meshFromUV(f, segments, true);
((Float Float -> Float3) Int Bool -> Mesh)function meshFromUV(f, segments, join)
= meshFromUV(f, segments, segments, 0.0, 0.0, 1.0, 1.0, join, join);
(Float Float -> Float3)function spherePoint(u, v)
= vector(-cos(u*2.0*pi) * sin(v*2.0*pi), cos(v*2.0*pi), sin(u*2.0*pi) * sin(v*2.0*pi));
(Int -> Mesh)function sphere(segments)
= meshFromUV(spherePoint, segments);
( -> Mesh)function sphere()
= sphere(32);
(Float Int -> Float3)function cylinderPoint(u, v)
= vector(sin(u*2.0*pi), v * 2, cos(u*2.0*pi));
(Int -> Mesh)function cylinder(segments)
= meshFromUV(cylinderPoint, segments);
( -> Mesh)function cylinder()
= cylinder(16);
(Float Float Int -> Mesh)function torus(r1, r2, segments)
= meshFromUV((u, v) => torusPoint(u, v, r1, r2), segments);
(Float Float Float Float -> Float3)function torusPoint(u, v, r1, r2)
= vector(
(r1 + r2 * cos(v*2.0*pi)) * cos(u*2.0*pi),
(r1 + r2 * cos(v*2.0*pi)) * sin(u*2.0*pi),
r2 * sin(v*2.0*pi));
(Float 'a (Float 'a -> Float3) Float3 Float -> Float3)function tangentFromUV(u, v, f, p: Float3, eps: Float)
= u - eps >= 0
? p - f(u - eps, v)
: f(u + eps, v) - p;
('a Float ('a Float -> Float3) Float3 Float -> Float3)function cotangentFromUV(u, v, f, p: Float3, eps: Float)
= v - eps >= 0
? p - f(u, v - eps)
: f(u, v + eps) - p;
(Float Float (Float Float -> Float3) -> Float3)function normalFromUV(u, v, f)
= var eps = 0.0001 in
var p = f(u, v) in
cross(tangentFromUV(u, v, f, p, eps), cotangentFromUV(u, v, f, p, eps)).normal;
( -> Mesh)function torus()
= torus(2, 0.5, 32);
(Mesh -> Int)function vertexCount(mesh)
= mesh.vertexBuffer.count;
(Mesh -> Int)function faceCount(mesh)
= mesh.indexBuffer.count / 3;
(Array<Float> -> Array<Float3>)function toVectors(xs)
= (0 .. xs.count/3).map(i => vector(xs[i*3], xs[i*3+1], xs[i*3+2]));
(Mesh (Float3 -> Float3) -> Mesh)function transform(m, f)
= m.setVertices(m.vertexBuffer.map(f));
(Mesh Float3 -> Mesh)function translate(m, amount)
= m.transform(v => v + amount);
(Mesh Array<Float3> -> Mesh)function translate(m, vectors)
= m.setVertices(m.vertexBuffer.zip(vectors, (p, v) => p + v));
(Mesh Float3 -> Mesh)function scale(m, amount)
= m.transform(v => v * amount);
(Float Float -> Float3)function kleinPoint(a, b) {
var u = a * pi * 2.0;
var v = b * pi * 2.0;
var x = 0.0, y = 0.0, z = 0.0;
if (u < pi) {
x = 3.0 * u.cos * (1.0 + u.sin) + (2.0 * (1.0 - u.cos / 2.0)) * u.cos * v.cos;
z = -8.0 * u.sin - 2.0 * (1.0 - u.cos / 2.0) * u.sin * v.cos;
} else {
x = 3.0 * u.cos * (1.0 + u.sin) + (2.0 * (1.0 - u.cos / 2.0)) * cos(v + pi);
z = -8.0 * u.sin;
}
y = -2.0 * (1.0 - u.cos / 2.0) * v.sin;
return vector(x / 4.0, y / 4.0, z / 4.0);
}
( -> Mesh)function klein()
= meshFromUV(kleinPoint, 32, false);
(Float Float -> Float3)function planeXYPoint(u, v) = vector(u, v, 0);
(Float Float -> Float3)function planeXZPoint(u, v) = vector(u, 0, v);
(Float Float -> Float3)function planeYZPoint(u, v) = vector(0, u, v);
(Float Float -> Float3)function planeYXPoint(u, v) = vector(v, u, 0);
(Float Float -> Float3)function planeZXPoint(u, v) = vector(v, 0, u);
(Float Float -> Float3)function planeZYPoint(u, v) = vector(0, v, u);
( -> Mesh)function plane()
= meshFromUV(planeXYPoint, 16, false);
(Float Float -> Float3)function mobiusPoint(a, b) {
var u = a - 0.5;
var v = b * 2.0 * pi;
return vector(
v.cos * (2 + u * cos( v / 2 )),
v.sin * (2 + u * cos( v / 2 )),
u * sin( v / 2 ));
}
( -> Mesh)function mobius()
= meshFromUV(mobiusPoint, 20, false);
(Mesh Int Int -> Float3)function facePoint(mesh, f, i)
= mesh.vertexBuffer[mesh.indexBuffer[f * 3 + i]];
(Mesh Int -> Float3)function faceNormal(mesh, f)
= cross(mesh.faceSide1(f), mesh.faceSide2(f)).normal;
(Mesh Int -> Float3)function faceSide1(mesh, f)
= mesh.facePoint(f, 1) - mesh.facePoint(f, 0);
(Mesh Int -> Float3)function faceSide2(mesh, f)
= mesh.facePoint(f, 2) - mesh.facePoint(f, 0);
(Mesh -> Array<Float3>)function faceNormals(mesh)
= (0..mesh.faceCount).map(i => mesh.faceNormal(i));
(Mesh -> Mesh)function computeVertexNormals(mesh) {
var sums = origin.repeat(mesh.vertexCount).mutable;
var counts = repeat(0, mesh.vertexCount).mutable;
for (var f in 0..mesh.faceCount) {
var normal = mesh.faceNormal(f);
for (var i in 0..3) {
var index = mesh.indexBuffer[f*3 + i];
sums[index] += normal;
counts[index] += 1;
}
}
return mesh.setVertexNormals(zip(sums, counts, (a, b) => (a / b.float.vector).normal));
}
(Float3 Float3 Float3 -> Float3)function applyRotation(pos, rotXYZ, rotW)
= pos + 2.0.vector * cross( rotXYZ, cross( rotXYZ, pos ) + rotW * pos );
(Float3 Float3 Float3 Float3 Float3 -> Float3)function applyTRS(pos, trans, rotXYZ, rotW, scale)
= applyRotation(pos * scale, rotXYZ, rotW) + trans;
}