Skip to content

Commit

Permalink
Update examples from JS -> TS (#807)
Browse files Browse the repository at this point in the history
* put types back

* updated MV example to TS

* fixed TS errors

* added docs

* switch make-manifold to TS

* fix TS errors

* added docs

* update MV

* fix TS build

* forgot declaration
  • Loading branch information
elalish authored May 10, 2024
1 parent 887f631 commit 6b4a4ed
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 256 deletions.
20 changes: 10 additions & 10 deletions bindings/wasm/examples/gltf-io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,12 @@ export function readMesh(mesh: Mesh, attributes: Attribute[] = []):
const manifoldPrimitive =
mesh.getExtension('EXT_mesh_manifold') as ManifoldPrimitive;

let vertPropArray: number[] = [];
let triVertArray: number[] = [];
let vertPropArray = Array<number>();
let triVertArray = Array<number>();
const runIndexArray = [0];
const mergeFromVertArray = [];
const mergeToVertArray = [];
const runProperties: Properties[] = [];
const mergeFromVertArray = Array<number>();
const mergeToVertArray = Array<number>();
const runProperties = Array<Properties>();
if (manifoldPrimitive != null) {
const numVert = primitives[0].getAttribute('POSITION')!.getCount();
const foundAttribute = attributes.map((a) => attributeDefs[a].type == null);
Expand Down Expand Up @@ -150,7 +150,7 @@ export function readMesh(mesh: Mesh, attributes: Attribute[] = []):
}
const mergeTriVert = manifoldPrimitive.getMergeIndices()?.getArray() ?? [];
const mergeTo = manifoldPrimitive.getMergeValues()?.getArray() ?? [];
const vert2merge = new Map();
const vert2merge = new Map<number, number>();
for (const [i, idx] of mergeTriVert.entries()) {
vert2merge.set(triVertArray[idx], mergeTo[i]);
}
Expand Down Expand Up @@ -218,8 +218,8 @@ export function writeMesh(
const manifoldExtension = doc.createExtension(EXTManifold);

const mesh = doc.createMesh();
const runIndex = [];
const attributeUnion: Attribute[] = [];
const runIndex = Array<number>();
const attributeUnion = Array<Attribute>();
const primitive2attributes = new Map<Primitive, Attribute[]>();
const numRun = manifoldMesh.runIndex.length - 1;
let lastID = -1;
Expand Down Expand Up @@ -323,8 +323,8 @@ export function writeMesh(
manifoldPrimitive.setRunIndex(runIndex);

const vert2merge = [...Array(manifoldMesh.numVert).keys()];
const ind = [];
const val = [];
const ind = Array<number>();
const val = Array<number>();
if (manifoldMesh.mergeFromVert && manifoldMesh.mergeToVert) {
for (const [i, from] of manifoldMesh.mergeFromVert.entries()) {
vert2merge[from] = manifoldMesh.mergeToVert[i];
Expand Down
4 changes: 2 additions & 2 deletions bindings/wasm/examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
<div id="editor" class="col"></div>
<div class="col container" style="flex-direction: column;">
<model-viewer camera-controls min-camera-orbit="auto 0deg auto" max-camera-orbit="auto 180deg auto"
shadow-intensity="1" tone-mapping="commerce" interaction-prompt="none" alt="Editor 3D output">
shadow-intensity="1" tone-mapping="neutral" interaction-prompt="none" alt="Editor 3D output">
<div class="center" slot="poster">
<p id="poster" style="text-align: center; line-height: 36px;"> Loading... </p>
</div>
Expand All @@ -95,6 +95,6 @@
integrity="sha512-iQEIc0rsSDujsfjtD+lfyJ1W23Bh/lbgriubKDAym6VlEIDRj9rrbSIyJRyshOrl8s0yRcQ0+gyrZfSLyjJGWQ=="
crossorigin="anonymous" referrerpolicy="no-referrer">
<script type="module" src="editor.js"></script>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.5.0/model-viewer.min.js"></script>

</html>
129 changes: 3 additions & 126 deletions bindings/wasm/examples/make-manifold.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,134 +55,11 @@
<button id="download" disabled>Download manifold GLB</button>
<input type="checkbox" id="viewFinal" disabled>
<label for="viewFinal">View Manifold GLB</label>
<model-viewer camera-controls shadow-intensity="1" tone-mapping="commerce" alt="Loaded GLB model">
<model-viewer camera-controls shadow-intensity="1" tone-mapping="neutral" alt="Loaded GLB model">
<span id="poster" slot="poster">Drop a GLB here</span>
</model-viewer>
</body>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
<script type="module">
import { WebIO } from '@gltf-transform/core';
import { prune } from '@gltf-transform/functions';
import { KHRONOS_EXTENSIONS } from '@gltf-transform/extensions';
import { SimpleDropzone } from 'simple-dropzone';
import { readMesh, writeMesh, setupIO, disposeMesh } from './gltf-io';
import Module from './built/manifold.js';

const io = setupIO(new WebIO());
io.registerExtensions(KHRONOS_EXTENSIONS);

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

const { Manifold, Mesh } = wasm;

const mv = document.querySelector('model-viewer');
const inputEl = document.querySelector('#input');
const downloadButton = document.querySelector('#download');
const checkbox = document.querySelector('#viewFinal');
const dropCtrl = new SimpleDropzone(mv, inputEl);

let inputGLBurl = null;
let outputGLBurl = null;
let allManifold = true;
let anyManifold = false;

dropCtrl.on('drop', async ({ files }) => {
for (const [path, file] of files) {
const filename = file.name.toLowerCase();
if (filename.match(/\.(gltf|glb)$/)) {
URL.revokeObjectURL(inputGLBurl);
inputGLBurl = URL.createObjectURL(file);
await writeGLB(await readGLB(inputGLBurl));
updateUI();
break;
}
}
});

function updateUI() {
if (allManifold) {
checkbox.checked = true;
checkbox.disabled = true;
} else if (anyManifold) {
checkbox.checked = false;
checkbox.disabled = false;
} else {
checkbox.checked = false;
checkbox.disabled = true;
}
onClick();
}

checkbox.onclick = onClick;

function onClick() {
mv.src = checkbox.checked ? outputGLBurl : inputGLBurl;
downloadButton.disabled = !checkbox.checked;
};

downloadButton.onclick = function () {
const link = document.createElement('a');
link.download = 'manifold.glb';
link.href = outputGLBurl;
link.click();
};

async function writeGLB(doc) {
URL.revokeObjectURL(outputGLBurl);
if (!anyManifold) {
return;
}
const glb = await io.writeBinary(doc);

const blob = new Blob([glb], { type: 'application/octet-stream' });
outputGLBurl = URL.createObjectURL(blob);
}

async function readGLB(url) {
allManifold = false;
anyManifold = false;
updateUI();
allManifold = true;
const docIn = await io.read(url);
const nodes = docIn.getRoot().listNodes();
for (const node of nodes) {
const mesh = node.getMesh();
if (!mesh) {
continue;
}

const tmp = readMesh(mesh);

tmp.mesh.runOriginalID = [];
const id2properties = new Map();
const numID = tmp.runProperties.length;
const firstID = Manifold.reserveIDs(numID);
for (let i = 0; i < numID; ++i) {
tmp.mesh.runOriginalID.push(firstID + i);
id2properties.set(firstID + i, tmp.runProperties[i]);
}
const manifoldMesh = new Mesh(tmp.mesh);
disposeMesh(mesh);

manifoldMesh.merge();

try {
// Test manifoldness - will throw if not.
const manifold = Manifold(manifoldMesh);
node.setMesh(writeMesh(docIn, manifold.getMesh(), id2properties));
manifold.delete();
anyManifold = true;
} catch (e) {
console.log(mesh.getName(), e);
allManifold = false;
}
}

await docIn.transform(prune());

return docIn;
}
</script>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.5.0/model-viewer.min.js"></script>
<script type="module" src="make-manifold.ts"></script>

</html>
149 changes: 149 additions & 0 deletions bindings/wasm/examples/make-manifold.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright 2024 The Manifold Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {Document, WebIO} from '@gltf-transform/core';
import {KHRONOS_EXTENSIONS} from '@gltf-transform/extensions';
import {prune} from '@gltf-transform/functions';
import {SimpleDropzone} from 'simple-dropzone';

import Module from './built/manifold.js';
import {disposeMesh, Properties, readMesh, setupIO, writeMesh} from './gltf-io';

// Set up gltf-transform
const io = setupIO(new WebIO());
io.registerExtensions(KHRONOS_EXTENSIONS);

// Set up Manifold WASM library
const wasm = await Module();
wasm.setup();
const {Manifold, Mesh} = wasm;

// UX elements
const mv = document.querySelector('model-viewer');
const inputEl = document.querySelector('#input') as HTMLInputElement;
const downloadButton = document.querySelector('#download') as HTMLButtonElement;
const checkbox = document.querySelector('#viewFinal') as HTMLInputElement;
const dropCtrl = new SimpleDropzone(mv, inputEl) as any;

// glTF objects in memory
let inputGLBurl = '';
let outputGLBurl = '';
// Status of manifoldness of all meshes
let allManifold = true;
let anyManifold = false;

// The processing is run when a glTF is drag-and-dropped onto this element.
dropCtrl.on('drop', async ({files}: {files: Map<string, File>}) => {
for (const [_path, file] of files) {
const filename = file.name.toLowerCase();
if (filename.match(/\.(gltf|glb)$/)) {
URL.revokeObjectURL(inputGLBurl);
inputGLBurl = URL.createObjectURL(file);
await writeGLB(await readGLB(inputGLBurl));
updateUI();
break;
}
}
});

// UI functions

function updateUI() {
if (allManifold) {
checkbox.checked = true;
checkbox.disabled = true;
} else if (anyManifold) {
checkbox.checked = false;
checkbox.disabled = false;
} else {
checkbox.checked = false;
checkbox.disabled = true;
}
onClick();
}

checkbox.onclick = onClick;

function onClick() {
(mv as any).src = checkbox.checked ? outputGLBurl : inputGLBurl;
downloadButton.disabled = !checkbox.checked;
};

downloadButton.onclick = () => {
const link = document.createElement('a');
link.download = 'manifold.glb';
link.href = outputGLBurl;
link.click();
};

// Write output glTF using gltf-transform, which contains only the meshes that
// are manifold, and using the EXT_mesh_manifold extension.
async function writeGLB(doc: Document): Promise<void> {
URL.revokeObjectURL(outputGLBurl);
if (!anyManifold) {
return;
}
const glb = await io.writeBinary(doc);

const blob = new Blob([glb], {type: 'application/octet-stream'});
outputGLBurl = URL.createObjectURL(blob);
}

// Read the glTF ObjectURL and return a gltf-transform document with all the
// non-manifold meshes stripped out.
async function readGLB(url: string): Promise<Document> {
allManifold = false;
anyManifold = false;
updateUI();
allManifold = true;
const docIn = await io.read(url);
const nodes = docIn.getRoot().listNodes();
for (const node of nodes) {
const mesh = node.getMesh();
if (!mesh) continue;

const tmp = readMesh(mesh);
if (!tmp) continue;

const id2properties = new Map<number, Properties>();
const numID = tmp.runProperties.length;
const firstID = Manifold.reserveIDs(numID);
tmp.mesh.runOriginalID = new Uint32Array(numID);
for (let i = 0; i < numID; ++i) {
tmp.mesh.runOriginalID[i] = firstID + i;
id2properties.set(firstID + i, tmp.runProperties[i]);
}
const manifoldMesh = new Mesh(tmp.mesh);
disposeMesh(mesh);
// Make the mesh manifold if it's close.
manifoldMesh.merge();

try {
// Test manifoldness - will throw if not.
const manifold = new Manifold(manifoldMesh);
// Replace the mesh with a manifold version
node.setMesh(writeMesh(docIn, manifold.getMesh(), id2properties));
manifold.delete();
anyManifold = true;
} catch (e) {
console.log(mesh.getName(), e);
allManifold = false;
}
}

// Prune the leftovers after non-manifold mesh removal.
await docIn.transform(prune());

return docIn;
}
2 changes: 1 addition & 1 deletion bindings/wasm/examples/manifold-gltf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export class EXTManifold extends Extension {

if (manifoldDef.manifoldPrimitive) {
let count = 0;
const runIndex = [];
const runIndex = Array<number>();
runIndex.push(count);
for (const primitive of mesh.listPrimitives()) {
const indices = primitive.getIndices();
Expand Down
Loading

0 comments on commit 6b4a4ed

Please sign in to comment.