Skip to content
Open
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
10 changes: 5 additions & 5 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ Named by noun.
- Behavior: Uses the Babylon scene loader to load an STL using NullEngine.
- `DracoEncoderBlock`
- Input: none
- Output: output (DracoEncoder)
- Output: output (GltfMeshCompressionOptions)
- Resources: Babylon default DracoEncoder
- Behavior: Attaches to `GltfOutputBlock`'s geometryCompression port. When present, sets the glTF export's `meshCompressionMethod` option to "Draco".
- Behavior: Prepares Babylon's default Draco encoder and provides `{ meshCompressionMethod: "Draco" }` as the per-export glTF mesh compression options that enable it. Connect its output to `GltfOutputBlock`'s `geometryCompressionOptions` port.

# Transforms

Expand All @@ -55,10 +55,10 @@ Named by verb.
Named by noun.

- `GltfOutputBlock`
- Inputs: input (BabylonScene), geometryCompressor (DracoEncoder, optional)
- Inputs: input (BabylonScene), geometryCompressionOptions (GltfMeshCompressionOptions, optional)
- Output: `File` which is a GLB
- Resources: Babylon glTF exporter
- Behavior: Uses GLBExport to export scene to GLB. Grabs GLB file from return of the GLBExport function. If geometryCompressor is truthy, sets meshCompressionMethod export option to "Draco".
- Behavior: Uses GLBExport to export the scene to GLB and passes through any supplied mesh compression options. If `geometryCompressionOptions` is not connected, the export remains uncompressed.

# Example: Hello, pipeline!

Expand Down Expand Up @@ -90,7 +90,7 @@ const destination = new GltfOutputBlock();

source.output.connectTo(compressTextures.input);
compressTextures.output.connectTo(destination.input);
dracoEncoder.output.connectTo(destination.geometryCompressor);
dracoEncoder.output.connectTo(destination.geometryCompressionOptions);

const asset = new NodeAsset({
name: "gltf-roundtrip",
Expand Down
33 changes: 0 additions & 33 deletions src/block/connectionPointType.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/block/block.ts → src/blocks/block.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ConnectionPointType, ConnectionPointValue } from "./connectionPointType";
import type { ConnectionPointType, ConnectionPointValue } from "../connectionPoints/connectionPoint";
import type { _AnyBlockDefinition, AuxiliaryInputDefinition, ConfigDefinition, ConfigValues } from "./blockDefinition";

/** @internal */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ConnectionPointType, ConnectionPointValue } from "./connectionPointType";
import type { ConnectionPointType, ConnectionPointValue } from "../connectionPoints/connectionPoint";
import type { ResourceDependencies, ResourceValues } from "../resources/resource";

