334 lines
17 KiB
JavaScript
334 lines
17 KiB
JavaScript
"use strict";
|
|
var __defProp = Object.defineProperty;
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __publicField = (obj, key, value) => {
|
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
const THREE = require("three");
|
|
const uv1 = require("../_polyfill/uv1.cjs");
|
|
class ColladaExporter {
|
|
constructor() {
|
|
__publicField(this, "options");
|
|
__publicField(this, "geometryInfo");
|
|
__publicField(this, "materialMap");
|
|
__publicField(this, "imageMap");
|
|
__publicField(this, "textures");
|
|
__publicField(this, "libraryImages");
|
|
__publicField(this, "libraryGeometries");
|
|
__publicField(this, "libraryEffects");
|
|
__publicField(this, "libraryMaterials");
|
|
__publicField(this, "canvas");
|
|
__publicField(this, "ctx");
|
|
__publicField(this, "transMat");
|
|
__publicField(this, "getFuncs", ["getX", "getY", "getZ", "getW"]);
|
|
this.options = {
|
|
version: "1.4.1",
|
|
author: null,
|
|
textureDirectory: "",
|
|
upAxis: "Y_UP",
|
|
unitName: null,
|
|
unitMeter: null
|
|
};
|
|
this.geometryInfo = /* @__PURE__ */ new WeakMap();
|
|
this.materialMap = /* @__PURE__ */ new WeakMap();
|
|
this.imageMap = /* @__PURE__ */ new WeakMap();
|
|
this.textures = [];
|
|
this.libraryImages = [];
|
|
this.libraryGeometries = [];
|
|
this.libraryEffects = [];
|
|
this.libraryMaterials = [];
|
|
this.canvas = null;
|
|
this.ctx = null;
|
|
this.transMat = null;
|
|
}
|
|
parse(object, onDone, options = {}) {
|
|
this.options = { ...this.options, ...options };
|
|
if (this.options.upAxis.match(/^[XYZ]_UP$/) === null) {
|
|
console.error("ColladaExporter: Invalid upAxis: valid values are X_UP, Y_UP or Z_UP.");
|
|
return null;
|
|
}
|
|
if (this.options.unitName !== null && this.options.unitMeter === null) {
|
|
console.error("ColladaExporter: unitMeter needs to be specified if unitName is specified.");
|
|
return null;
|
|
}
|
|
if (this.options.unitMeter !== null && this.options.unitName === null) {
|
|
console.error("ColladaExporter: unitName needs to be specified if unitMeter is specified.");
|
|
return null;
|
|
}
|
|
if (this.options.textureDirectory !== "") {
|
|
this.options.textureDirectory = `${this.options.textureDirectory}/`.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
}
|
|
if (this.options.version !== "1.4.1" && this.options.version !== "1.5.0") {
|
|
console.warn(`ColladaExporter : Version ${this.options.version} not supported for export. Only 1.4.1 and 1.5.0.`);
|
|
return null;
|
|
}
|
|
const libraryVisualScenes = this.processObject(object);
|
|
const specLink = this.options.version === "1.4.1" ? "http://www.collada.org/2005/11/COLLADASchema" : "https://www.khronos.org/collada/";
|
|
let dae = `<?xml version="1.0" encoding="UTF-8" standalone="no" ?>${`<COLLADA xmlns="${specLink}" version="${this.options.version}">`}<asset><contributor><authoring_tool>three.js Collada Exporter</authoring_tool>${this.options.author !== null ? `<author>${this.options.author}</author>` : ""}</contributor>${`<created>${(/* @__PURE__ */ new Date()).toISOString()}</created>`}${`<modified>${(/* @__PURE__ */ new Date()).toISOString()}</modified>`}<up_axis>Y_UP</up_axis></asset>`;
|
|
dae += `<library_images>${this.libraryImages.join("")}</library_images>`;
|
|
dae += `<library_effects>${this.libraryEffects.join("")}</library_effects>`;
|
|
dae += `<library_materials>${this.libraryMaterials.join("")}</library_materials>`;
|
|
dae += `<library_geometries>${this.libraryGeometries.join("")}</library_geometries>`;
|
|
dae += `<library_visual_scenes><visual_scene id="Scene" name="scene">${libraryVisualScenes}</visual_scene></library_visual_scenes>`;
|
|
dae += '<scene><instance_visual_scene url="#Scene"/></scene>';
|
|
dae += "</COLLADA>";
|
|
const res = {
|
|
data: this.format(dae),
|
|
textures: this.textures
|
|
};
|
|
if (typeof onDone === "function") {
|
|
requestAnimationFrame(() => onDone(res));
|
|
}
|
|
return res;
|
|
}
|
|
// Convert the urdf xml into a well-formatted, indented format
|
|
format(urdf) {
|
|
var _a, _b;
|
|
const IS_END_TAG = /^<\//;
|
|
const IS_SELF_CLOSING = /(\?>$)|(\/>$)/;
|
|
const HAS_TEXT = /<[^>]+>[^<]*<\/[^<]+>/;
|
|
const pad = (ch, num) => num > 0 ? ch + pad(ch, num - 1) : "";
|
|
let tagnum = 0;
|
|
return (_b = (_a = urdf.match(/(<[^>]+>[^<]+<\/[^<]+>)|(<[^>]+>)/g)) == null ? void 0 : _a.map((tag) => {
|
|
if (!HAS_TEXT.test(tag) && !IS_SELF_CLOSING.test(tag) && IS_END_TAG.test(tag)) {
|
|
tagnum--;
|
|
}
|
|
const res = `${pad(" ", tagnum)}${tag}`;
|
|
if (!HAS_TEXT.test(tag) && !IS_SELF_CLOSING.test(tag) && !IS_END_TAG.test(tag)) {
|
|
tagnum++;
|
|
}
|
|
return res;
|
|
}).join("\n")) != null ? _b : "";
|
|
}
|
|
// Convert an image into a png format for saving
|
|
base64ToBuffer(str) {
|
|
const b = atob(str);
|
|
const buf = new Uint8Array(b.length);
|
|
for (let i = 0, l = buf.length; i < l; i++) {
|
|
buf[i] = b.charCodeAt(i);
|
|
}
|
|
return buf;
|
|
}
|
|
imageToData(image, ext) {
|
|
var _a;
|
|
this.canvas = this.canvas || document.createElement("canvas");
|
|
this.ctx = this.ctx || this.canvas.getContext("2d");
|
|
this.canvas.width = image.width instanceof SVGAnimatedLength ? 0 : image.width;
|
|
this.canvas.height = image.height instanceof SVGAnimatedLength ? 0 : image.height;
|
|
(_a = this.ctx) == null ? void 0 : _a.drawImage(image, 0, 0);
|
|
const base64data = this.canvas.toDataURL(`image/${ext}`, 1).replace(/^data:image\/(png|jpg);base64,/, "");
|
|
return this.base64ToBuffer(base64data);
|
|
}
|
|
// gets the attribute array. Generate a new array if the attribute is interleaved
|
|
attrBufferToArray(attr) {
|
|
if (attr instanceof THREE.InterleavedBufferAttribute && attr.isInterleavedBufferAttribute) {
|
|
const TypedArrayConstructor = attr.array.constructor;
|
|
const arr = new TypedArrayConstructor(attr.count * attr.itemSize);
|
|
const size = attr.itemSize;
|
|
for (let i = 0, l = attr.count; i < l; i++) {
|
|
for (let j = 0; j < size; j++) {
|
|
arr[i * size + j] = attr[this.getFuncs[j]](i);
|
|
}
|
|
}
|
|
return arr;
|
|
} else {
|
|
return attr.array;
|
|
}
|
|
}
|
|
// Returns an array of the same type starting at the `st` index,
|
|
// and `ct` length
|
|
subArray(arr, st, ct) {
|
|
if (Array.isArray(arr)) {
|
|
return arr.slice(st, st + ct);
|
|
} else {
|
|
const TypedArrayConstructor = arr.constructor;
|
|
return new TypedArrayConstructor(arr.buffer, st * arr.BYTES_PER_ELEMENT, ct);
|
|
}
|
|
}
|
|
// Returns the string for a geometry's attribute
|
|
getAttribute(attr, name, params, type) {
|
|
const array = this.attrBufferToArray(attr);
|
|
const res = Array.isArray(array) ? `${`<source id="${name}"><float_array id="${name}-array" count="${array.length}">` + array.join(" ")}</float_array><technique_common>${`<accessor source="#${name}-array" count="${Math.floor(
|
|
array.length / attr.itemSize
|
|
)}" stride="${attr.itemSize}">`}${params.map((n) => `<param name="${n}" type="${type}" />`).join("")}</accessor></technique_common></source>` : "";
|
|
return res;
|
|
}
|
|
// Returns the string for a node's transform information
|
|
getTransform(o) {
|
|
o.updateMatrix();
|
|
this.transMat = this.transMat || new THREE.Matrix4();
|
|
this.transMat.copy(o.matrix);
|
|
this.transMat.transpose();
|
|
return `<matrix>${this.transMat.toArray().join(" ")}</matrix>`;
|
|
}
|
|
// Process the given piece of geometry into the geometry library
|
|
// Returns the mesh id
|
|
processGeometry(g) {
|
|
let info = this.geometryInfo.get(g);
|
|
if (!info) {
|
|
const bufferGeometry = g;
|
|
if (!bufferGeometry.isBufferGeometry) {
|
|
throw new Error("THREE.ColladaExporter: Geometry is not of type THREE.BufferGeometry.");
|
|
}
|
|
const meshid = `Mesh${this.libraryGeometries.length + 1}`;
|
|
const indexCount = bufferGeometry.index ? bufferGeometry.index.count * bufferGeometry.index.itemSize : bufferGeometry.attributes.position.count;
|
|
const groups = bufferGeometry.groups != null && bufferGeometry.groups.length !== 0 ? bufferGeometry.groups : [{ start: 0, count: indexCount, materialIndex: 0 }];
|
|
const gname = g.name ? ` name="${g.name}"` : "";
|
|
let gnode = `<geometry id="${meshid}"${gname}><mesh>`;
|
|
const posName = `${meshid}-position`;
|
|
const vertName = `${meshid}-vertices`;
|
|
gnode += this.getAttribute(bufferGeometry.attributes.position, posName, ["X", "Y", "Z"], "float");
|
|
gnode += `<vertices id="${vertName}"><input semantic="POSITION" source="#${posName}" /></vertices>`;
|
|
let triangleInputs = `<input semantic="VERTEX" source="#${vertName}" offset="0" />`;
|
|
if ("normal" in bufferGeometry.attributes) {
|
|
const normName = `${meshid}-normal`;
|
|
gnode += this.getAttribute(bufferGeometry.attributes.normal, normName, ["X", "Y", "Z"], "float");
|
|
triangleInputs += `<input semantic="NORMAL" source="#${normName}" offset="0" />`;
|
|
}
|
|
if ("uv" in bufferGeometry.attributes) {
|
|
const uvName = `${meshid}-texcoord`;
|
|
gnode += this.getAttribute(bufferGeometry.attributes.uv, uvName, ["S", "T"], "float");
|
|
triangleInputs += `<input semantic="TEXCOORD" source="#${uvName}" offset="0" set="0" />`;
|
|
}
|
|
if (uv1.UV1 in bufferGeometry.attributes) {
|
|
const uvName = `${meshid}-texcoord2`;
|
|
gnode += this.getAttribute(bufferGeometry.attributes[uv1.UV1], uvName, ["S", "T"], "float");
|
|
triangleInputs += `<input semantic="TEXCOORD" source="#${uvName}" offset="0" set="1" />`;
|
|
}
|
|
if ("color" in bufferGeometry.attributes) {
|
|
const colName = `${meshid}-color`;
|
|
gnode += this.getAttribute(bufferGeometry.attributes.color, colName, ["X", "Y", "Z"], "uint8");
|
|
triangleInputs += `<input semantic="COLOR" source="#${colName}" offset="0" />`;
|
|
}
|
|
let indexArray = null;
|
|
if (bufferGeometry.index) {
|
|
indexArray = this.attrBufferToArray(bufferGeometry.index);
|
|
} else {
|
|
indexArray = new Array(indexCount);
|
|
for (let i = 0, l = indexArray.length; i < l && Array.isArray(indexArray); i++)
|
|
indexArray[i] = i;
|
|
}
|
|
for (let i = 0, l = groups.length; i < l; i++) {
|
|
const group = groups[i];
|
|
const subarr = this.subArray(indexArray, group.start, group.count);
|
|
const polycount = subarr.length / 3;
|
|
gnode += `<triangles material="MESH_MATERIAL_${group.materialIndex}" count="${polycount}">`;
|
|
gnode += triangleInputs;
|
|
gnode += `<p>${subarr.join(" ")}</p>`;
|
|
gnode += "</triangles>";
|
|
}
|
|
gnode += "</mesh></geometry>";
|
|
this.libraryGeometries.push(gnode);
|
|
info = { meshid, bufferGeometry };
|
|
this.geometryInfo.set(g, info);
|
|
}
|
|
return info;
|
|
}
|
|
// Process the given texture into the image library
|
|
// Returns the image library
|
|
processTexture(tex) {
|
|
let texid = this.imageMap.get(tex);
|
|
if (texid == null) {
|
|
texid = `image-${this.libraryImages.length + 1}`;
|
|
const ext = "png";
|
|
const name = tex.name || texid;
|
|
let imageNode = `<image id="${texid}" name="${name}">`;
|
|
if (this.options.version === "1.5.0") {
|
|
imageNode += `<init_from><ref>${this.options.textureDirectory}${name}.${ext}</ref></init_from>`;
|
|
} else {
|
|
imageNode += `<init_from>${this.options.textureDirectory}${name}.${ext}</init_from>`;
|
|
}
|
|
imageNode += "</image>";
|
|
this.libraryImages.push(imageNode);
|
|
this.imageMap.set(tex, texid);
|
|
this.textures.push({
|
|
directory: this.options.textureDirectory,
|
|
name,
|
|
ext,
|
|
data: this.imageToData(tex.image, ext),
|
|
original: tex
|
|
});
|
|
}
|
|
return texid;
|
|
}
|
|
// Process the given material into the material and effect libraries
|
|
// Returns the material id
|
|
processMaterial(m) {
|
|
let matid = this.materialMap.get(m);
|
|
if (matid == null) {
|
|
matid = `Mat${this.libraryEffects.length + 1}`;
|
|
let type = "phong";
|
|
if (m instanceof THREE.MeshLambertMaterial) {
|
|
type = "lambert";
|
|
} else if (m instanceof THREE.MeshBasicMaterial) {
|
|
type = "constant";
|
|
if (m.map !== null) {
|
|
console.warn("ColladaExporter: Texture maps not supported with MeshBasicMaterial.");
|
|
}
|
|
}
|
|
if (m instanceof THREE.MeshPhongMaterial) {
|
|
const emissive = m.emissive ? m.emissive : new THREE.Color(0, 0, 0);
|
|
const diffuse = m.color ? m.color : new THREE.Color(0, 0, 0);
|
|
const specular = m.specular ? m.specular : new THREE.Color(1, 1, 1);
|
|
const shininess = m.shininess || 0;
|
|
const reflectivity = m.reflectivity || 0;
|
|
let transparencyNode = "";
|
|
if (m.transparent) {
|
|
transparencyNode += `<transparent>${m.map ? '<texture texture="diffuse-sampler"></texture>' : "<float>1</float>"}</transparent>`;
|
|
if (m.opacity < 1) {
|
|
transparencyNode += `<transparency><float>${m.opacity}</float></transparency>`;
|
|
}
|
|
}
|
|
const techniqueNode = `${`<technique sid="common"><${type}>`}<emission>${m.emissiveMap ? '<texture texture="emissive-sampler" texcoord="TEXCOORD" />' : `<color sid="emission">${emissive.r} ${emissive.g} ${emissive.b} 1</color>`}</emission>${type !== "constant" ? `<diffuse>${m.map ? '<texture texture="diffuse-sampler" texcoord="TEXCOORD" />' : `<color sid="diffuse">${diffuse.r} ${diffuse.g} ${diffuse.b} 1</color>`}</diffuse>` : ""}${type !== "constant" ? `<bump>${m.normalMap ? '<texture texture="bump-sampler" texcoord="TEXCOORD" />' : ""}</bump>` : ""}${type === "phong" ? `${`<specular><color sid="specular">${specular.r} ${specular.g} ${specular.b} 1</color></specular>`}<shininess>${m.specularMap ? '<texture texture="specular-sampler" texcoord="TEXCOORD" />' : `<float sid="shininess">${shininess}</float>`}</shininess>` : ""}${`<reflective><color>${diffuse.r} ${diffuse.g} ${diffuse.b} 1</color></reflective>`}${`<reflectivity><float>${reflectivity}</float></reflectivity>`}${transparencyNode}${`</${type}></technique>`}`;
|
|
const effectnode = `${`<effect id="${matid}-effect">`}<profile_COMMON>${m.map ? `<newparam sid="diffuse-surface"><surface type="2D">${`<init_from>${this.processTexture(
|
|
m.map
|
|
)}</init_from>`}</surface></newparam><newparam sid="diffuse-sampler"><sampler2D><source>diffuse-surface</source></sampler2D></newparam>` : ""}${m.specularMap ? `<newparam sid="specular-surface"><surface type="2D">${`<init_from>${this.processTexture(
|
|
m.specularMap
|
|
)}</init_from>`}</surface></newparam><newparam sid="specular-sampler"><sampler2D><source>specular-surface</source></sampler2D></newparam>` : ""}${m.emissiveMap ? `<newparam sid="emissive-surface"><surface type="2D">${`<init_from>${this.processTexture(
|
|
m.emissiveMap
|
|
)}</init_from>`}</surface></newparam><newparam sid="emissive-sampler"><sampler2D><source>emissive-surface</source></sampler2D></newparam>` : ""}${m.normalMap ? `<newparam sid="bump-surface"><surface type="2D">${`<init_from>${this.processTexture(
|
|
m.normalMap
|
|
)}</init_from>`}</surface></newparam><newparam sid="bump-sampler"><sampler2D><source>bump-surface</source></sampler2D></newparam>` : ""}${techniqueNode}${m.side === THREE.DoubleSide ? '<extra><technique profile="THREEJS"><double_sided sid="double_sided" type="int">1</double_sided></technique></extra>' : ""}</profile_COMMON></effect>`;
|
|
const materialName = m.name ? ` name="${m.name}"` : "";
|
|
const materialNode = `<material id="${matid}"${materialName}><instance_effect url="#${matid}-effect" /></material>`;
|
|
this.libraryMaterials.push(materialNode);
|
|
this.libraryEffects.push(effectnode);
|
|
this.materialMap.set(m, matid);
|
|
}
|
|
}
|
|
return matid;
|
|
}
|
|
// Recursively process the object into a scene
|
|
processObject(o) {
|
|
let node = `<node name="${o.name}">`;
|
|
node += this.getTransform(o);
|
|
const a = new THREE.Mesh();
|
|
a.geometry;
|
|
if (o instanceof THREE.Mesh && o.isMesh && o.geometry !== null) {
|
|
const geomInfo = this.processGeometry(o.geometry);
|
|
const meshid = geomInfo.meshid;
|
|
const geometry = geomInfo.bufferGeometry;
|
|
let matids = null;
|
|
let matidsArray;
|
|
const mat = o.material || new THREE.MeshBasicMaterial();
|
|
const materials = Array.isArray(mat) ? mat : [mat];
|
|
if (geometry.groups.length > materials.length) {
|
|
matidsArray = new Array(geometry.groups.length);
|
|
} else {
|
|
matidsArray = new Array(materials.length);
|
|
}
|
|
matids = matidsArray.fill(null).map((_, i) => this.processMaterial(materials[i % materials.length]));
|
|
node += `${`<instance_geometry url="#${meshid}">` + (matids != null ? `<bind_material><technique_common>${matids.map(
|
|
(id, i) => `${`<instance_material symbol="MESH_MATERIAL_${i}" target="#${id}" >`}<bind_vertex_input semantic="TEXCOORD" input_semantic="TEXCOORD" input_set="0" /></instance_material>`
|
|
).join("")}</technique_common></bind_material>` : "")}</instance_geometry>`;
|
|
}
|
|
o.children.forEach((c) => node += this.processObject(c));
|
|
node += "</node>";
|
|
return node;
|
|
}
|
|
}
|
|
exports.ColladaExporter = ColladaExporter;
|
|
//# sourceMappingURL=ColladaExporter.cjs.map
|