UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

124 lines 4.8 kB
import { GLTFExporter } from "three/examples/jsm/exporters/GLTFExporter.js"; import GLTFMeshGPUInstancingExtension from "../../../include/three/EXT_mesh_gpu_instancing_exporter.js"; import { AnimationUtils } from "../../engine_animation.js"; import { registerExportExtensions } from "../../extensions/index.js"; import { __isExporting } from "../state.js"; import { shouldExport_HideFlags } from "../utils.js"; import { GizmoWriter as GLTFGizmoWriter, RenderTextureWriter as GLTFRenderTextureWriter } from "./Writers.js"; const DEFAULT_OPTIONS = { binary: true, animations: true, }; export async function exportAsGLTF(_opts) { if (!_opts.context) { throw new Error("No context provided to exportAsGLTF"); } if (!_opts.scene) { _opts.scene = _opts.context.scene; } const opts = { ...DEFAULT_OPTIONS, ..._opts }; const { context } = opts; const exporter = new GLTFExporter(); exporter.register(writer => new GLTFMeshGPUInstancingExtension(writer)); exporter.register(writer => new GLTFGizmoWriter(writer)); exporter.register(writer => new GLTFRenderTextureWriter(writer)); registerExportExtensions(exporter, opts.context); const exporterOptions = { binary: opts.binary, animations: collectAnimations(context, opts.scene, []), }; const state = new ExporterState(); console.debug("Exporting GLTF", exporterOptions); state.onBeforeExport(opts); __isExporting(true); const res = await exporter.parseAsync(opts.scene, exporterOptions).catch((e) => { console.error(e); return null; }); __isExporting(false); state.onAfterExport(opts); if (!res) { throw new Error("Failed to export GLTF"); } if (opts.downloadAs != undefined) { let blob = null; if (res instanceof ArrayBuffer) { blob = new Blob([res], { type: "application/octet-stream" }); } else { console.error("Can not download GLTF as a blob", res); } if (blob) { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; let name = opts.downloadAs; if (!name.endsWith(".glb") && !name.endsWith(".gltf")) { name += opts.binary ? ".glb" : ".gltf"; } a.download = name; a.click(); } } return res; } const ACTIONS_WEIGHT_KEY = Symbol("needle:weight"); class ExporterState { _undo = []; onBeforeExport(opts) { opts.context.animations.mixers.forEach(mixer => { const actions = AnimationUtils.tryGetActionsFromMixer(mixer); if (actions) { for (let i = 0; i < actions.length; i++) { const action = actions[i]; action[ACTIONS_WEIGHT_KEY] = action.weight; action.weight = 0; this._undo.push(() => { action.weight = action[ACTIONS_WEIGHT_KEY]; }); } } mixer.update(0); }); opts.context.scene.traverse(obj => { if (!shouldExport_HideFlags(obj)) { const parent = obj.parent; if (parent) { obj.removeFromParent(); this._undo.push(() => parent.add(obj)); } } }); } onAfterExport(_opts) { this._undo.forEach(fn => fn()); this._undo.length = 0; } } function collectAnimations(context, scene, clips) { // Get all animations that are used by any mixer in the scene // technically we might also collect animations here that aren't used by any object in the scene because they're part of another scene // But that's a problem for later... context.animations.mixers.forEach(mixer => { const actions = AnimationUtils.tryGetActionsFromMixer(mixer); if (actions) { for (let i = 0; i < actions.length; i++) { const action = actions[i]; const clip = action.getClip(); // TODO: might need to check if the clip is part of the scene that we want to export clips.push(clip); } } }); // Get all animations that are directly assigned to objects in the scene if (!Array.isArray(scene)) scene = [scene]; for (const obj of scene) { AnimationUtils.tryGetAnimationClipsFromObjectHierarchy(obj, clips); } // ensure we only have unique clips const uniqueClips = new Set(clips); return Array.from(uniqueClips); } //# sourceMappingURL=index.js.map