declare const configType: unique symbol;
Expand Down
6 changes: 3 additions & 3 deletions src/blocks/compressTexturesBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import type { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial.js";
import type { Scene as BabylonScene } from "@babylonjs/core/scene.js";
import type sharpFactory from "sharp";

import { Block } from "../block/block";
import { defineBlock } from "../block/blockDefinition";
import { BabylonSceneType } from "../block/connectionPointType";
import { Block } from "./block";
import { defineBlock } from "./blockDefinition";
import { BabylonSceneType } from "../connectionPoints/babylonScene";

type CompressTexturesBlockDefinition = ReturnType<typeof createCompressTexturesBlockDefinition>;

Expand Down
16 changes: 7 additions & 9 deletions src/blocks/dracoEncoderBlock.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import type { IDracoCodecConfiguration } from "@babylonjs/core/Meshes/Compression/dracoCodec.js";
import type { DracoEncoder as BabylonDracoEncoder } from "@babylonjs/core/Meshes/Compression/dracoEncoder.js";

import { Block, type BlockOptions } from "../block/block";
import { defineSourceBlock } from "../block/blockDefinition";
import { DracoEncoderType } from "../block/connectionPointType";
import { createGltfMeshCompressionOptions, GltfMeshCompressionOptionsType, type GltfMeshCompressionOptions } from "../connectionPoints/gltfMeshCompressionOptions";
import { Block, type BlockOptions } from "./block";
import { defineSourceBlock } from "./blockDefinition";

const DracoEncoderBlockDefinition = /* @__PURE__ */ defineSourceBlock({
type: "input.draco-encoder",
output: DracoEncoderType,
runAsync: async () => {
output: GltfMeshCompressionOptionsType,
runAsync: async (): Promise<GltfMeshCompressionOptions> => {
const [{ DracoEncoder }, { RegisterKHR_draco_mesh_compression }] = await Promise.all([
import("@babylonjs/core/Meshes/Compression/dracoEncoder.js"),
import("@babylonjs/serializers/glTF/2.0/Extensions/KHR_draco_mesh_compression.pure.js"),
]);
await prepareDefaultEncoderForNodeAsync(DracoEncoder);
RegisterKHR_draco_mesh_compression();
return DracoEncoder.Default;
return createGltfMeshCompressionOptions({ meshCompressionMethod: "Draco" });
},
});

/** Provides Babylon.js's default Draco encoder for glTF geometry compression. */
/** Prepares Babylon.js's default Draco encoder and provides glTF mesh compression options that enable it. */
export class DracoEncoderBlock extends Block<typeof DracoEncoderBlockDefinition> {
public constructor(options?: BlockOptions<typeof DracoEncoderBlockDefinition>) {
super(DracoEncoderBlockDefinition, options);
Expand All @@ -28,7 +27,6 @@ export class DracoEncoderBlock extends Block<typeof DracoEncoderBlockDefinition>

interface DracoEncoderConstructor {
DefaultConfiguration: IDracoCodecConfiguration;
readonly Default: BabylonDracoEncoder;
ResetDefault(skipDispose?: boolean): void;
}

Expand Down
7 changes: 4 additions & 3 deletions src/blocks/fbxInputBlock.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Block, type BlockOptions } from "../block/block";
import { defineBlock } from "../block/blockDefinition";
import { BabylonSceneType, UrlType } from "../block/connectionPointType";
import { BabylonSceneType } from "../connectionPoints/babylonScene";
import { UrlType } from "../connectionPoints/url";
import { NullEngineResource } from "../resources/nullEngineResource";
import { Block, type BlockOptions } from "./block";
import { defineBlock } from "./blockDefinition";
import { loadSingleFileSceneWithPluginAsync } from "../helpers/loadSceneWithPlugin";

const FbxInputBlockDefinition = /* @__PURE__ */ defineBlock({
Expand Down
7 changes: 4 additions & 3 deletions src/blocks/gltfInputBlock.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Block, type BlockOptions } from "../block/block";
import { defineBlock } from "../block/blockDefinition";
import { BabylonSceneType, UrlType } from "../block/connectionPointType";
import { BabylonSceneType } from "../connectionPoints/babylonScene";
import { UrlType } from "../connectionPoints/url";
import { NullEngineResource } from "../resources/nullEngineResource";
import { Block, type BlockOptions } from "./block";
import { defineBlock } from "./blockDefinition";
import { fetchAsDataUriAsync, loadSingleFileSceneWithPluginAsync } from "../helpers/loadSceneWithPlugin";

const GltfInputBlockDefinition = /* @__PURE__ */ defineBlock({
Expand Down
25 changes: 11 additions & 14 deletions src/blocks/gltfOutputBlock.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,43 @@
import type { BaseTexture } from "@babylonjs/core/Materials/Textures/baseTexture.js";
import type { Scene as BabylonScene } from "@babylonjs/core/scene.js";

import { Block, type BlockOptions, type InputPort } from "../block/block";
import { defineBlock } from "../block/blockDefinition";
import { BabylonSceneType, DracoEncoderType, FileType } from "../block/connectionPointType";
import { BabylonSceneType } from "../connectionPoints/babylonScene";
import { FileType } from "../connectionPoints/file";
import { GltfMeshCompressionOptionsType, type GltfMeshCompressionOptions } from "../connectionPoints/gltfMeshCompressionOptions";
import { Block, type BlockOptions, type InputPort } from "./block";
import { defineBlock } from "./blockDefinition";

const GltfOutputBlockDefinition = /* @__PURE__ */ defineBlock({
type: "output.gltf",
input: BabylonSceneType,
auxiliaryInputs: {
geometryCompressor: DracoEncoderType,
geometryCompressionOptions: GltfMeshCompressionOptionsType,
},
output: FileType,
runAsync: (scene, _config, _resources, { geometryCompressor }) => serializeGlbAsync(scene, geometryCompressor === undefined ? undefined : "Draco"),
runAsync: (scene, _config, _resources, { geometryCompressionOptions }) => serializeGlbAsync(scene, geometryCompressionOptions),
});

/** Options for naming the block or supplying its initial scene input. */
export type GltfOutputBlockOptions = BlockOptions<typeof GltfOutputBlockDefinition>;

/** Serializes a Babylon.js scene to a binary glTF file. */
export class GltfOutputBlock extends Block<typeof GltfOutputBlockDefinition> {
public readonly geometryCompressor: InputPort<typeof DracoEncoderType>;
public readonly geometryCompressionOptions: InputPort<typeof GltfMeshCompressionOptionsType>;

public constructor(options?: GltfOutputBlockOptions) {
super(GltfOutputBlockDefinition, options);
this.geometryCompressor = this.auxiliaryInputs.geometryCompressor;
this.geometryCompressionOptions = this.auxiliaryInputs.geometryCompressionOptions;
}
}

async function serializeGlbAsync(scene: BabylonScene, meshCompressionMethod: "Draco" | undefined): Promise<File> {
async function serializeGlbAsync(scene: BabylonScene, geometryCompressionOptions: GltfMeshCompressionOptions | undefined): Promise<File> {
if (scene.textures.some(requiresTextureTransform)) {
const { RegisterKHR_texture_transform } = await import("@babylonjs/serializers/glTF/2.0/Extensions/KHR_texture_transform.pure.js");
RegisterKHR_texture_transform();
}
const { GLTF2Export } = await import("@babylonjs/serializers/glTF/2.0/glTFSerializer.js");
const fileName = "scene.glb";
const result =
meshCompressionMethod === undefined
? await GLTF2Export.GLBAsync(scene, fileName)
: await GLTF2Export.GLBAsync(scene, fileName, {
meshCompressionMethod,
});
const result = await GLTF2Export.GLBAsync(scene, fileName, geometryCompressionOptions);
const root = result.files[fileName];
if (!(root instanceof Blob)) {
throw new Error(`The Babylon glTF serializer did not produce "${fileName}".`);
Expand Down
7 changes: 4 additions & 3 deletions src/blocks/objInputBlock.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { BaseTexture } from "@babylonjs/core/Materials/Textures/baseTexture.js";
import type { Scene as BabylonScene } from "@babylonjs/core/scene.js";

import { Block, type BlockOptions } from "../block/block";
import { defineBlock } from "../block/blockDefinition";
import { BabylonSceneType, UrlType } from "../block/connectionPointType";
import { BabylonSceneType } from "../connectionPoints/babylonScene";
import { UrlType } from "../connectionPoints/url";
import { createDataUri, fetchOrThrowAsync, isHttpUrl, loadSceneWithPluginAsync } from "../helpers/loadSceneWithPlugin";
import { NullEngineResource } from "../resources/nullEngineResource";
import { Block, type BlockOptions } from "./block";
import { defineBlock } from "./blockDefinition";

const MaximumConcurrentTextureFetches = 8;

Expand Down
7 changes: 4 additions & 3 deletions src/blocks/stlInputBlock.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Block, type BlockOptions } from "../block/block";
import { defineBlock } from "../block/blockDefinition";
import { BabylonSceneType, UrlType } from "../block/connectionPointType";
import { BabylonSceneType } from "../connectionPoints/babylonScene";
import { UrlType } from "../connectionPoints/url";
import { NullEngineResource } from "../resources/nullEngineResource";
import { Block, type BlockOptions } from "./block";
import { defineBlock } from "./blockDefinition";
import { loadSingleFileSceneWithPluginAsync } from "../helpers/loadSceneWithPlugin";

const StlInputBlockDefinition = /* @__PURE__ */ defineBlock({
Expand Down
9 changes: 9 additions & 0 deletions src/connectionPoints/babylonScene.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Scene as BabylonScene } from "@babylonjs/core/scene.js";

import { defineConnectionPointType } from "./connectionPoint";

export const BabylonSceneType = /* @__PURE__ */ defineConnectionPointType<BabylonScene>(
"babylon-scene",
(value): value is BabylonScene =>
typeof value === "object" && value !== null && "getEngine" in value && typeof value.getEngine === "function" && "dispose" in value && typeof value.dispose === "function"
);
13 changes: 13 additions & 0 deletions src/connectionPoints/connectionPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
declare const connectionPointData: unique symbol;

export interface ConnectionPointType<Payload> {
readonly id: string;
readonly [connectionPointData]: Payload;
is(value: unknown): value is Payload;
}

export type ConnectionPointValue<C extends ConnectionPointType<unknown>> = C extends ConnectionPointType<infer TData> ? TData : never;

export function defineConnectionPointType<P>(id: string, isData: (value: unknown) => value is P): ConnectionPointType<P> {
return Object.freeze({ id, is: isData }) as ConnectionPointType<P>;
}
3 changes: 3 additions & 0 deletions src/connectionPoints/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineConnectionPointType } from "./connectionPoint";

export const FileType = /* @__PURE__ */ defineConnectionPointType<File>("file", (value): value is File => value instanceof File);
16 changes: 16 additions & 0 deletions src/connectionPoints/gltfMeshCompressionOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { IExportOptions } from "@babylonjs/serializers/glTF/2.0/glTFSerializer.js";

import { defineConnectionPointType } from "./connectionPoint";

export type GltfMeshCompressionOptions = Required<Pick<IExportOptions, "meshCompressionMethod">>;

const gltfMeshCompressionOptions = /* @__PURE__ */ Symbol("gltf-mesh-compression-options");

export function createGltfMeshCompressionOptions(options: GltfMeshCompressionOptions): GltfMeshCompressionOptions {
return Object.freeze(Object.defineProperty({ ...options }, gltfMeshCompressionOptions, { value: true }));
}

export const GltfMeshCompressionOptionsType = /* @__PURE__ */ defineConnectionPointType<GltfMeshCompressionOptions>(
"gltf-mesh-compression-options",
(value): value is GltfMeshCompressionOptions => typeof value === "object" && value !== null && gltfMeshCompressionOptions in value
);
3 changes: 3 additions & 0 deletions src/connectionPoints/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineConnectionPointType } from "./connectionPoint";

export const UrlType = /* @__PURE__ */ defineConnectionPointType<string>("url", (value): value is string => typeof value === "string");
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export { GltfInputBlock } from "./blocks/gltfInputBlock";
export { GltfOutputBlock, type GltfOutputBlockOptions } from "./blocks/gltfOutputBlock";
export { ObjInputBlock } from "./blocks/objInputBlock";
export { StlInputBlock } from "./blocks/stlInputBlock";
export { NodeAsset } from "./nodeAsset/nodeAsset";
export { NodeAssetContext } from "./nodeAsset/nodeAssetContext";
export type { GltfMeshCompressionOptions } from "./connectionPoints/gltfMeshCompressionOptions";
export { NodeAsset } from "./nodeAsset";
export { NodeAssetContext } from "./nodeAssetContext";
6 changes: 3 additions & 3 deletions src/nodeAsset/nodeAsset.ts → src/nodeAsset.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { _BlockRuntime } from "../block/block";
import type { ConnectionPointValue } from "../block/connectionPointType";
import type { _BlockRuntime } from "./blocks/block";
import type { ConnectionPointValue } from "./connectionPoints/connectionPoint";
import type { NodeAssetContext } from "./nodeAssetContext";
import { ResourceScope } from "../resources/resourceScope";
import { ResourceScope } from "./resources/resourceScope";

type AnyBlock = _BlockRuntime;

Expand Down
6 changes: 3 additions & 3 deletions src/nodeAsset/nodeAssetContext.ts → src/nodeAssetContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { _BlockRuntime, Block } from "../block/block";
import type { _InputBlockDefinition } from "../block/blockDefinition";
import type { ConnectionPointValue } from "../block/connectionPointType";
import type { _BlockRuntime, Block } from "./blocks/block";
import type { _InputBlockDefinition } from "./blocks/blockDefinition";
import type { ConnectionPointValue } from "./connectionPoints/connectionPoint";
import type { NodeAsset } from "./nodeAsset";

type AnyBlock = _BlockRuntime;
Expand Down
4 changes: 2 additions & 2 deletions tests/helpers/numberBlocks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineBlock, enumValue } from "../../src/block/blockDefinition";
import { defineConnectionPointType } from "../../src/block/connectionPointType";
import { defineBlock, enumValue } from "../../src/blocks/blockDefinition";
import { defineConnectionPointType } from "../../src/connectionPoints/connectionPoint";

export const NumberType = defineConnectionPointType<number>("number", (value): value is number => typeof value === "number");
export const OtherNumberType = defineConnectionPointType<number>("number", (value): value is number => typeof value === "number");
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/compressedGlbPipeline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe("compressed GLB pipeline", () => {

source.output.connectTo(compressTextures.input);
compressTextures.output.connectTo(destination.input);
dracoEncoder.output.connectTo(destination.geometryCompressor);
dracoEncoder.output.connectTo(destination.geometryCompressionOptions);

const asset = new NodeAsset({ name: "gltf-roundtrip", outputBlock: destination });
const result = await asset.executeAsync();
Expand Down
Loading