improve slice/preview visuals with custom normals
This commit is contained in:
parent
c422c5779e
commit
ce856146e9
7 changed files with 143 additions and 41 deletions
|
|
@ -196,6 +196,8 @@ function pointsToPath(points, offset, open, miter = 1.5) {
|
|||
const left = [];
|
||||
const right = [];
|
||||
const faces = [];
|
||||
const normals = [];
|
||||
const zn = -1;
|
||||
// calculate vertex normals from segments normals
|
||||
// vertex info is associated with the origin point
|
||||
let fl, fr;
|
||||
|
|
@ -244,19 +246,33 @@ function pointsToPath(points, offset, open, miter = 1.5) {
|
|||
let r1 = right.peek(1);
|
||||
let l2 = left.peek(2);
|
||||
let r2 = right.peek(2);
|
||||
let ln = l1.vp.normal;
|
||||
let rn = r1.vp.normal;
|
||||
if (split_left && split_right) {
|
||||
faces.push(l1, l2, r1);
|
||||
faces.push(r2, r1, l2);
|
||||
fl = fl || l2;
|
||||
fr = fr || r2;
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
} else if (split_left) {
|
||||
faces.push(l1, l2, r1);
|
||||
fl = fl || l2;
|
||||
fr = fr || r1;
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
} else { // split right
|
||||
faces.push(r2, r1, l1);
|
||||
fl = fl || l1;
|
||||
fr = fr || r2;
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -270,6 +286,14 @@ function pointsToPath(points, offset, open, miter = 1.5) {
|
|||
let r1 = right.peek(split_right ? 2 : 1);
|
||||
faces.push(l1, l0, r1);
|
||||
faces.push(r0, r1, l0);
|
||||
let ln = l0.vp.normal;
|
||||
let rn = r0.vp.normal;
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
}
|
||||
}
|
||||
if (open) {
|
||||
|
|
@ -292,15 +316,24 @@ function pointsToPath(points, offset, open, miter = 1.5) {
|
|||
if (!open && faces) {
|
||||
let l1 = left.peek(1);
|
||||
let r1 = right.peek(1);
|
||||
let ln = l1.vp.normal;
|
||||
let rn = r1.vp.normal;
|
||||
faces.push(fl, l1, fr);
|
||||
faces.push(r1, fr, l1);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(-rn.dx, -rn.dy, zn);
|
||||
normals.push(ln.dx, ln.dy, zn);
|
||||
}
|
||||
return { left, right, faces, open };
|
||||
return { left, right, faces, normals, open };
|
||||
}
|
||||
|
||||
function pathTo3D(path, height, z) {
|
||||
const { faces, left, right, open } = path;
|
||||
const { faces, normals, left, right, open } = path;
|
||||
const out = [];
|
||||
const nrm = [];
|
||||
if (!(faces && left && right)) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -315,6 +348,13 @@ function pathTo3D(path, height, z) {
|
|||
for (let p of faces.slice().reverse()) {
|
||||
out.push(p.x, p.y, p.z + height);
|
||||
}
|
||||
nrm.appendAll(normals);
|
||||
// reverse normals to match faces, but underside so reverse Z as well
|
||||
for (let i=normals.length-1; i>0; i-=3) {
|
||||
nrm.push(normals[i-2]);
|
||||
nrm.push(normals[i-1]);
|
||||
nrm.push(-normals[i-0]);
|
||||
}
|
||||
for (let i=0, l=left.length, tl = open ? l-1 : l; i<tl; i++) {
|
||||
let p0 = left[i];
|
||||
let p1 = left[(i+1)%l];
|
||||
|
|
@ -324,6 +364,13 @@ function pathTo3D(path, height, z) {
|
|||
out.push(p1.x, p1.y, p1.z - height);
|
||||
out.push(p1.x, p1.y, p1.z + height);
|
||||
out.push(p0.x, p0.y, p0.z + height);
|
||||
let ln = p0.vp.normal;
|
||||
nrm.push(ln.dx, ln.dy, -1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, -1);
|
||||
nrm.push(ln.dx, ln.dy, -1);
|
||||
}
|
||||
for (let i=0, l=right.length, tl = open ? l-1 : l; i<tl; i++) {
|
||||
let p0 = right[i];
|
||||
|
|
@ -334,8 +381,16 @@ function pathTo3D(path, height, z) {
|
|||
out.push(p1.x, p1.y, p1.z - height);
|
||||
out.push(p0.x, p0.y, p0.z + height);
|
||||
out.push(p1.x, p1.y, p1.z + height);
|
||||
let rn = p0.vp.normal;
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
nrm.push(-rn.dy, rn.dx, -1);
|
||||
nrm.push(-rn.dy, rn.dx, -1);
|
||||
nrm.push(-rn.dy, rn.dx, -1);
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
}
|
||||
if (open) {
|
||||
// begin cap
|
||||
let l0 = left[0];
|
||||
let r0 = right[0];
|
||||
out.push(l0.x, l0.y, l0.z + height);
|
||||
|
|
@ -344,16 +399,31 @@ function pathTo3D(path, height, z) {
|
|||
out.push(r0.x, r0.y, r0.z + height);
|
||||
out.push(r0.x, r0.y, r0.z - height);
|
||||
out.push(l0.x, l0.y, l0.z + height);
|
||||
let ln = left.peek();
|
||||
let rn = right.peek();
|
||||
out.push(ln.x, ln.y, ln.z + height);
|
||||
out.push(ln.x, ln.y, ln.z - height);
|
||||
out.push(rn.x, rn.y, rn.z - height);
|
||||
out.push(rn.x, rn.y, rn.z + height);
|
||||
out.push(ln.x, ln.y, ln.z + height);
|
||||
out.push(rn.x, rn.y, rn.z - height);
|
||||
let ln = l0.vp.normal;
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
// end cap
|
||||
let le = left.peek();
|
||||
let re = right.peek();
|
||||
out.push(le.x, le.y, le.z + height);
|
||||
out.push(le.x, le.y, le.z - height);
|
||||
out.push(re.x, re.y, re.z - height);
|
||||
out.push(re.x, re.y, re.z + height);
|
||||
out.push(le.x, le.y, le.z + height);
|
||||
out.push(re.x, re.y, re.z - height);
|
||||
ln = re.vp.normal;
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
}
|
||||
return out;
|
||||
return { faces: out, normals: nrm };
|
||||
}
|
||||
|
||||
// produces indexed geometry which isn't ideal for rendering because
|
||||
|
|
|
|||
|
|
@ -920,6 +920,11 @@ class Polygon {
|
|||
return this;
|
||||
}
|
||||
|
||||
setOpenValue(b) {
|
||||
this.open = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
isOpen() {
|
||||
return this.open;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ FDM.slice = function(settings, widget, onupdate, ondone) {
|
|||
let fillSpace = fillSpacing * spaceMult * solidWidth;
|
||||
doSolidLayerFill(slice, fillSpace, sliceFillAngle);
|
||||
}
|
||||
sliceFillAngle += 90.0;
|
||||
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
||||
}, "solid layers");
|
||||
|
||||
// add lead in anchor when specified in belt mode (but not for synths)
|
||||
|
|
@ -717,7 +717,7 @@ FDM.slice = function(settings, widget, onupdate, ondone) {
|
|||
let fillSpace = fillSpacing * spaceMult * solidWidth;
|
||||
let solidMinArea = params.sliceSolidMinArea;
|
||||
doSolidsFill(slice, fillSpace, sliceFillAngle, solidMinArea, promises);
|
||||
sliceFillAngle += 90.0;
|
||||
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
||||
}, "fill solids");
|
||||
// very last layer (top) is set to finish solid rate
|
||||
slices.last().finishSolids = true
|
||||
|
|
|
|||
|
|
@ -195,13 +195,15 @@ kiri.Layers.prototype.encode = function(state) {
|
|||
polys: encode(layer.polys, state),
|
||||
lines: encodePointArray(layer.lines, state),
|
||||
faces: codec.allocFloat32Array(layer.faces),
|
||||
norms: layer.norms ? codec.allocFloat32Array(layer.norms) : undefined,
|
||||
cface: layer.cface || codec.undef,
|
||||
color: layer.color,
|
||||
paths: layer.paths.map(lp => {
|
||||
const pe = {
|
||||
z: lp.z,
|
||||
index: lp.index,
|
||||
faces: codec.allocFloat32Array(lp.faces)
|
||||
faces: codec.allocFloat32Array(lp.faces),
|
||||
norms: lp.norms ? codec.allocFloat32Array(lp.norms) : undefined
|
||||
};
|
||||
if (zeroOut && state.zeros && pe.faces.length) {
|
||||
state.zeros.push(pe.faces.buffer);
|
||||
|
|
@ -226,22 +228,25 @@ kiri.Layers.prototype.encode = function(state) {
|
|||
registerDecoder('layers', function(v, state) {
|
||||
let render = new kiri.Layers();
|
||||
for (let i=0; i<v.layers.length; i++) {
|
||||
const data = v.data[i];
|
||||
const d = render.layers[v.layers[i]] = {
|
||||
polys: decode(v.data[i].polys, state),
|
||||
cpoly: v.data[i].cpoly,
|
||||
lines: decodePointArray(v.data[i].lines),
|
||||
faces: codec.allocFloat32Array(v.data[i].faces),
|
||||
cface: v.data[i].cface,
|
||||
color: v.data[i].color,
|
||||
paths: v.data[i].paths.map(lp => {
|
||||
polys: decode(data.polys, state),
|
||||
cpoly: data.cpoly,
|
||||
lines: decodePointArray(data.lines),
|
||||
faces: codec.allocFloat32Array(data.faces),
|
||||
norms: data.norms ? codec.allocFloat32Array(data.norms) : undefined,
|
||||
cface: data.cface,
|
||||
color: data.color,
|
||||
paths: data.paths.map(lp => {
|
||||
return {
|
||||
z: lp.z,
|
||||
index: lp.index,
|
||||
faces: codec.allocFloat32Array(lp.faces)
|
||||
faces: codec.allocFloat32Array(lp.faces),
|
||||
norms: lp.norms ? codec.allocFloat32Array(lp.norms) : undefined
|
||||
};
|
||||
}),
|
||||
cpath: v.data[i].cpath,
|
||||
off: v.data[i].off
|
||||
cpath: data.cpath,
|
||||
off: data.off
|
||||
};
|
||||
// fixup null -> Infinity in material counts (JSON stringify sucks)
|
||||
if (d.cface) {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ class Layers {
|
|||
lines: [],
|
||||
polys: [], // colors are an attribute on polygons
|
||||
faces: [],
|
||||
norms: undefined, // vertex normals
|
||||
cface: undefined, // face color indices
|
||||
paths: [],
|
||||
cpath: undefined, // path colors indices
|
||||
|
|
@ -148,18 +149,25 @@ class Layers {
|
|||
if (!polys.length) {
|
||||
return;
|
||||
}
|
||||
const z = polys[0].getZ(), faces = this.current.faces;
|
||||
const cur = this.current;
|
||||
const z = polys[0].getZ(), faces = cur.faces;
|
||||
const off_opt = { z, flat: true };
|
||||
polys.forEach(poly => {
|
||||
const faceidx = faces.length / 3;
|
||||
if (newPath) {
|
||||
const path = poly.toPath2D(offset * 0.95);
|
||||
const { left, right, normals } = path;
|
||||
for (let p of path.faces) {
|
||||
faces.push(p.x, p.y, p.z);
|
||||
}
|
||||
if (opts.outline) {
|
||||
this.addPolys(newPolygon().addPoints(path.left).setOpen());
|
||||
this.addPolys(newPolygon().addPoints(path.right).setOpen());
|
||||
this.addPolys(newPolygon().addPoints(left).setOpenValue(poly.open));
|
||||
this.addPolys(newPolygon().addPoints(right).setOpenValue(poly.open));
|
||||
}
|
||||
if (cur.norms) {
|
||||
cur.norms.appendAll(normals);
|
||||
} else {
|
||||
cur.norms = normals;
|
||||
}
|
||||
} else {
|
||||
let exp = off_opt.outs = [];
|
||||
|
|
@ -186,7 +194,6 @@ class Layers {
|
|||
});
|
||||
});
|
||||
}
|
||||
const cur = this.current;
|
||||
const color = opts.color ?
|
||||
(typeof(opts.color) === 'number' ? { line: opts.color, face: opts.color } : opts.color) :
|
||||
cur.color;
|
||||
|
|
@ -220,7 +227,7 @@ class Layers {
|
|||
continue;
|
||||
}
|
||||
const z = opts.z || poly.getZ();
|
||||
const faces = poly.toPath3D(offset, height, 0);
|
||||
const { faces, normals } = poly.toPath3D(offset, height, 0);
|
||||
const cur = this.current;
|
||||
const one = cur.paths[0];
|
||||
if (one) {
|
||||
|
|
@ -244,8 +251,11 @@ class Layers {
|
|||
cur.cpath.push(Object.assign({ start: indln, count: Infinity }, opts.color));
|
||||
}
|
||||
}
|
||||
if (normals) {
|
||||
one.norms.appendAll(normals);
|
||||
}
|
||||
} else {
|
||||
cur.paths.push({ index: [], faces, z });
|
||||
cur.paths.push({ index: [], faces, z, norms: normals });
|
||||
if (opts.color) {
|
||||
cur.cpath = [ Object.assign({ start: 0, count: Infinity }, opts.color) ];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ class Stack {
|
|||
}
|
||||
}
|
||||
|
||||
const { polys, lines, faces, cface, paths, cpath, color, off } = layer;
|
||||
const { polys, lines, faces, cface, paths, cpath, color, off, norms } = layer;
|
||||
const meshes = [];
|
||||
const defstate = !off;
|
||||
const mats = [];
|
||||
|
|
@ -168,15 +168,20 @@ class Stack {
|
|||
} else {
|
||||
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
||||
}
|
||||
geo.computeVertexNormals();
|
||||
if (norms) {
|
||||
geo.setAttribute('normal', new THREE.BufferAttribute(norms, 3));
|
||||
// geo.normalizeNormals();
|
||||
} else {
|
||||
geo.computeVertexNormals();
|
||||
}
|
||||
if (cface) {
|
||||
cface.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
||||
} else {
|
||||
geo.addGroup(0, Infinity, 0);
|
||||
}
|
||||
const mesh = new THREE.Mesh(geo, mat);
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
// mesh.castShadow = true;
|
||||
// mesh.receiveShadow = true;
|
||||
meshes.push(mesh);
|
||||
group.add(mesh);
|
||||
mats.appendAll(mat);
|
||||
|
|
@ -191,11 +196,18 @@ class Stack {
|
|||
mat.forEach(m => m.visible = defstate);
|
||||
// const mat = newMat(data);
|
||||
paths.forEach((path, i) => {
|
||||
const { index, faces, z, colors } = path;
|
||||
const { index, faces, norms, z, colors } = path;
|
||||
const geo = new THREE.BufferGeometry();
|
||||
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
||||
if (index.length) geo.setIndex(index);
|
||||
geo.computeVertexNormals();
|
||||
if (index.length) {
|
||||
geo.setIndex(index);
|
||||
}
|
||||
if (norms) {
|
||||
geo.setAttribute('normal', new THREE.BufferAttribute(norms, 3));
|
||||
// geo.normalizeNormals();
|
||||
} else {
|
||||
geo.computeVertexNormals();
|
||||
}
|
||||
if (cpath) {
|
||||
cpath.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
||||
} else {
|
||||
|
|
@ -203,8 +215,8 @@ class Stack {
|
|||
}
|
||||
const mesh = new THREE.Mesh(geo, mat);
|
||||
mesh.position.z = z;
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
// mesh.castShadow = true;
|
||||
// mesh.receiveShadow = true;
|
||||
meshes.push(mesh);
|
||||
group.add(mesh);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -307,8 +307,8 @@ class Widget {
|
|||
mesh.renderOrder = 1;
|
||||
geometry.computeVertexNormals();
|
||||
geometry.addGroup(0, Infinity, 0);
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
// mesh.castShadow = true;
|
||||
// mesh.receiveShadow = true;
|
||||
mesh.widget = this;
|
||||
this.mesh = mesh;
|
||||
// invalidates points cache (like any scale/rotation)
|
||||
|
|
|
|||
Loading…
Reference in a new issue