Export using THREE.GLTFExporter

This commit is contained in:
Robert Long 2018-07-18 15:57:03 -07:00
Родитель 3a94487f7c
Коммит e775a60003
7 изменённых файлов: 95 добавлений и 95 удалений

4
package-lock.json сгенерированный
Просмотреть файл

@ -12804,8 +12804,8 @@
}
},
"three": {
"version": "github:mozillareality/three.js#e86afa5bb698350c2ebddcda70b3e7929143444f",
"from": "github:mozillareality/three.js#hubs-editor/dev"
"version": "github:mozillareality/three.js#27dfd525502e876af2c8e6d195eb1579dc6bd2bc",
"from": "github:mozillareality/three.js#hubs/dev-v2"
},
"three-pathfinding": {
"version": "0.5.5",

Просмотреть файл

@ -51,7 +51,7 @@
"react-tooltip": "^3.6.1",
"selfsigned": "^1.10.3",
"signals": "^1.0.0",
"three": "github:mozillareality/three.js#hubs-editor/dev",
"three": "github:mozillareality/three.js#hubs/dev-v2",
"ws": "^5.2.0"
},
"devDependencies": {

Просмотреть файл

@ -22,11 +22,15 @@ export default class Project extends EventEmitter {
}
getUrl(relativePath) {
return new URL(`api/files/${relativePath}`, window.location).href;
return new URL(relativePath, this.serverUrl).href;
}
fetch(relativePath, options) {
return fetch(this.getUrl(relativePath), options);
}
async writeBlob(relativePath, blob) {
const res = await fetch(this.serverUrl + relativePath, {
const res = await this.fetch(relativePath, {
method: "POST",
body: blob
});
@ -37,7 +41,7 @@ export default class Project extends EventEmitter {
}
async readBlob(relativePath) {
const res = await fetch(this.serverUrl + relativePath);
const res = await this.fetch(relativePath);
const blob = await res.blob();
@ -45,7 +49,7 @@ export default class Project extends EventEmitter {
}
async readJSON(relativePath) {
const res = await fetch(this.serverUrl + relativePath);
const res = await this.fetch(relativePath);
const json = await res.json();
@ -53,9 +57,8 @@ export default class Project extends EventEmitter {
}
async writeJSON(relativePath, data) {
const res = await fetch(this.serverUrl + relativePath, {
const res = await this.fetch(relativePath, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
@ -65,7 +68,7 @@ export default class Project extends EventEmitter {
}
async mkdir(relativePath) {
const res = await fetch(this.serverUrl + relativePath + "?mkdir=true", { method: "POST" });
const res = await this.fetch(relativePath + "?mkdir=true", { method: "POST" });
const json = await res.json();
@ -73,7 +76,7 @@ export default class Project extends EventEmitter {
}
async openFile(relativePath) {
const res = await fetch(this.serverUrl + relativePath + "?open=true", {
const res = await this.fetch(relativePath + "?open=true", {
method: "POST"
});
@ -126,7 +129,7 @@ export default class Project extends EventEmitter {
}
async optimizeScene(sceneURI, outputURI) {
const res = await fetch(this.serverUrl + "/api/optimize", {
const res = await this.fetch("/api/optimize", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({

Просмотреть файл

@ -7,7 +7,7 @@ import RemoveObjectCommand from "./commands/RemoveObjectCommand";
import AddObjectCommand from "./commands/AddObjectCommand";
import { Components } from "./components";
import SceneReferenceComponent from "./components/SceneReferenceComponent";
import { loadScene, loadSerializedScene, serializeScene } from "./SceneLoader";
import { loadScene, loadSerializedScene, serializeScene, exportScene } from "./SceneLoader";
import DirectionalLightComponent from "./components/DirectionalLightComponent";
import AmbientLightComponent from "./components/AmbientLightComponent";
import ShadowComponent from "./components/ShadowComponent";
@ -313,6 +313,10 @@ export default class Editor {
return serializeScene(this.scene, sceneURI || this.scene.userData._uri);
}
exportScene() {
return exportScene(this.scene);
}
//
addObject(object, parent) {
@ -395,20 +399,6 @@ export default class Editor {
this.textures[texture.uuid] = texture;
}
exportScene() {
return new Promise((resolve, reject) => {
try {
const gltfExporter = new THREE.GLTFExporter();
gltfExporter.parseParts(this.scene, resolve, {
trs: true,
embedImages: false
});
} catch (e) {
reject(e);
}
});
}
//
addHelper = (function() {

Просмотреть файл

@ -62,48 +62,39 @@ function loadGLTF(url) {
});
}
function postProcessGLTF(scene, sceneURI, gltf) {
const { json } = gltf;
export async function exportScene(scene) {
// TODO: export animations
const chunks = await new Promise((resolve, reject) => {
new THREE.GLTFExporter().parseChunks(scene, resolve, reject, {
mode: "gltf",
onlyVisible: false
});
});
const buffers = json.buffers;
const buffers = chunks.json.buffers;
if (buffers && buffers.length > 0 && buffers[0].uri === undefined) {
buffers[0].uri = scene.name + ".bin";
}
const absoluteSceneURI = new URL(sceneURI, window.location).href;
if (Array.isArray(json.images)) {
for (const image of json.images) {
image.uri = absoluteToRelativeURL(absoluteSceneURI, image.uri);
}
}
const componentNames = Components.map(component => component.componentName);
for (const node of gltf.json.nodes) {
for (const node of chunks.json.nodes) {
if (!node.extras) continue;
if (!node.extensions) {
node.extensions = [];
}
for (const component of node.extras._components) {
if (componentNames.includes(component.name)) {
node.extensions.push(component);
if (node.extras._components) {
for (const component of node.extras._components) {
if (componentNames.includes(component.name)) {
node.extensions.push(component);
}
}
delete node.extras._components;
}
delete node.extras._components;
}
return gltf;
}
export function exportScene(scene, sceneURI) {
return new Promise(resolve => {
new THREE.GLTFExporter().parseParts(scene, resolve, {
embedImages: false,
onlyVisible: false
});
}).then(gltf => postProcessGLTF(scene, sceneURI, gltf));
return chunks;
}
function inflateGLTFComponents(scene, addComponent) {

Просмотреть файл

@ -279,22 +279,37 @@ class EditorContainer extends Component {
};
onExport = async outputPath => {
const scene = this.props.editor.scene;
const gltfPath = outputPath + "/" + scene.name + ".gltf";
const binPath = outputPath + "/" + scene.name + ".bin";
const { json, bin } = await exportScene(scene, gltfPath);
// Export current editor scene using THREE.GLTFExporter
const { json, buffers, images } = await this.props.editor.exportScene();
// Ensure the output directory exists
await this.props.project.mkdir(outputPath);
// Write the .gltf file
const scene = this.props.editor.scene;
const gltfPath = outputPath + "/" + scene.name + ".gltf";
await this.props.project.writeJSON(gltfPath, json);
if (bin) {
await this.props.project.writeBlob(binPath, bin);
// Write .bin files
for (const [index, buffer] of buffers.entries()) {
if (buffer !== undefined) {
const bufferName = json.buffers[index].uri;
await this.props.project.writeBlob(outputPath + "/" + bufferName, buffer);
}
}
// Write image files
for (const [index, image] of images.entries()) {
if (image !== undefined) {
const imageName = json.images[index].uri;
await this.props.project.writeBlob(outputPath + "/" + imageName, image);
}
}
// Run optimizations on .gltf and overwrite any existing files
await this.props.project.optimizeScene(gltfPath, gltfPath);
// Close modal
this.setState({ openModal: null });
};

Просмотреть файл

@ -12,8 +12,8 @@ import fs from "fs-extra";
import chokidar from "chokidar";
import debounce from "lodash.debounce";
import opn from "opn";
import generateUnlitTextures from "gltf-unlit-generator";
import { contentHashAndCopy } from "./gltf";
// import generateUnlitTextures from "gltf-unlit-generator";
async function getProjectHierarchy(projectPath) {
async function buildProjectNode(filePath, name, ext, isDirectory, uri) {
@ -183,45 +183,45 @@ export default async function startServer(options) {
)
);
router.post("/api/files/:filePath*", koaBody({ multipart: true }), async ctx => {
router.post("/api/files/:filePath*", async ctx => {
const filePath = ctx.params.filePath ? path.resolve(projectPath, ctx.params.filePath) : projectPath;
if (ctx.request.query.open) {
// Attempt to open file at filePath with the default application for that file type.
opn(filePath);
ctx.body = {
success: true
};
} else if (ctx.request.files && ctx.request.files.file) {
const file = ctx.request.files.file;
await fs.rename(file.path, filePath);
ctx.body = {
success: true
};
} else if (ctx.request.type === "application/json") {
await fs.writeJSON(filePath, ctx.request.body, { spaces: 2 });
ctx.body = {
success: true
};
} else if (ctx.request.type === "application/octet-stream") {
const bytes = await new Promise(resolve => {
ctx.req.on("readable", () => {
resolve(ctx.req.read());
});
});
await fs.writeFile(filePath, bytes);
ctx.body = { success: true };
} else if (ctx.request.query.mkdir) {
// Make the directory at filePath if it doesn't already exist.
await fs.ensureDir(filePath);
ctx.body = {
success: true
};
} else {
ctx.throw(400, "Invalid request");
// If uploading as text body, write it to filePath using the stream API.
const writeStream = fs.createWriteStream(filePath, { flags: "w" });
ctx.req.pipe(writeStream);
await new Promise((resolve, reject) => {
function cleanUp() {
// eslint-disable-next-line
writeStream.removeListener("finish", onFinish);
// eslint-disable-next-line
writeStream.removeListener("error", onError);
}
function onFinish() {
cleanUp();
resolve();
}
function onError(err) {
cleanUp();
reject(err);
}
writeStream.on("finish", onFinish);
writeStream.on("error", onError);
});
}
ctx.body = { success: true };
});
router.post("/api/optimize", koaBody(), async ctx => {
@ -236,11 +236,12 @@ export default async function startServer(options) {
const outputPath = path.resolve(projectPath, outputURI.replace("/api/files/", ""));
const outputDirPath = path.dirname(outputPath);
await generateUnlitTextures(scenePath, outputDirPath);
// TODO: fix unlit texture generation
// await generateUnlitTextures(scenePath, outputDirPath);
const json = await fs.readJSON(outputPath);
json.images = await contentHashAndCopy(json.images, sceneDirPath, outputDirPath);
json.images = await contentHashAndCopy(json.images, sceneDirPath, outputDirPath, true);
json.buffers = await contentHashAndCopy(json.buffers, sceneDirPath, outputDirPath, true);
await fs.writeJSON(outputPath, json);