320 lines
11 KiB
JavaScript
320 lines
11 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
const THREE = require("three");
|
|
const LoaderUtils = require("../_polyfill/LoaderUtils.cjs");
|
|
class PLYLoader extends THREE.Loader {
|
|
constructor(manager) {
|
|
super(manager);
|
|
this.propertyNameMapping = {};
|
|
}
|
|
load(url, onLoad, onProgress, onError) {
|
|
const scope = this;
|
|
const loader = new THREE.FileLoader(this.manager);
|
|
loader.setPath(this.path);
|
|
loader.setResponseType("arraybuffer");
|
|
loader.setRequestHeader(this.requestHeader);
|
|
loader.setWithCredentials(this.withCredentials);
|
|
loader.load(
|
|
url,
|
|
function(text) {
|
|
try {
|
|
onLoad(scope.parse(text));
|
|
} catch (e) {
|
|
if (onError) {
|
|
onError(e);
|
|
} else {
|
|
console.error(e);
|
|
}
|
|
scope.manager.itemError(url);
|
|
}
|
|
},
|
|
onProgress,
|
|
onError
|
|
);
|
|
}
|
|
setPropertyNameMapping(mapping) {
|
|
this.propertyNameMapping = mapping;
|
|
}
|
|
parse(data) {
|
|
function parseHeader(data2) {
|
|
const patternHeader = /ply([\s\S]*)end_header\r?\n/;
|
|
let headerText = "";
|
|
let headerLength = 0;
|
|
const result = patternHeader.exec(data2);
|
|
if (result !== null) {
|
|
headerText = result[1];
|
|
headerLength = new Blob([result[0]]).size;
|
|
}
|
|
const header = {
|
|
comments: [],
|
|
elements: [],
|
|
headerLength,
|
|
objInfo: ""
|
|
};
|
|
const lines = headerText.split("\n");
|
|
let currentElement;
|
|
function make_ply_element_property(propertValues, propertyNameMapping) {
|
|
const property = { type: propertValues[0] };
|
|
if (property.type === "list") {
|
|
property.name = propertValues[3];
|
|
property.countType = propertValues[1];
|
|
property.itemType = propertValues[2];
|
|
} else {
|
|
property.name = propertValues[1];
|
|
}
|
|
if (property.name in propertyNameMapping) {
|
|
property.name = propertyNameMapping[property.name];
|
|
}
|
|
return property;
|
|
}
|
|
for (let i = 0; i < lines.length; i++) {
|
|
let line = lines[i];
|
|
line = line.trim();
|
|
if (line === "")
|
|
continue;
|
|
const lineValues = line.split(/\s+/);
|
|
const lineType = lineValues.shift();
|
|
line = lineValues.join(" ");
|
|
switch (lineType) {
|
|
case "format":
|
|
header.format = lineValues[0];
|
|
header.version = lineValues[1];
|
|
break;
|
|
case "comment":
|
|
header.comments.push(line);
|
|
break;
|
|
case "element":
|
|
if (currentElement !== void 0) {
|
|
header.elements.push(currentElement);
|
|
}
|
|
currentElement = {};
|
|
currentElement.name = lineValues[0];
|
|
currentElement.count = parseInt(lineValues[1]);
|
|
currentElement.properties = [];
|
|
break;
|
|
case "property":
|
|
currentElement.properties.push(make_ply_element_property(lineValues, scope.propertyNameMapping));
|
|
break;
|
|
case "obj_info":
|
|
header.objInfo = line;
|
|
break;
|
|
default:
|
|
console.log("unhandled", lineType, lineValues);
|
|
}
|
|
}
|
|
if (currentElement !== void 0) {
|
|
header.elements.push(currentElement);
|
|
}
|
|
return header;
|
|
}
|
|
function parseASCIINumber(n, type) {
|
|
switch (type) {
|
|
case "char":
|
|
case "uchar":
|
|
case "short":
|
|
case "ushort":
|
|
case "int":
|
|
case "uint":
|
|
case "int8":
|
|
case "uint8":
|
|
case "int16":
|
|
case "uint16":
|
|
case "int32":
|
|
case "uint32":
|
|
return parseInt(n);
|
|
case "float":
|
|
case "double":
|
|
case "float32":
|
|
case "float64":
|
|
return parseFloat(n);
|
|
}
|
|
}
|
|
function parseASCIIElement(properties, line) {
|
|
const values = line.split(/\s+/);
|
|
const element = {};
|
|
for (let i = 0; i < properties.length; i++) {
|
|
if (properties[i].type === "list") {
|
|
const list = [];
|
|
const n = parseASCIINumber(values.shift(), properties[i].countType);
|
|
for (let j = 0; j < n; j++) {
|
|
list.push(parseASCIINumber(values.shift(), properties[i].itemType));
|
|
}
|
|
element[properties[i].name] = list;
|
|
} else {
|
|
element[properties[i].name] = parseASCIINumber(values.shift(), properties[i].type);
|
|
}
|
|
}
|
|
return element;
|
|
}
|
|
function parseASCII(data2, header) {
|
|
const buffer = {
|
|
indices: [],
|
|
vertices: [],
|
|
normals: [],
|
|
uvs: [],
|
|
faceVertexUvs: [],
|
|
colors: []
|
|
};
|
|
let result;
|
|
const patternBody = /end_header\s([\s\S]*)$/;
|
|
let body = "";
|
|
if ((result = patternBody.exec(data2)) !== null) {
|
|
body = result[1];
|
|
}
|
|
const lines = body.split("\n");
|
|
let currentElement = 0;
|
|
let currentElementCount = 0;
|
|
for (let i = 0; i < lines.length; i++) {
|
|
let line = lines[i];
|
|
line = line.trim();
|
|
if (line === "") {
|
|
continue;
|
|
}
|
|
if (currentElementCount >= header.elements[currentElement].count) {
|
|
currentElement++;
|
|
currentElementCount = 0;
|
|
}
|
|
const element = parseASCIIElement(header.elements[currentElement].properties, line);
|
|
handleElement(buffer, header.elements[currentElement].name, element);
|
|
currentElementCount++;
|
|
}
|
|
return postProcess(buffer);
|
|
}
|
|
function postProcess(buffer) {
|
|
let geometry2 = new THREE.BufferGeometry();
|
|
if (buffer.indices.length > 0) {
|
|
geometry2.setIndex(buffer.indices);
|
|
}
|
|
geometry2.setAttribute("position", new THREE.Float32BufferAttribute(buffer.vertices, 3));
|
|
if (buffer.normals.length > 0) {
|
|
geometry2.setAttribute("normal", new THREE.Float32BufferAttribute(buffer.normals, 3));
|
|
}
|
|
if (buffer.uvs.length > 0) {
|
|
geometry2.setAttribute("uv", new THREE.Float32BufferAttribute(buffer.uvs, 2));
|
|
}
|
|
if (buffer.colors.length > 0) {
|
|
geometry2.setAttribute("color", new THREE.Float32BufferAttribute(buffer.colors, 3));
|
|
}
|
|
if (buffer.faceVertexUvs.length > 0) {
|
|
geometry2 = geometry2.toNonIndexed();
|
|
geometry2.setAttribute("uv", new THREE.Float32BufferAttribute(buffer.faceVertexUvs, 2));
|
|
}
|
|
geometry2.computeBoundingSphere();
|
|
return geometry2;
|
|
}
|
|
function handleElement(buffer, elementName, element) {
|
|
if (elementName === "vertex") {
|
|
buffer.vertices.push(element.x, element.y, element.z);
|
|
if ("nx" in element && "ny" in element && "nz" in element) {
|
|
buffer.normals.push(element.nx, element.ny, element.nz);
|
|
}
|
|
if ("s" in element && "t" in element) {
|
|
buffer.uvs.push(element.s, element.t);
|
|
}
|
|
if ("red" in element && "green" in element && "blue" in element) {
|
|
buffer.colors.push(element.red / 255, element.green / 255, element.blue / 255);
|
|
}
|
|
} else if (elementName === "face") {
|
|
const vertex_indices = element.vertex_indices || element.vertex_index;
|
|
const texcoord = element.texcoord;
|
|
if (vertex_indices.length === 3) {
|
|
buffer.indices.push(vertex_indices[0], vertex_indices[1], vertex_indices[2]);
|
|
if (texcoord && texcoord.length === 6) {
|
|
buffer.faceVertexUvs.push(texcoord[0], texcoord[1]);
|
|
buffer.faceVertexUvs.push(texcoord[2], texcoord[3]);
|
|
buffer.faceVertexUvs.push(texcoord[4], texcoord[5]);
|
|
}
|
|
} else if (vertex_indices.length === 4) {
|
|
buffer.indices.push(vertex_indices[0], vertex_indices[1], vertex_indices[3]);
|
|
buffer.indices.push(vertex_indices[1], vertex_indices[2], vertex_indices[3]);
|
|
}
|
|
}
|
|
}
|
|
function binaryRead(dataview, at, type, little_endian) {
|
|
switch (type) {
|
|
case "int8":
|
|
case "char":
|
|
return [dataview.getInt8(at), 1];
|
|
case "uint8":
|
|
case "uchar":
|
|
return [dataview.getUint8(at), 1];
|
|
case "int16":
|
|
case "short":
|
|
return [dataview.getInt16(at, little_endian), 2];
|
|
case "uint16":
|
|
case "ushort":
|
|
return [dataview.getUint16(at, little_endian), 2];
|
|
case "int32":
|
|
case "int":
|
|
return [dataview.getInt32(at, little_endian), 4];
|
|
case "uint32":
|
|
case "uint":
|
|
return [dataview.getUint32(at, little_endian), 4];
|
|
case "float32":
|
|
case "float":
|
|
return [dataview.getFloat32(at, little_endian), 4];
|
|
case "float64":
|
|
case "double":
|
|
return [dataview.getFloat64(at, little_endian), 8];
|
|
}
|
|
}
|
|
function binaryReadElement(dataview, at, properties, little_endian) {
|
|
const element = {};
|
|
let result, read = 0;
|
|
for (let i = 0; i < properties.length; i++) {
|
|
if (properties[i].type === "list") {
|
|
const list = [];
|
|
result = binaryRead(dataview, at + read, properties[i].countType, little_endian);
|
|
const n = result[0];
|
|
read += result[1];
|
|
for (let j = 0; j < n; j++) {
|
|
result = binaryRead(dataview, at + read, properties[i].itemType, little_endian);
|
|
list.push(result[0]);
|
|
read += result[1];
|
|
}
|
|
element[properties[i].name] = list;
|
|
} else {
|
|
result = binaryRead(dataview, at + read, properties[i].type, little_endian);
|
|
element[properties[i].name] = result[0];
|
|
read += result[1];
|
|
}
|
|
}
|
|
return [element, read];
|
|
}
|
|
function parseBinary(data2, header) {
|
|
const buffer = {
|
|
indices: [],
|
|
vertices: [],
|
|
normals: [],
|
|
uvs: [],
|
|
faceVertexUvs: [],
|
|
colors: []
|
|
};
|
|
const little_endian = header.format === "binary_little_endian";
|
|
const body = new DataView(data2, header.headerLength);
|
|
let result, loc = 0;
|
|
for (let currentElement = 0; currentElement < header.elements.length; currentElement++) {
|
|
for (let currentElementCount = 0; currentElementCount < header.elements[currentElement].count; currentElementCount++) {
|
|
result = binaryReadElement(body, loc, header.elements[currentElement].properties, little_endian);
|
|
loc += result[1];
|
|
const element = result[0];
|
|
handleElement(buffer, header.elements[currentElement].name, element);
|
|
}
|
|
}
|
|
return postProcess(buffer);
|
|
}
|
|
let geometry;
|
|
const scope = this;
|
|
if (data instanceof ArrayBuffer) {
|
|
const text = LoaderUtils.decodeText(new Uint8Array(data));
|
|
const header = parseHeader(text);
|
|
geometry = header.format === "ascii" ? parseASCII(text, header) : parseBinary(data, header);
|
|
} else {
|
|
geometry = parseASCII(data, parseHeader(data));
|
|
}
|
|
return geometry;
|
|
}
|
|
}
|
|
exports.PLYLoader = PLYLoader;
|
|
//# sourceMappingURL=PLYLoader.cjs.map
|