10000 new JS Mesh class by elalish · Pull Request #272 · elalish/manifold · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

new JS Mesh class #272

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,13 @@
"codecvt": "cpp",
"regex": "cpp",
"future": "cpp",
"shared_mutex": "cpp"
"shared_mutex": "cpp",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe we should put this into .gitignore to reduce noise. Btw you can use compile_commands.json instead of manually configuring it, see https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html and https://code.visualstudio.com/docs/cpp/faq-cpp#_how-do-i-get-intellisense-to-work-correctly

"__bits": "cpp",
"compare": "cpp",
"concepts": "cpp",
"queue": "cpp",
"stack": "cpp",
"__hash_table": "cpp"
},
"C_Cpp.clang_format_fallbackStyle": "google",
"editor.formatOnSave": true,
Expand Down
64 changes: 46 additions & 18 deletions bindings/wasm/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

#include <emscripten/bind.h>
#include <emscripten/val.h>

using namespace emscripten;

Expand Down Expand Up @@ -55,6 +56,48 @@ std::vector<SimplePolygon> ToPolygon(
return simplePolygons;
}

val GetMeshJS(const Manifold& manifold) {
MeshGL mesh = manifold.GetMeshGL();
val meshJS = val::object();

meshJS.set("triVerts",
val(typed_memory_view(mesh.triVerts.size(), mesh.triVerts.data()))
.call<val>("slice"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a documentation for typed_memory_view and this slice thing? I cannot find it in the documentation for val.h and embind.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, some of their docs are pretty well-hidden: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/em 10000 bind.html?highlight=typed_memory_view#memory-views and slice is just the JS Array method; the cool thing is val.call lets you call any JS function you want.

Now that I know about this, I'm thinking it would be better to move more of our logic from bindings.js to bindings.cpp so we can minimize the footprint of our exposed bindings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a comment. I'm not sure if I'm right, but I feel there is some overhead in using the val as it has to switch context between JS and wasm. It might be better to use EM_JS to do all the javascript work in one context switch.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That may be true, but val is also vastly easier to use (no restrictions on types). Still, that may be reason enough not to bother moving the other logic. Some perf comparisons would be interesting!

meshJS.set("vertPos",
val(typed_memory_view(mesh.vertPos.size(), mesh.vertPos.data()))
.call<val>("slice"));
meshJS.set("vertNormal", val(typed_memory_view(mesh.vertNormal.size(),
mesh.vertNormal.data()))
.call<val>("slice"));
meshJS.set("halfedgeTangent",
val(typed_memory_view(mesh.halfedgeTangent.size(),
mesh.halfedgeTangent.data()))
.call<val>("slice"));

return meshJS;
}

MeshGL MeshJS2GL(const val& mesh) {
MeshGL out;
out.triVerts = convertJSArrayToNumberVector<uint32_t>(mesh["triVerts"]);
out.vertPos = convertJSArrayToNumberVector<float>(mesh["vertPos"]);
if (mesh["vertNormal"] != val::undefined()) {
out.vertNormal = convertJSArrayToNumberVector<float>(mesh["vertNormal"]);
}
if (mesh["halfedgeTangent"] != val::undefined()) {
out.halfedgeTangent =
convertJSArrayToNumberVector<float>(mesh["halfedgeTangent"]);
}
return out;
}

Manifold FromMeshJS(const val& mesh) { return Manifold(MeshJS2GL(mesh)); }

Manifold Smooth(const val& mesh,
const std::vector<Smoothness>& sharpenedEdges = {}) {
return Manifold::Smooth(MeshJS2GL(mesh), sharpenedEdges);
}

Manifold Extrude(std::vector<std::vector<glm::vec2>>& polygons, float height,
int nDivisions, float twistDegrees, glm::vec2 scaleTop) {
return Manifold::Extrude(ToPolygon(polygons), height, nDivisions,
Expand Down Expand Up @@ -158,27 +201,12 @@ EMSCRIPTEN_BINDINGS(whatever) {
register_vector<BaryRef>("Vector_baryRef");
register_vector<glm::vec4>("Vector_vec4");

value_object<Mesh>("Mesh")
.field("vertPos", &Mesh::vertPos)
.field("triVerts", &Mesh::triVerts)
.field("vertNormal", &Mesh::vertNormal)
.field("halfedgeTangent", &Mesh::halfedgeTangent);

class_<MeshGL>("MeshGL")
.constructor<int, int>()
.property("numVert", &MeshGL::numVert)
.property("numTri", &MeshGL::numTri)
.function("vertPos", &MeshGL::vertPos, allow_raw_pointers())
.function("triVerts", &MeshGL::triVerts, allow_raw_pointers())
.function("vertNormal", &MeshGL::vertNormal, allow_raw_pointers());

class_<Manifold>("Manifold")
.constructor<Mesh>()
.constructor(&FromMeshJS)
.function("add", &Union)
.function("subtract", &Difference)
.function("intersect", &Intersection)
.function("_GetMesh", & 6D4E Manifold::GetMesh)
.function("_GetMeshGL", &Manifold::GetMeshGL)
.function("_GetMeshJS", &GetMeshJS)
.function("refine", &Manifold::Refine)
.function("_Warp", &Warp)
.function("_Transform", &Transform)
Expand All @@ -204,7 +232,7 @@ EMSCRIPTEN_BINDINGS(whatever) {
function("_Cylinder", &Manifold::Cylinder);
function("_Sphere", &Manifold::Sphere);
function("tetrahedron", &Manifold::Tetrahedron);
function("_Smooth", &Manifold::Smooth);
function("_Smooth", &Smooth);
function("_Extrude", &Extrude);
function("_Revolve", &Revolve);
function("_LevelSet", &LevelSetJs);
Expand Down
100 changes: 44 additions & 56 deletions bindings/wasm/bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,34 +50,6 @@ Module.setup = function() {
polygonsVec.delete();
}

function mesh2vec(mesh) {
const vertPos = toVec(new Module.Vector_vec3, mesh.vertPos, p => {
return {
x: p[0], y: p[1], z: p[2]
}
});
const triVerts = toVec(new Module.Vector_ivec3, mesh.triVerts);
const vertNormal = toVec(new Module.Vector_vec3, mesh.vertNormal, p => {
return {
x: p[0], y: p[1], z: p[2]
}
});
const halfedgeTangent =
toVec(new Module.Vector_vec4, mesh.halfedgeTangent, p => {
return {
x: p[0], y: p[1], z: p[2], w: p[3]
}
});
return {vertPos, triVerts, vertNormal, halfedgeTangent};
}

function disposeMesh(meshVec) {
meshVec.vertPos.delete();
meshVec.triVerts.delete();
meshVec.vertNormal.delete();
meshVec.halfedgeTangent.delete();
}

function vararg2vec(vec) {
if (vec[0] instanceof Array)
return {x: vec[0][0], y: vec[0][1], z: vec[0][2]};
Expand Down Expand Up @@ -149,23 +121,48 @@ Module.setup = function() {
return result;
};

class Mesh {
constructor({
triVerts = new Uint32Array(),
vertPos = new Float32Array(),
vertNormal,
halfedgeTangent
} = {}) {
this.triVerts = triVerts;
this.vertPos = vertPos;
this.vertNormal = vertNormal;
this.halfedgeTangent = halfedgeTangent;
}

get numTri() {
return this.triVerts.length / 3;
}

get numVert() {
return this.vertPos.length / 3;
}

verts(tri) {
return this.triVerts.subarray(3 * tri, 3 * (tri + 1));
}

position(vert) {
return this.vertPos.subarray(3 * vert, 3 * (vert + 1));
}

normal(vert) {
return this.vertNormal.subarray(3 * vert, 3 * (vert + 1));
}

tangent(halfedge) {
return this.halfedgeTangent.subarray(4 * halfedge, 4 * (halfedge + 1));
}
}

Module.Mesh = Mesh;

Module.Manifold.prototype.getMesh = function() {
const result = this._GetMesh();
const oldVertPos = result.vertPos;
const oldTriVerts = result.triVerts;
const oldVertNormal = result.vertNormal;
const oldHalfedgeTangent = result.halfedgeTangent;
const conversion1 = v => ['x', 'y', 'z'].map(f => v[f]);
const conversion2 = v => ['x', 'y', 'z', 'w'].map(f => v[f]);
result.vertPos = fromVec(oldVertPos, conversion1);
result.triVerts = fromVec(oldTriVerts);
result.vertNormal = fromVec(oldVertNormal, conversion1);
result.halfedgeTangent = fromVec(oldHalfedgeTangent, conversion2);
oldVertPos.delete();
oldTriVerts.delete();
oldVertNormal.delete();
oldHalfedgeTangent.delete();
return result;
return new Mesh(this._GetMeshJS());
};

Module.Manifold.prototype.getMeshRelation = function() {
Expand Down Expand Up @@ -232,8 +229,8 @@ Module.setup = function() {
});

const ManifoldCtor = Module.Manifold;
Module.ManifoldFromMeshVec = function(meshVec) {
const manifold = new ManifoldCtor(meshVec);
Module.Manifold = function(mesh) {
const manifold = new ManifoldCtor(mesh);

const status = manifold.status();
if (status.value !== 0) {
Expand All @@ -243,13 +240,6 @@ Module.setup = function() {
return manifold;
};

Module.Manifold = function Manifold(mesh) {
const meshVec = mesh2vec(mesh);
const manifold = Module.ManifoldFromMeshVec(meshVec);
disposeMesh(meshVec);
return manifold;
};

Module.Manifold.prototype = Object.create(ManifoldCtor.prototype);

Module.cube = function(...args) {
Expand All @@ -276,12 +266,10 @@ Module.setup = function() {
};

Module.smooth = function(mesh, sharpenedEdges = []) {
const meshVec = mesh2vec(mesh);
const sharp = new Module.Vector_smoothness();
toVec(sharp, sharpenedEdges);
const result = Module._Smooth(meshVec, sharp);
const result = Module._Smooth(mesh, sharp);
sharp.delete();
disposeMesh(meshVec);
return result;
};

Expand Down
23 changes: 13 additions & 10 deletions bindings/wasm/examples/examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,32 +155,35 @@ exports.functions = {
const sharpness = 0.8;
const n = 50;

const scallop = {vertPos: [], triVerts: []};
scallop.vertPos.push([-offset, 0, height], [-offset, 0, -height]);
const vertPos = [];
const triVerts = [];
vertPos.push(-offset, 0, height, -offset, 0, -height);
const sharpenedEdges = [];

const delta = 3.14159 / wiggles;
for (let i = 0; i < 2 * wiggles; ++i) {
const theta = (i - wiggles) * delta;
const amp = 0.5 * height * Math.max(Math.cos(0.8 * theta), 0);

scallop.vertPos.push([
radius * Math.cos(theta), radius * Math.sin(theta),
amp * (i % 2 == 0 ? 1 : -1)
]);
vertPos.push(
radius * Math.cos(theta), radius * Math.sin(theta),
amp * (i % 2 == 0 ? 1 : -1));
let j = i + 1;
if (j == 2 * wiggles) j = 0;

const smoothness = 1 - sharpness * Math.cos((theta + delta / 2) / 2);
let halfedge = 3 * scallop.triVerts.length + 1;
let halfedge = triVerts.length + 1;
sharpenedEdges.push({halfedge, smoothness});
scallop.triVerts.push([0, 2 + i, 2 + j]);
triVerts.push(0, 2 + i, 2 + j);

halfedge = 3 * scallop.triVerts.length + 1;
halfedge = triVerts.length + 1;
sharpenedEdges.push({halfedge, smoothness});
scallop.triVerts.push([1, 2 + j, 2 + i]);
triVerts.push(1, 2 + j, 2 + i);
}

const scallop = new Mesh();
scallop.triVerts = Uint32Array.from(triVerts);
scallop.vertPos = Float32Array.from(vertPos);
const result = smooth(scallop, sharpenedEdges).refine(n);
return result;
},
Expand Down
52 changes: 6 additions & 46 deletions bindings/wasm/examples/model-viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@

const icosahedron = simplify(new THREE.IcosahedronGeometry(0.16));
const manifold_1 = wasm.cube(0.2, 0.2, 0.2, true);
// const manifold_1 = wasm
// .revolve([[[0.1, 0.1], [0.2, 0.1], [0.1, 0.2]]], 64)
// .translate(0, 0, -0.1);
const manifold_2 = new wasm.Manifold(geometry2mesh(icosahedron));

const csg = function (operation) {
Expand Down Expand Up @@ -104,53 +101,16 @@

// functions to convert between three.js and wasm
function geometry2mesh(geometry) {
const mesh = {
vertPos: new wasm.Vector_vec3(),
vertNormal: new wasm.Vector_vec3(),
triVerts: new wasm.Vector_ivec3(),
halfedgeTangent: new wasm.Vector_vec4()
};
const temp = new THREE.Vector3();
const p = geometry.attributes.position;
// const n = geometry.attributes.normal;
const x = geometry.index;
for (let i = 0; i < p.count; i++) {
temp.fromBufferAttribute(p, i);
mesh.vertPos.push_back(temp);
// temp.fromBufferAttribute(n, i);
// mesh.vertNormal.push_back(temp);
}
for (let i = 0; i < x.count; i += 3) {
mesh.triVerts.push_back(x.array.subarray(i, i + 3));
}
return mesh;
const vertPos = geometry.attributes.position.array;
const vertNormal = geometry.attributes.normal.array;
const triVerts = geometry.index.array;
return new wasm.Mesh({vertPos, vertNormal, triVerts});
}

function mesh2geometry(mesh) {
const geometry = new THREE.BufferGeometry();

const numVert = mesh.vertPos.length;
const vert = new Float32Array(3 * numVert);
for (let i = 0; i < numVert; i++) {
const v = mesh.vertPos[i];
const idx = 3 * i;
vert[idx] = v[0];
vert[idx + 1] = v[1];
vert[idx + 2] = v[2];
}

const numTri = mesh.triVerts.length;
const tri = new Uint32Array(3 * numTri);
for (let i = 0; i < numTri; i++) {
const v = mesh.triVerts[i];
const idx = 3 * i;
tri[idx] = v[0];
tri[idx + 1] = v[1];
tri[idx + 2] = v[2];
}

geometry.setAttribute('position', new THREE.BufferAttribute(vert, 3));
geometry.setIndex(new THREE.BufferAttribute(tri, 1));
geometry.setAttribute('position', new THREE.BufferAttribute(mesh.vertPos, 3));
geometry.setIndex(new THREE.BufferAttribute(mesh.triVerts, 1));
return geometry;
}

Expand Down
Loading
0