Skip to content

Commit

Permalink
Merge branch 'release/5.0.0'.
Browse files Browse the repository at this point in the history
  • Loading branch information
petrbroz committed Apr 9, 2024
2 parents 23836b6 + 77ff2a0 commit 1b0df50
Show file tree
Hide file tree
Showing 26 changed files with 692 additions and 376 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [5.0.0] - 2024-04-09

- Modified
- **[BREAKING CHANGE]** Library has been renamed from `forge-convert-utils` to `svf-utils`
- **[BREAKING CHANGE]** SVF readers and downloaders now expect an `IAuthenticationProvider` interface
for specifying how the requests to the Model Derivative service will be authenticated
- Changed branding from Forge to APS everywhere
- Migrated to the official APS SDKs

## [4.0.5] - 2023-09-29

- Added
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 Petr Broz
Copyright (c) 2024 Autodesk

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
133 changes: 45 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
# forge-convert-utils
# svf-utils

![Publish to NPM](https://github.com/petrbroz/forge-convert-utils/workflows/Publish%20to%20NPM/badge.svg)
[![npm version](https://badge.fury.io/js/forge-convert-utils.svg)](https://badge.fury.io/js/forge-convert-utils)
![node](https://img.shields.io/node/v/forge-convert-utils.svg)
![npm downloads](https://img.shields.io/npm/dw/forge-convert-utils.svg)
![Publish to NPM](https://github.com/petrbroz/svf-utils/workflows/Publish%20to%20NPM/badge.svg)
[![npm version](https://badge.fury.io/js/svf-utils.svg)](https://badge.fury.io/js/svf-utils)
![node](https://img.shields.io/node/v/svf-utils.svg)
![npm downloads](https://img.shields.io/npm/dw/svf-utils.svg)
![platforms](https://img.shields.io/badge/platform-windows%20%7C%20osx%20%7C%20linux-lightgray.svg)
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)

![Forge & glTF logos](./logo.png)
![APS & glTF logos](./logo.png)

Utilities for converting [Autodesk Forge](https://forge.autodesk.com) SVF file format into
Utilities for converting [Autodesk Platform Services](https://aps.autodesk.com) SVF file format into
[glTF 2.0](https://github.com/KhronosGroup/glTF/tree/master/specification/2.0).

> Check out [forge-convert-sqlite](https://github.com/petrbroz/forge-convert-sqlite) with an experimental
> serialization/deserialization of glTF to/from sqlite.
## Usage

### Command line

- install the package: `npm install --global forge-convert-utils`
- run the `forge-convert` command without parameters for usage info
- install the package: `npm install --global svf-utils`
- run the `svf-to-gltf` command without parameters for usage info
- run the command with a path to a local SVF file
- run the command with a Model Derivative URN (and optionally viewable GUID)
- to access Forge you must also specify credentials (`FORGE_CLIENT_ID` and `FORGE_CLIENT_SECRET`)
or an authentication token (`FORGE_ACCESS_TOKEN`) as env. variables
- to access APS you must also specify credentials (`APS_CLIENT_ID` and `APS_CLIENT_SECRET`)
or an authentication token (`APS_ACCESS_TOKEN`) as env. variables
- this will also download the property database in sqlite format
- optionally, use any combination of the following command line args:
- `--output-folder <folder>` to change output folder (by default '.')
Expand All @@ -38,125 +35,85 @@ Utilities for converting [Autodesk Forge](https://forge.autodesk.com) SVF file f
#### Unix/macOS

```
forge-convert <path to local svf> --output-folder <path to output folder>
svf-to-gltf <path to local svf> --output-folder <path to output folder>
```

or

```
export FORGE_CLIENT_ID=<client id>
export FORGE_CLIENT_SECRET=<client secret>
forge-convert <urn> --output-folder <path to output folder>
export APS_CLIENT_ID=<client id>
export APS_CLIENT_SECRET=<client secret>
svf-to-gltf <urn> --output-folder <path to output folder>
```

or

```
export FORGE_ACCESS_TOKEN=<access token>>
forge-convert <urn> --output-folder <path to output folder>
export APS_ACCESS_TOKEN=<access token>
svf-to-gltf <urn> --output-folder <path to output folder>
```

#### Windows

```
forge-convert <path to local svf> --output-folder <path to output folder>
svf-to-gltf <path to local svf> --output-folder <path to output folder>
```

or

```
set FORGE_CLIENT_ID=<client id>
set FORGE_CLIENT_SECRET=<client secret>
forge-convert <urn> --output-folder <path to output folder>
set APS_CLIENT_ID=<client id>
set APS_CLIENT_SECRET=<client secret>
svf-to-gltf <urn> --output-folder <path to output folder>
```

or

```
set FORGE_ACCESS_TOKEN=<access token>
forge-convert <urn> --output-folder <path to output folder>
set APS_ACCESS_TOKEN=<access token>
svf-to-gltf <urn> --output-folder <path to output folder>
```

### Node.js

The library can be used at different levels of granularity.

The easiest way to convert an SVF file is to read the entire model into memory
using [SvfReader#read](https://petrbroz.github.io/forge-convert-utils/docs/classes/_svf_reader_.reader.html#read)
method, and save the model into glTF using [GltfWriter#write](https://petrbroz.github.io/forge-convert-utils/docs/classes/_gltf_writer_.writer.html#write):

```js
const path = require('path');
const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils');
const { SvfReader, GltfWriter } = require('forge-convert-utils');

const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env;

async function run(urn, outputDir) {
const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET };
const modelDerivativeClient = new ModelDerivativeClient(auth);
const manifestHelper = new ManifestHelper(await modelDerivativeClient.getManifest(urn));
const derivatives = manifestHelper.search({ type: 'resource', role: 'graphics' });
const readerOptions = {
log: console.log
};
const writerOptions = {
deduplicate: true,
skipUnusedUvs: true,
center: true,
log: console.log,
filter: (dbid) => (dbid >= 100 && dbid <= 200) // only output objects with dbIDs between 100 and 200
};
const writer = new GltfWriter(writerOptions);
for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) {
const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth);
const scene = await reader.read(readerOptions);
await writer.write(scene, path.join(outputDir, derivative.guid));
}
}

run('your model urn', 'path/to/output/folder');
```
using [SvfReader#read](https://petrbroz.github.io/svf-utils/docs/classes/_svf_reader_.reader.html#read)
method, and save the model into glTF using [GltfWriter#write](https://petrbroz.github.io/svf-utils/docs/classes/_gltf_writer_.writer.html#write):
[samples/remote-svf-to-gltf.js](./samples/remote-svf-to-gltf.js).

If you don't want to read the entire model into memory (for example, when distributing
the parsing of an SVF over multiple servers), you can use methods like
[SvfReader#enumerateFragments](https://petrbroz.github.io/forge-convert-utils/docs/classes/_svf_reader_.reader.html#enumeratefragments)
or [SvfReader#enumerateGeometries](https://petrbroz.github.io/forge-convert-utils/docs/classes/_svf_reader_.reader.html#enumerategeometries)
[SvfReader#enumerateFragments](https://petrbroz.github.io/svf-utils/docs/classes/_svf_reader_.reader.html#enumeratefragments)
or [SvfReader#enumerateGeometries](https://petrbroz.github.io/svf-utils/docs/classes/_svf_reader_.reader.html#enumerategeometries)
to _asynchronously_ iterate over individual elements:

```js
const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils');
const { SvfReader } = require('forge-convert-utils');

const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env;

async function run (urn) {
const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET };
const modelDerivativeClient = new ModelDerivativeClient(auth);
const manifestHelper = new ManifestHelper(await modelDerivativeClient.getManifest(urn));
const derivatives = manifestHelper.search({ type: 'resource', role: 'graphics' });
for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) {
const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth);
for await (const fragment of reader.enumerateFragments()) {
console.log(fragment);
}
}
}
const { SvfReader } = require('svf-utils');

// ...

run('your model urn');
const reader = await SvfReader.FromDerivativeService(urn, guid, authProvider);
for await (const fragment of reader.enumerateFragments()) {
console.log(fragment);
}
```

And finally, if you already have the individual SVF assets in memory, you can parse the binary data
directly using _synchronous_ iterators like [parseMeshes](https://petrbroz.github.io/forge-convert-utils/docs/modules/_svf_meshes_.html#parsemeshes):
directly using _synchronous_ iterators like [parseMeshes](https://petrbroz.github.io/svf-utils/docs/modules/_svf_meshes_.html#parsemeshes):

```js
const { parseMeshes } = require('forge-convert-utils/lib/svf/meshes');
const { parseMeshes } = require('svf-utils/lib/svf/meshes');

// ...

for (const mesh of parseMeshes(buffer)) {
console.log(mesh);
}
```

> For additional examples, see the [test](./test) subfolder.
> For additional examples, see the [samples](./samples) subfolder.
### Customization

Expand All @@ -167,7 +124,7 @@ You can customize the translation by sub-classing the reader and/or the writer c

### Metadata

When converting models from [Model Derivative service](https://forge.autodesk.com/en/docs/model-derivative/v2),
When converting models from [Model Derivative service](https://aps.autodesk.com/en/docs/model-derivative/v2),
you can retrieve the model's properties and metadata in form of a sqlite database. The command line tool downloads
this database automatically as _properties.sqlite_ file directly in your output folder. If you're using this library
in your own Node.js code, you can find the database in the manifest by looking for an asset with type "resource",
Expand Down Expand Up @@ -213,7 +170,7 @@ See [./samples/local-svf-to-gltf.sh](./samples/local-svf-to-gltf.sh) or
- clone the repository
- install dependencies: `yarn install`
- build the library (transpile TypeScript): `yarn run build`
- run samples in the _test_ subfolder, for example: `FORGE_CLIENT_ID=<your client id> FORGE_CLIENT_SECRET=<your client secret> node test/remote-svf-to-gltf.js <model urn> <path to output folder>`
- run samples in the _test_ subfolder, for example: `APS_CLIENT_ID=<your client id> APS_CLIENT_SECRET=<your client secret> node test/remote-svf-to-gltf.js <model urn> <path to output folder>`

If you're using [Visual Studio Code](https://code.visualstudio.com), you can use the following "task" and "launch" configurations:

Expand Down Expand Up @@ -252,8 +209,8 @@ In _.vscode/launch.json_:
"program": "${workspaceFolder}/test/remote-svf-to-gltf.js",
"args": ["<your model urn>", "<path to output folder>"],
"env": {
"FORGE_CLIENT_ID": "<your client id>",
"FORGE_CLIENT_SECRET": "<your client secret>"
"APS_CLIENT_ID": "<your client id>",
"APS_CLIENT_SECRET": "<your client secret>"
},
"preLaunchTask": "build"
},
Expand Down
62 changes: 37 additions & 25 deletions bin/forge-convert.js → bin/svf-to-gltf.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

const program = require('commander');
const path = require('path');
const fse = require('fs-extra');
const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils');
const { SdkManagerBuilder } = require('@aps_sdk/autodesk-sdkmanager');
const { ModelDerivativeClient} = require('@aps_sdk/model-derivative');
const { Scopes } = require('@aps_sdk/authentication');
const { SvfReader, GltfWriter, BasicAuthenticationProvider, TwoLeggedAuthenticationProvider } = require('../lib');

const { SvfReader, GltfWriter } = require('..');

const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET, FORGE_ACCESS_TOKEN } = process.env;
let auth = null;
if (FORGE_ACCESS_TOKEN) {
auth = { token: FORGE_ACCESS_TOKEN };
} else if (FORGE_CLIENT_ID && FORGE_CLIENT_SECRET) {
auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET };
const { APS_CLIENT_ID, APS_CLIENT_SECRET, APS_ACCESS_TOKEN } = process.env;
let authenticationProvider = null;
if (APS_ACCESS_TOKEN) {
authenticationProvider = new BasicAuthenticationProvider(APS_ACCESS_TOKEN);
} else if (APS_CLIENT_ID && APS_CLIENT_SECRET) {
authenticationProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET);
}

async function convertRemote(urn, guid, outputFolder, options) {
console.log(`Converting urn ${urn}, guid ${guid}`);
const reader = await SvfReader.FromDerivativeService(urn, guid, auth);
const reader = await SvfReader.FromDerivativeService(urn, guid, authenticationProvider);
const scene = await reader.read({ log: console.log });
const writer = new GltfWriter(options);
await writer.write(scene, path.join(outputFolder, guid));
Expand Down Expand Up @@ -59,31 +59,43 @@ program
} else {
// ID is the Model Derivative URN
// Convert input guid or all guids
if (!auth) {
console.warn('Missing environment variables for Autodesk Forge authentication.');
console.warn('Provide FORGE_CLIENT_ID and FORGE_CLIENT_SECRET, or FORGE_ACCESS_TOKEN.');
if (!authenticationProvider) {
console.warn('Missing environment variables for APS authentication.');
console.warn('Provide APS_CLIENT_ID and APS_CLIENT_SECRET, or APS_ACCESS_TOKEN.');
return;
}

const urn = id;
const client = new ModelDerivativeClient(auth);
const helper = new ManifestHelper(await client.getManifest(urn));
const folder = path.join(program.outputFolder, urn);
if (guid) {
await convertRemote(urn, guid, folder, options);
} else {
const derivatives = helper.search({ type: 'resource', role: 'graphics' });
for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) {
const sdkManager = SdkManagerBuilder.create().build();
const modelDerivativeClient = new ModelDerivativeClient(sdkManager);
const accessToken = await authenticationProvider.getToken([Scopes.ViewablesRead]);
const manifest = await modelDerivativeClient.getManifest(accessToken, urn);
const derivatives = [];
function traverse(derivative) {
if (derivative.type === 'resource' && derivative.role === 'graphics' && derivative.mime === 'application/autodesk-svf') {
derivatives.push(derivative);
}
if (derivative.children) {
for (const child of derivative.children) {
traverse(child);
}
}
}
for (const derivative of manifest.derivatives) {
if (derivative.children) {
for (const child of derivative.children) {
traverse(child);
}
}
}
for (const derivative of derivatives) {
await convertRemote(urn, derivative.guid, folder, options);
}
}

// Store the property database within the <urn> subfolder (it is shared by all viewables)
const pdbDerivatives = helper.search({ type: 'resource', role: 'Autodesk.CloudPlatform.PropertyDatabase' });
if (pdbDerivatives.length > 0) {
const databaseStream = client.getDerivativeChunked(urn, pdbDerivatives[0].urn, 1 << 20);
databaseStream.pipe(fse.createWriteStream(path.join(folder, 'properties.sqlite')));
}
}
} catch (err) {
console.error(err);
Expand Down
Binary file modified logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 12 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "forge-convert-utils",
"version": "4.0.5",
"description": "Tools for converting Autodesk Forge file formats.",
"name": "svf-utils",
"version": "5.0.0",
"description": "Tools for working with the SVF format used by Autodesk Platform Services.",
"main": "lib/index.js",
"bin": {
"forge-convert": "./bin/forge-convert.js"
"svf-to-gltf": "./bin/svf-to-gltf.js"
},
"engines": {
"node": ">=10.15.0"
Expand All @@ -16,19 +16,18 @@
"author": "Petr Broz <[email protected]>",
"license": "MIT",
"keywords": [
"autodesk",
"forge",
"autodesk-platform-services",
"gltf",
"typescript"
],
"repository": {
"type": "git",
"url": "git+https://github.com/petrbroz/forge-convert-utils.git"
"url": "git+https://github.com/petrbroz/svf-utils.git"
},
"bugs": {
"url": "https://github.com/petrbroz/forge-convert-utils/issues"
"url": "https://github.com/petrbroz/svf-utils/issues"
},
"homepage": "https://github.com/petrbroz/forge-convert-utils#readme",
"homepage": "https://github.com/petrbroz/svf-utils#readme",
"devDependencies": {
"@types/adm-zip": "^0.4.32",
"@types/fs-extra": "^9.0.13",
Expand All @@ -37,9 +36,12 @@
"typescript": "^4.5.5"
},
"dependencies": {
"@aps_sdk/authentication": "^0.1.0-beta.1",
"@aps_sdk/autodesk-sdkmanager": "^0.0.7-beta.1",
"@aps_sdk/model-derivative": "^0.1.0-beta.1",
"adm-zip": "^0.5.9",
"axios": "^1.6.8",
"commander": "^3.0.2",
"forge-server-utils": "^8.3.5",
"fs-extra": "^10.0.0"
}
}
Loading

0 comments on commit 1b0df50

Please sign in to comment.