three-stdlib
Version:
stand-alone library of threejs examples
334 lines (333 loc) • 17.4 kB
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