diff --git a/src/kiri/codec.js b/src/kiri/codec.js index a9d19d3d..1909d4a8 100644 --- a/src/kiri/codec.js +++ b/src/kiri/codec.js @@ -125,7 +125,14 @@ polys: encode(layer.polys, state), lines: encodePointArray(layer.lines, state), faces: codec.allocFloat32Array(layer.faces), - color: layer.color + color: layer.color, + paths: layer.paths.map(path => { + return { + index: path.index, + faces: path.faces, + z: path.z + } + }) } }) }; return enc; @@ -138,7 +145,8 @@ polys: decode(v.data[i].polys, state), lines: decodePointArray(v.data[i].lines), faces: v.data[i].faces, - color: v.data[i].color + color: v.data[i].color, + paths: v.data[i].paths }; } return render; diff --git a/src/kiri/render.js b/src/kiri/render.js index ce5b6a91..684f55f7 100644 --- a/src/kiri/render.js +++ b/src/kiri/render.js @@ -22,6 +22,7 @@ lines: [], polys: [], faces: [], + paths: [], color: colors || { line: 0, face: 0 @@ -37,33 +38,21 @@ addPoly(poly) { this.current.polys.push(poly); - // const lines = this.current.lines; - // const points = poly.points; - // const len = points.length; - // for (let i=1; i { + const contour = []; + poly.points.forEach(p => { + contour.push(new THREE.Vector2(p.x, p.y)); + }); + const {index, faces} = ProfiledContourGeometry(profile, contour, true); + this.current.paths.push({ index, faces, z: poly.getZ() }); + }); + return this; + } + } + + function flat(polys) { + if (Array.isArray(polys)) { + return POLY.flatten(polys, [], true); + } else { + return POLY.flatten([polys], [], true); + } + } + + function ProfiledContourGeometry(profileShape, contour, contourClosed) { + + contourClosed = contourClosed !== undefined ? contourClosed : true; + + let profileGeometry = new THREE.ShapeBufferGeometry(profileShape); + profileGeometry.rotateX(Math.PI * .5); + + let profile = profileGeometry.attributes.position; + let faces = new Float32Array(profile.count * contour.length * 3); + + for (let i = 0; i < contour.length; i++) { + let v1 = new THREE.Vector2().subVectors(contour[i - 1 < 0 ? contour.length - 1 : i - 1], contour[i]); + let v2 = new THREE.Vector2().subVectors(contour[i + 1 == contour.length ? 0 : i + 1], contour[i]); + let angle = v2.angle() - v1.angle(); + let halfAngle = angle * .5; + let hA = halfAngle; + let tA = v2.angle() + Math.PI * .5; + + if (!contourClosed){ + if (i == 0 || i == contour.length - 1) {hA = Math.PI * .5;} + if (i == contour.length - 1) {tA = v1.angle() - Math.PI * .5;} + } + + let shift = Math.tan(hA - Math.PI * .5); + let shiftMatrix = new THREE.Matrix4().set( + 1, 0, 0, 0, + -shift, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); + + let tempAngle = tA; + let rotationMatrix = new THREE.Matrix4().set( + Math.cos(tempAngle), -Math.sin(tempAngle), 0, 0, + Math.sin(tempAngle), Math.cos(tempAngle), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); + + let translationMatrix = new THREE.Matrix4().set( + 1, 0, 0, contour[i].x, + 0, 1, 0, contour[i].y, + 0, 0, 1, 0, + 0, 0, 0, 1, + ); + + let cloneProfile = profile.clone(); + cloneProfile.applyMatrix4(shiftMatrix); + cloneProfile.applyMatrix4(rotationMatrix); + cloneProfile.applyMatrix4(translationMatrix); + + faces.set(cloneProfile.array, cloneProfile.count * i * 3); + } + + let index = []; + let lastCorner = contourClosed == false ? contour.length - 1: contour.length; + + for (let i = 0; i < lastCorner; i++) { + for (let j = 0; j < profile.count; j++) { + let currCorner = i; + let nextCorner = i + 1 == contour.length ? 0 : i + 1; + let currPoint = j; + let nextPoint = j + 1 == profile.count ? 0 : j + 1; + + let a = nextPoint + profile.count * currCorner; + let b = currPoint + profile.count * currCorner; + let c = currPoint + profile.count * nextCorner; + let d = nextPoint + profile.count * nextCorner; + + index.push(a, b, d); + index.push(b, c, d); + } + } + + return {index, faces}; } self.kiri.Render = Render; diff --git a/src/mode/cam/client.js b/src/mode/cam/client.js index a84bc5fe..e384ab07 100644 --- a/src/mode/cam/client.js +++ b/src/mode/cam/client.js @@ -46,7 +46,7 @@ } for (const [layer, data] of Object.entries(render.layers)) { - const { polys, lines, faces } = data; + const { polys, lines, faces, paths } = data; if (polys.length || lines.length) { const mat = new THREE.LineBasicMaterial({ color: data.color.line }); const geo = new THREE.Geometry(), vert = geo.vertices; @@ -79,6 +79,29 @@ mesh.receiveShadow = true; group.add(mesh); } + if (paths.length) { + const mat = new THREE.MeshPhongMaterial({ + transparent: true, + shininess: 100, + specular: 0x181818, + opacity: 1, + color: data.color.face, + side: THREE.DoubleSide + }); + paths.forEach((path, i) => { + const { index, faces, z } = path; + const geo = new THREE.BufferGeometry(); + geo.setAttribute('position', new THREE.BufferAttribute(faces, 3)); + geo.setIndex(index); + geo.computeFaceNormals(); + geo.computeVertexNormals(); + const mesh = new THREE.Mesh(geo, mat); + mesh.position.z = z; + mesh.castShadow = true; + mesh.receiveShadow = true; + group.add(mesh); + }); + } } slice.view.add(group); diff --git a/src/mode/cam/slice.js b/src/mode/cam/slice.js index af8aa5c7..be2ce310 100644 --- a/src/mode/cam/slice.js +++ b/src/mode/cam/slice.js @@ -124,8 +124,9 @@ // sliceAll.push(data.slice); // data.slice.output() // .setLayer("debug", {face: 0xff0000, line: 0}) - // // .addFlat(POLY.setZ(tshadow.clone(true), data.z), {offset: 1, outline: true}); - // .addPolys(POLY.setZ(tshadow.clone(true), data.z)); + // // .addFlats(POLY.setZ(tshadow.clone(true), data.z), {offset: 1, outline: true}); + // // .addPolys(POLY.setZ(tshadow.clone(true), data.z)); + // .addPaths(POLY.setZ(tshadow.clone(true), data.z)); updateOp(index, total); }, genso: true }); let shadowTop = terrain[terrain.length - 1];