@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
120 lines (104 loc) • 3.55 kB
text/typescript
/**
* Base class of Exporter nodes
*
* @remarks
*/
import {BaseSopNodeType, TypedSopNode} from './_Base';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {BaseNodeType} from '../_Base';
import {InputCloneMode} from '../../poly/InputCloneMode';
import {CoreGroup} from '../../../core/geometry/Group';
import {Scene} from 'three';
import {Object3D} from 'three';
import {downloadBlob} from '../../../core/BlobUtils';
import {StringParam} from '../../params/String';
import {isString} from '../../../core/Type';
type Result = ArrayBuffer | string | object;
const previousParentByObject: WeakMap<Object3D, Object3D | null> = new WeakMap();
export class BaseExporterSopParamsConfig extends NodeParamsConfig {
/** @param fileName */
fileName = ParamConfig.STRING('`$OS`');
/** @param export */
download = ParamConfig.BUTTON(null, {
hidden: true,
callback: (node: BaseNodeType) => {
ExporterSopNode.PARAM_CALLBACK_download(node as ExporterSopNode<BaseExporterSopParamsConfig>);
},
});
}
export abstract class ExporterSopNode<K extends BaseExporterSopParamsConfig> extends TypedSopNode<K> {
abstract fileExtension(): string;
abstract createBlob(): Promise<Blob>;
override initializeNode() {
this.io.inputs.setCount(1);
this.io.inputs.initInputsClonedState(InputCloneMode.NEVER);
}
override async cook(inputCoreGroups: CoreGroup[]) {
this.setCoreGroup(inputCoreGroups[0]);
}
static PARAM_CALLBACK_download(node: ExporterSopNode<BaseExporterSopParamsConfig>) {
node._paramCallbackDownload();
}
async fileName() {
return await exporterSopFileName(this.p.fileName, this.fileExtension());
}
async _paramCallbackDownload() {
const blob = await this.createBlob();
const fileName = await this.fileName();
downloadBlob(blob, fileName);
}
protected async _prepareScene() {
return await exporterSopPrepareScene(this);
}
protected _handleResult(result: Result, objects: Object3D[], resolve: (blob: Blob) => void) {
exporterSopHandleResult(result, objects, resolve);
}
}
export async function exporterSopFileName(fileNameParam: StringParam, fileExtension: string) {
if (fileNameParam.isDirty()) {
await fileNameParam.compute();
}
const fileNameShort = fileNameParam.value;
const fileName = `${fileNameShort}.${fileExtension}`;
return fileName;
}
export async function exporterSopPrepareScene(node: BaseSopNodeType) {
const container = await node.compute();
const coreGroup = container.coreContent();
if (!coreGroup) {
console.error('input invalid');
return;
}
// save current parents
const objects = coreGroup.threejsObjects();
for (const object of objects) {
previousParentByObject.set(object, object.parent);
}
// add to exported scene
const scene = new Scene();
for (const object of objects) {
scene.add(object);
}
return {scene, objects};
}
function _createBlob(result: Result) {
if (result instanceof Uint8Array || result instanceof ArrayBuffer) {
return new Blob([result], {type: 'application/octet-stream'});
}
if (isString(result)) {
return new Blob([result], {type: 'text/plain'});
}
const output = JSON.stringify(result, null, 2);
return new Blob([output], {type: 'text/plain'});
}
export function exporterSopHandleResult(result: Result, objects: Object3D[], resolve: (blob: Blob) => void) {
const blob = _createBlob(result);
// restore parents
for (const object of objects) {
const previousParent = previousParentByObject.get(object);
if (previousParent) {
previousParent.add(object);
}
}
resolve(blob);
}