Skip to content
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

Manifold extension for glTF #414

Closed
wants to merge 15 commits into from
4 changes: 2 additions & 2 deletions bindings/c/manifoldc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ size_t manifold_meshgl_tri_length(ManifoldMeshGL *m) {
}

size_t manifold_meshgl_merge_length(ManifoldMeshGL *m) {
return from_c(m)->mergeFromVert.size();
return from_c(m)->mergeTriVert.size();
}

size_t manifold_meshgl_run_index_length(ManifoldMeshGL *m) {
Expand Down Expand Up @@ -388,7 +388,7 @@ uint32_t *manifold_meshgl_tri_verts(void *mem, ManifoldMeshGL *m) {
}

uint32_t *manifold_meshgl_merge_from_vert(void *mem, ManifoldMeshGL *m) {
return copy_data(mem, from_c(m)->mergeFromVert);
return copy_data(mem, from_c(m)->mergeTriVert);
}

uint32_t *manifold_meshgl_merge_to_vert(void *mem, ManifoldMeshGL *m) {
Expand Down
30 changes: 22 additions & 8 deletions bindings/wasm/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ std::vector<SimplePolygon> ToPolygon(
return simplePolygons;
}

val GetMeshJS(const Manifold& manifold, const glm::ivec3& normalIdx) {
MeshGL mesh = manifold.GetMeshGL(normalIdx);
val MeshGL2JS(const MeshGL& mesh) {
val meshJS = val::object();

meshJS.set("numProp", mesh.numProp);
Expand All @@ -68,9 +67,9 @@ val GetMeshJS(const Manifold& manifold, const glm::ivec3& normalIdx) {
val(typed_memory_view(mesh.vertProperties.size(),
mesh.vertProperties.data()))
.call<val>("slice"));
meshJS.set("mergeFromVert", val(typed_memory_view(mesh.mergeFromVert.size(),
mesh.mergeFromVert.data()))
.call<val>("slice"));
meshJS.set("mergeTriVert", val(typed_memory_view(mesh.mergeTriVert.size(),
mesh.mergeTriVert.data()))
.call<val>("slice"));
meshJS.set("mergeToVert", val(typed_memory_view(mesh.mergeToVert.size(),
mesh.mergeToVert.data()))
.call<val>("slice"));
Expand Down Expand Up @@ -100,9 +99,9 @@ MeshGL MeshJS2GL(const val& mesh) {
out.triVerts = convertJSArrayToNumberVector<uint32_t>(mesh["triVerts"]);
out.vertProperties =
convertJSArrayToNumberVector<float>(mesh["vertProperties"]);
if (mesh["mergeFromVert"] != val::undefined()) {
out.mergeFromVert =
convertJSArrayToNumberVector<uint32_t>(mesh["mergeFromVert"]);
if (mesh["mergeTriVert"] != val::undefined()) {
out.mergeTriVert =
convertJSArrayToNumberVector<uint32_t>(mesh["mergeTriVert"]);
}
if (mesh["mergeToVert"] != val::undefined()) {
out.mergeToVert =
Expand All @@ -129,6 +128,20 @@ MeshGL MeshJS2GL(const val& mesh) {
return out;
}

val GetMeshJS(const Manifold& manifold, const glm::ivec3& normalIdx) {
MeshGL mesh = manifold.GetMeshGL(normalIdx);
return MeshGL2JS(mesh);
}

val Merge(const val& mesh) {
val out = val::object();
MeshGL meshGL = MeshJS2GL(mesh);
bool changed = meshGL.Merge();
out.set("changed", changed);
out.set("mesh", changed ? MeshGL2JS(meshGL) : mesh);
return out;
}

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

Manifold Smooth(const val& mesh,
Expand Down Expand Up @@ -270,6 +283,7 @@ EMSCRIPTEN_BINDINGS(whatever) {
function("_Triangulate", &Triangulate);
function("_Revolve", &Revolve);
function("_LevelSet", &LevelSetJs);
function("_Merge", &Merge);

function("_unionN", &UnionN);
function("_differenceN", &DifferenceN);
Expand Down
18 changes: 13 additions & 5 deletions bindings/wasm/bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Module.setup = function() {
numProp = 3,
triVerts = new Uint32Array(),
vertProperties = new Float32Array(),
mergeFromVert,
mergeTriVert,
mergeToVert,
runIndex,
runOriginalID,
Expand All @@ -132,7 +132,7 @@ Module.setup = function() {
this.numProp = numProp;
this.triVerts = triVerts;
this.vertProperties = vertProperties;
this.mergeFromVert = mergeFromVert;
this.mergeTriVert = mergeTriVert;
this.mergeToVert = mergeToVert;
this.runIndex = runIndex;
this.runOriginalID = runOriginalID;
Expand All @@ -153,6 +153,12 @@ Module.setup = function() {
return this.runOriginalID.length;
}

merge() {
const {changed, mesh} = Module._Merge(this);
Object.assign(this, {...mesh});
return changed;
}

verts(tri) {
return this.triVerts.subarray(3 * tri, 3 * (tri + 1));
}
Expand Down Expand Up @@ -302,14 +308,16 @@ Module.setup = function() {
return result;
};

Module.triangulate = function(polygons, precision = -1) {
Module.triangulate =
function(polygons, precision = -1) {
const polygonsVec = polygons2vec(polygons);
const result = fromVec(Module._Triangulate(polygonsVec, precision), (x) => [x[0], x[1], x[2]]);
const result = fromVec(
Module._Triangulate(polygonsVec, precision), (x) => [x[0], x[1], x[2]]);
disposePolygons(polygonsVec);
return result;
}

Module.revolve = function(polygons, circularSegments = 0) {
Module.revolve = function(polygons, circularSegments = 0) {
const polygonsVec = polygons2vec(polygons);
const result = Module._Revolve(polygonsVec, circularSegments);
disposePolygons(polygonsVec);
Expand Down
97 changes: 28 additions & 69 deletions bindings/wasm/examples/model-viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,55 +47,42 @@
</select>
<model-viewer camera-controls shadow-intensity="1" alt="Output of mesh Boolean operation"></model-viewer>
</body>
<script type="importmap">
{
"imports": {
"@gltf-transform/core": "https://cdn.jsdelivr.net/npm/@gltf-transform/core@3.1.2/+esm"
}
}
</script>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/2.0.1/model-viewer.min.js"></script>
<script type="module">
import { Accessor, Document, WebIO } from 'https://cdn.skypack.dev/pin/@gltf-transform/core@v3.0.0-SfbIFhNPTRdr1UE2VSan/mode=imports,min/optimized/@gltf-transform/core.js';
import { Document, WebIO } from '@gltf-transform/core';
import { loadTexture, writeMesh, disposeMesh, setupIO, getMaterialMap } from '../gltfIO.js';
import Module from '../manifold.js';

const io = new WebIO();
const io = setupIO(new WebIO());
const doc = new Document();
const node = doc.createNode();
doc.createScene().addChild(node);

const buffer = doc.createBuffer();
const position = doc.createAccessor().setBuffer(buffer).setType(Accessor.Type.VEC3);
const uv = doc.createAccessor().setBuffer(buffer).setType(Accessor.Type.VEC2);
const moonIndices = doc.createAccessor().setBuffer(buffer).setType(Accessor.Type.SCALAR);
const spaceIndices = doc.createAccessor().setBuffer(buffer).setType(Accessor.Type.SCALAR);
const moonTexture = doc.createTexture('moon');
const spaceTexture = doc.createTexture('space');
const moonMaterial = doc.createMaterial('moon').setBaseColorTexture(moonTexture);
const spaceMaterial = doc.createMaterial('moon').setBaseColorTexture(spaceTexture);
const moonPrimitive = doc.createPrimitive().setMaterial(moonMaterial)
.setIndices(moonIndices).setAttribute('POSITION', position).setAttribute('TEXCOORD_0', uv);
const spacePrimitive = doc.createPrimitive().setMaterial(spaceMaterial)
.setIndices(spaceIndices).setAttribute('POSITION', position).setAttribute('TEXCOORD_0', uv);
const mesh = doc.createMesh('result').addPrimitive(moonPrimitive).addPrimitive(spacePrimitive);
const node = doc.createNode().setMesh(mesh);
const scene = doc.createScene().addChild(node);

async function loadTexture(texture, uri) {
const response = await fetch(uri);
const blob = await response.blob();
texture.setMimeType(blob.type);
texture.setImage(new Uint8Array(await blob.arrayBuffer()));
}

async function loadTextures() {
const moonLoads = loadTexture(moonTexture, './moonAltitude.jpg');
const spaceLoads = loadTexture(spaceTexture, './hubbleDeepField.jpg');
await Promise.all([moonLoads, spaceLoads]);
}

const texturesLoad = loadTextures();
const materialMap = new Map();
const spaceMaterial = doc.createMaterial('space').setBaseColorTexture(spaceTexture);
const moonLoads = loadTexture(moonTexture, './moonAltitude.jpg');
const spaceLoads = loadTexture(spaceTexture, './hubbleDeepField.jpg');
const texturesLoad = Promise.all([moonLoads, spaceLoads]);

const wasm = await Module();
wasm.setup();

const firstID = wasm.reserveIDs(2);
const space = cubeUV(firstID).scale(50);
const moon = sphereUV(firstID + 1).scale(60);
materialMap.set(firstID, spaceIndices);
materialMap.set(firstID + 1, moonIndices);
const materialMap = getMaterialMap();
materialMap.set(firstID, spaceMaterial);
materialMap.set(firstID + 1, moonMaterial);
const attributeArray = ['POSITION', 'TEXCOORD_0'];

const csg = function (operation) {
push2MV(wasm[operation](space, moon));
Expand All @@ -112,26 +99,11 @@

async function push2MV(manifold) {
// From Z-up to Y-up (glTF)
const mesh = manifold.rotate([-90, 0, 0]).getMesh();
const manifoldMesh = manifold.rotate([-90, 0, 0]).getMesh();

for (const [i, id] of mesh.runOriginalID.entries()) {
const indices = materialMap.get(id);
indices.setArray(mesh.triVerts.subarray(mesh.runIndex[i], mesh.runIndex[i + 1]));
}

const numVert = mesh.numVert;
const numProp = mesh.numProp;
const posArray = new Float32Array(3 * numVert);
const uvArray = new Float32Array(2 * numVert);
for (let i = 0; i < numVert; ++i) {
posArray[3 * i] = mesh.vertProperties[numProp * i];
posArray[3 * i + 1] = mesh.vertProperties[numProp * i + 1];
posArray[3 * i + 2] = mesh.vertProperties[numProp * i + 2];
uvArray[2 * i] = mesh.vertProperties[numProp * i + 3];
uvArray[2 * i + 1] = mesh.vertProperties[numProp * i + 4];
}
position.setArray(posArray);
uv.setArray(uvArray);
disposeMesh(node.getMesh());
const mesh = writeMesh(doc, manifoldMesh, attributeArray);
node.setMesh(mesh);

await texturesLoad;

Expand Down Expand Up @@ -168,24 +140,13 @@
const v4 = [0, 1, 2, 5];
const v6 = [0, 1, 2, 0, 2, 3];
const vertProperties = new Float32Array(numProp * numPropVert);
const mergeFromVert = new Uint32Array(numPropVert - numVert);
const mergeToVert = new Uint32Array(numPropVert - numVert);
const runOriginalID = new Uint32Array(1);
const meshUV = new wasm.Mesh({ numProp, triVerts, vertProperties, mergeFromVert, mergeToVert, runOriginalID });
const meshUV = new wasm.Mesh({ numProp, triVerts, vertProperties, runOriginalID });
runOriginalID[0] = id;
const vert2vert = [-1, -1, -1, -1, -1, -1, -1, -1];
let idx = 0;
for (let face = 0; face < 6; ++face) {
for (let v = 0; v < 4; ++v) {
const vIdx = 4 * face + v;
const vOld = triVerts[6 * face + v4[v]];
if (vert2vert[vOld] === -1) {
vert2vert[vOld] = vIdx;
} else {
mergeFromVert[idx] = vIdx;
mergeToVert[idx] = vert2vert[vOld];
++idx;
}
for (let i = 0; i < 3; ++i) {
vertProperties[numProp * vIdx + i] = vertPos[3 * vOld + i];
}
Expand All @@ -196,6 +157,7 @@
triVerts[6 * face + i] = 4 * face + v6[i];
}
}
meshUV.merge();
return wasm.Manifold(meshUV);
}

Expand All @@ -210,10 +172,8 @@
const numProp = 5;
const triVerts = new Uint32Array(3 * numTri);
const vertProperties = new Float32Array(numProp * (numVert + nMerge));
const mergeFromVert = new Uint32Array(nMerge);
const mergeToVert = new Uint32Array(nMerge);
const runOriginalID = new Uint32Array(1);
const meshUV = new wasm.Mesh({ numProp, triVerts, vertProperties, mergeFromVert, mergeToVert, runOriginalID });
const meshUV = new wasm.Mesh({ numProp, triVerts, vertProperties, runOriginalID });
runOriginalID[0] = id;
const vert2vert = new Uint32Array(numVert);
let idx = 0;
Expand All @@ -229,8 +189,6 @@
vertProperties[numProp * i + 4] = v;
if (Math.abs(y) < tol && x < -tol) {// Seam
const iNew = numVert + idx;
mergeFromVert[idx] = iNew;
mergeToVert[idx] = i;
vert2vert[i] = iNew;
vertProperties[numProp * iNew] = x;
vertProperties[numProp * iNew + 1] = y;
Expand Down Expand Up @@ -260,6 +218,7 @@
triVerts[3 * tri + 2] = v2;
}
}
meshUV.merge();
return wasm.Manifold(meshUV);
}
</script>
Expand Down
Loading