(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three")); } else { var mod = { exports: {} }; factory(mod.exports, global.three); global.BufferGeometryUtils = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.computeMorphedAttributes = computeMorphedAttributes; _exports.computeTangents = computeTangents; _exports.estimateBytesUsed = estimateBytesUsed; _exports.interleaveAttributes = interleaveAttributes; _exports.mergeBufferAttributes = mergeBufferAttributes; _exports.mergeBufferGeometries = mergeBufferGeometries; _exports.mergeVertices = mergeVertices; _exports.toTrianglesDrawMode = toTrianglesDrawMode; function computeTangents(geometry) { geometry.computeTangents(); console.warn('THREE.BufferGeometryUtils: .computeTangents() has been removed. Use BufferGeometry.computeTangents() instead.'); } /** * @param {Array} geometries * @param {Boolean} useGroups * @return {BufferGeometry} */ function mergeBufferGeometries(geometries) { var useGroups = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var isIndexed = geometries[0].index !== null; var attributesUsed = new Set(Object.keys(geometries[0].attributes)); var morphAttributesUsed = new Set(Object.keys(geometries[0].morphAttributes)); var attributes = {}; var morphAttributes = {}; var morphTargetsRelative = geometries[0].morphTargetsRelative; var mergedGeometry = new _three.BufferGeometry(); var offset = 0; for (var i = 0; i < geometries.length; ++i) { var geometry = geometries[i]; var attributesCount = 0; // ensure that all geometries are indexed, or none if (isIndexed !== (geometry.index !== null)) { console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.'); return null; } // gather attributes, exit early if they're different for (var name in geometry.attributes) { if (!attributesUsed.has(name)) { console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.'); return null; } if (attributes[name] === undefined) attributes[name] = []; attributes[name].push(geometry.attributes[name]); attributesCount++; } // ensure geometries have the same number of attributes if (attributesCount !== attributesUsed.size) { console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.'); return null; } // gather morph attributes, exit early if they're different if (morphTargetsRelative !== geometry.morphTargetsRelative) { console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.'); return null; } for (var _name in geometry.morphAttributes) { if (!morphAttributesUsed.has(_name)) { console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.'); return null; } if (morphAttributes[_name] === undefined) morphAttributes[_name] = []; morphAttributes[_name].push(geometry.morphAttributes[_name]); } // gather .userData mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || []; mergedGeometry.userData.mergedUserData.push(geometry.userData); if (useGroups) { var count = void 0; if (isIndexed) { count = geometry.index.count; } else if (geometry.attributes.position !== undefined) { count = geometry.attributes.position.count; } else { console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute'); return null; } mergedGeometry.addGroup(offset, count, i); offset += count; } } // merge indices if (isIndexed) { var indexOffset = 0; var mergedIndex = []; for (var _i = 0; _i < geometries.length; ++_i) { var index = geometries[_i].index; for (var j = 0; j < index.count; ++j) { mergedIndex.push(index.getX(j) + indexOffset); } indexOffset += geometries[_i].attributes.position.count; } mergedGeometry.setIndex(mergedIndex); } // merge attributes for (var _name2 in attributes) { var mergedAttribute = mergeBufferAttributes(attributes[_name2]); if (!mergedAttribute) { console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + _name2 + ' attribute.'); return null; } mergedGeometry.setAttribute(_name2, mergedAttribute); } // merge morph attributes for (var _name3 in morphAttributes) { var numMorphTargets = morphAttributes[_name3][0].length; if (numMorphTargets === 0) break; mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {}; mergedGeometry.morphAttributes[_name3] = []; for (var _i2 = 0; _i2 < numMorphTargets; ++_i2) { var morphAttributesToMerge = []; for (var _j = 0; _j < morphAttributes[_name3].length; ++_j) { morphAttributesToMerge.push(morphAttributes[_name3][_j][_i2]); } var mergedMorphAttribute = mergeBufferAttributes(morphAttributesToMerge); if (!mergedMorphAttribute) { console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + _name3 + ' morphAttribute.'); return null; } mergedGeometry.morphAttributes[_name3].push(mergedMorphAttribute); } } return mergedGeometry; } /** * @param {Array} attributes * @return {BufferAttribute} */ function mergeBufferAttributes(attributes) { var TypedArray; var itemSize; var normalized; var arrayLength = 0; for (var i = 0; i < attributes.length; ++i) { var attribute = attributes[i]; if (attribute.isInterleavedBufferAttribute) { console.error('THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. InterleavedBufferAttributes are not supported.'); return null; } if (TypedArray === undefined) TypedArray = attribute.array.constructor; if (TypedArray !== attribute.array.constructor) { console.error('THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes.'); return null; } if (itemSize === undefined) itemSize = attribute.itemSize; if (itemSize !== attribute.itemSize) { console.error('THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes.'); return null; } if (normalized === undefined) normalized = attribute.normalized; if (normalized !== attribute.normalized) { console.error('THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes.'); return null; } arrayLength += attribute.array.length; } var array = new TypedArray(arrayLength); var offset = 0; for (var _i3 = 0; _i3 < attributes.length; ++_i3) { array.set(attributes[_i3].array, offset); offset += attributes[_i3].array.length; } return new _three.BufferAttribute(array, itemSize, normalized); } /** * @param {Array} attributes * @return {Array} */ function interleaveAttributes(attributes) { // Interleaves the provided attributes into an InterleavedBuffer and returns // a set of InterleavedBufferAttributes for each attribute var TypedArray; var arrayLength = 0; var stride = 0; // calculate the the length and type of the interleavedBuffer for (var i = 0, l = attributes.length; i < l; ++i) { var attribute = attributes[i]; if (TypedArray === undefined) TypedArray = attribute.array.constructor; if (TypedArray !== attribute.array.constructor) { console.error('AttributeBuffers of different types cannot be interleaved'); return null; } arrayLength += attribute.array.length; stride += attribute.itemSize; } // Create the set of buffer attributes var interleavedBuffer = new _three.InterleavedBuffer(new TypedArray(arrayLength), stride); var offset = 0; var res = []; var getters = ['getX', 'getY', 'getZ', 'getW']; var setters = ['setX', 'setY', 'setZ', 'setW']; for (var j = 0, _l = attributes.length; j < _l; j++) { var _attribute = attributes[j]; var itemSize = _attribute.itemSize; var count = _attribute.count; var iba = new _three.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, _attribute.normalized); res.push(iba); offset += itemSize; // Move the data for each attribute into the new interleavedBuffer // at the appropriate offset for (var c = 0; c < count; c++) { for (var k = 0; k < itemSize; k++) { iba[setters[k]](c, _attribute[getters[k]](c)); } } } return res; } /** * @param {Array} geometry * @return {number} */ function estimateBytesUsed(geometry) { // Return the estimated memory used by this geometry in bytes // Calculate using itemSize, count, and BYTES_PER_ELEMENT to account // for InterleavedBufferAttributes. var mem = 0; for (var name in geometry.attributes) { var attr = geometry.getAttribute(name); mem += attr.count * attr.itemSize * attr.array.BYTES_PER_ELEMENT; } var indices = geometry.getIndex(); mem += indices ? indices.count * indices.itemSize * indices.array.BYTES_PER_ELEMENT : 0; return mem; } /** * @param {BufferGeometry} geometry * @param {number} tolerance * @return {BufferGeometry>} */ function mergeVertices(geometry) { var tolerance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1e-4; tolerance = Math.max(tolerance, Number.EPSILON); // Generate an index buffer if the geometry doesn't have one, or optimize it // if it's already available. var hashToIndex = {}; var indices = geometry.getIndex(); var positions = geometry.getAttribute('position'); var vertexCount = indices ? indices.count : positions.count; // next value for triangle indices var nextIndex = 0; // attributes and new attribute arrays var attributeNames = Object.keys(geometry.attributes); var attrArrays = {}; var morphAttrsArrays = {}; var newIndices = []; var getters = ['getX', 'getY', 'getZ', 'getW']; // initialize the arrays for (var i = 0, l = attributeNames.length; i < l; i++) { var name = attributeNames[i]; attrArrays[name] = []; var morphAttr = geometry.morphAttributes[name]; if (morphAttr) { morphAttrsArrays[name] = new Array(morphAttr.length).fill().map(function () { return []; }); } } // convert the error tolerance to an amount of decimal places to truncate to var decimalShift = Math.log10(1 / tolerance); var shiftMultiplier = Math.pow(10, decimalShift); for (var _i4 = 0; _i4 < vertexCount; _i4++) { var index = indices ? indices.getX(_i4) : _i4; // Generate a hash for the vertex attributes at the current index 'i' var hash = ''; for (var j = 0, _l2 = attributeNames.length; j < _l2; j++) { var _name4 = attributeNames[j]; var attribute = geometry.getAttribute(_name4); var itemSize = attribute.itemSize; for (var k = 0; k < itemSize; k++) { // double tilde truncates the decimal value hash += "".concat(~~(attribute[getters[k]](index) * shiftMultiplier), ","); } } // Add another reference to the vertex if it's already // used by another index if (hash in hashToIndex) { newIndices.push(hashToIndex[hash]); } else { // copy data to the new index in the attribute arrays for (var _j2 = 0, _l3 = attributeNames.length; _j2 < _l3; _j2++) { var _name5 = attributeNames[_j2]; var _attribute2 = geometry.getAttribute(_name5); var _morphAttr = geometry.morphAttributes[_name5]; var _itemSize = _attribute2.itemSize; var newarray = attrArrays[_name5]; var newMorphArrays = morphAttrsArrays[_name5]; for (var _k = 0; _k < _itemSize; _k++) { var getterFunc = getters[_k]; newarray.push(_attribute2[getterFunc](index)); if (_morphAttr) { for (var m = 0, ml = _morphAttr.length; m < ml; m++) { newMorphArrays[m].push(_morphAttr[m][getterFunc](index)); } } } } hashToIndex[hash] = nextIndex; newIndices.push(nextIndex); nextIndex++; } } // Generate typed arrays from new attribute arrays and update // the attributeBuffers var result = geometry.clone(); for (var _i5 = 0, _l4 = attributeNames.length; _i5 < _l4; _i5++) { var _name6 = attributeNames[_i5]; var oldAttribute = geometry.getAttribute(_name6); var buffer = new oldAttribute.array.constructor(attrArrays[_name6]); var _attribute3 = new _three.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized); result.setAttribute(_name6, _attribute3); // Update the attribute arrays if (_name6 in morphAttrsArrays) { for (var _j3 = 0; _j3 < morphAttrsArrays[_name6].length; _j3++) { var oldMorphAttribute = geometry.morphAttributes[_name6][_j3]; var _buffer = new oldMorphAttribute.array.constructor(morphAttrsArrays[_name6][_j3]); var morphAttribute = new _three.BufferAttribute(_buffer, oldMorphAttribute.itemSize, oldMorphAttribute.normalized); result.morphAttributes[_name6][_j3] = morphAttribute; } } } // indices result.setIndex(newIndices); return result; } /** * @param {BufferGeometry} geometry * @param {number} drawMode * @return {BufferGeometry>} */ function toTrianglesDrawMode(geometry, drawMode) { if (drawMode === _three.TrianglesDrawMode) { console.warn('THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.'); return geometry; } if (drawMode === _three.TriangleFanDrawMode || drawMode === _three.TriangleStripDrawMode) { var index = geometry.getIndex(); // generate index if not present if (index === null) { var indices = []; var position = geometry.getAttribute('position'); if (position !== undefined) { for (var i = 0; i < position.count; i++) { indices.push(i); } geometry.setIndex(indices); index = geometry.getIndex(); } else { console.error('THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.'); return geometry; } } // var numberOfTriangles = index.count - 2; var newIndices = []; if (drawMode === _three.TriangleFanDrawMode) { // gl.TRIANGLE_FAN for (var _i6 = 1; _i6 <= numberOfTriangles; _i6++) { newIndices.push(index.getX(0)); newIndices.push(index.getX(_i6)); newIndices.push(index.getX(_i6 + 1)); } } else { // gl.TRIANGLE_STRIP for (var _i7 = 0; _i7 < numberOfTriangles; _i7++) { if (_i7 % 2 === 0) { newIndices.push(index.getX(_i7)); newIndices.push(index.getX(_i7 + 1)); newIndices.push(index.getX(_i7 + 2)); } else { newIndices.push(index.getX(_i7 + 2)); newIndices.push(index.getX(_i7 + 1)); newIndices.push(index.getX(_i7)); } } } if (newIndices.length / 3 !== numberOfTriangles) { console.error('THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.'); } // build final geometry var newGeometry = geometry.clone(); newGeometry.setIndex(newIndices); newGeometry.clearGroups(); return newGeometry; } else { console.error('THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:', drawMode); return geometry; } } /** * Calculates the morphed attributes of a morphed/skinned BufferGeometry. * Helpful for Raytracing or Decals. * @param {Mesh | Line | Points} object An instance of Mesh, Line or Points. * @return {Object} An Object with original position/normal attributes and morphed ones. */ function computeMorphedAttributes(object) { if (object.geometry.isBufferGeometry !== true) { console.error('THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.'); return null; } var _vA = new _three.Vector3(); var _vB = new _three.Vector3(); var _vC = new _three.Vector3(); var _tempA = new _three.Vector3(); var _tempB = new _three.Vector3(); var _tempC = new _three.Vector3(); var _morphA = new _three.Vector3(); var _morphB = new _three.Vector3(); var _morphC = new _three.Vector3(); function _calculateMorphedAttributeData(object, material, attribute, morphAttribute, morphTargetsRelative, a, b, c, modifiedAttributeArray) { _vA.fromBufferAttribute(attribute, a); _vB.fromBufferAttribute(attribute, b); _vC.fromBufferAttribute(attribute, c); var morphInfluences = object.morphTargetInfluences; if (material.morphTargets && morphAttribute && morphInfluences) { _morphA.set(0, 0, 0); _morphB.set(0, 0, 0); _morphC.set(0, 0, 0); for (var _i8 = 0, _il = morphAttribute.length; _i8 < _il; _i8++) { var influence = morphInfluences[_i8]; var morph = morphAttribute[_i8]; if (influence === 0) continue; _tempA.fromBufferAttribute(morph, a); _tempB.fromBufferAttribute(morph, b); _tempC.fromBufferAttribute(morph, c); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); _morphB.addScaledVector(_tempB, influence); _morphC.addScaledVector(_tempC, influence); } else { _morphA.addScaledVector(_tempA.sub(_vA), influence); _morphB.addScaledVector(_tempB.sub(_vB), influence); _morphC.addScaledVector(_tempC.sub(_vC), influence); } } _vA.add(_morphA); _vB.add(_morphB); _vC.add(_morphC); } if (object.isSkinnedMesh) { object.boneTransform(a, _vA); object.boneTransform(b, _vB); object.boneTransform(c, _vC); } modifiedAttributeArray[a * 3 + 0] = _vA.x; modifiedAttributeArray[a * 3 + 1] = _vA.y; modifiedAttributeArray[a * 3 + 2] = _vA.z; modifiedAttributeArray[b * 3 + 0] = _vB.x; modifiedAttributeArray[b * 3 + 1] = _vB.y; modifiedAttributeArray[b * 3 + 2] = _vB.z; modifiedAttributeArray[c * 3 + 0] = _vC.x; modifiedAttributeArray[c * 3 + 1] = _vC.y; modifiedAttributeArray[c * 3 + 2] = _vC.z; } var geometry = object.geometry; var material = object.material; var a, b, c; var index = geometry.index; var positionAttribute = geometry.attributes.position; var morphPosition = geometry.morphAttributes.position; var morphTargetsRelative = geometry.morphTargetsRelative; var normalAttribute = geometry.attributes.normal; var morphNormal = geometry.morphAttributes.position; var groups = geometry.groups; var drawRange = geometry.drawRange; var i, j, il, jl; var group, groupMaterial; var start, end; var modifiedPosition = new Float32Array(positionAttribute.count * positionAttribute.itemSize); var modifiedNormal = new Float32Array(normalAttribute.count * normalAttribute.itemSize); if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (i = 0, il = groups.length; i < il; i++) { group = groups[i]; groupMaterial = material[group.materialIndex]; start = Math.max(group.start, drawRange.start); end = Math.min(group.start + group.count, drawRange.start + drawRange.count); for (j = start, jl = end; j < jl; j += 3) { a = index.getX(j); b = index.getX(j + 1); c = index.getX(j + 2); _calculateMorphedAttributeData(object, groupMaterial, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition); _calculateMorphedAttributeData(object, groupMaterial, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal); } } } else { start = Math.max(0, drawRange.start); end = Math.min(index.count, drawRange.start + drawRange.count); for (i = start, il = end; i < il; i += 3) { a = index.getX(i); b = index.getX(i + 1); c = index.getX(i + 2); _calculateMorphedAttributeData(object, material, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition); _calculateMorphedAttributeData(object, material, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal); } } } else if (positionAttribute !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (i = 0, il = groups.length; i < il; i++) { group = groups[i]; groupMaterial = material[group.materialIndex]; start = Math.max(group.start, drawRange.start); end = Math.min(group.start + group.count, drawRange.start + drawRange.count); for (j = start, jl = end; j < jl; j += 3) { a = j; b = j + 1; c = j + 2; _calculateMorphedAttributeData(object, groupMaterial, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition); _calculateMorphedAttributeData(object, groupMaterial, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal); } } } else { start = Math.max(0, drawRange.start); end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (i = start, il = end; i < il; i += 3) { a = i; b = i + 1; c = i + 2; _calculateMorphedAttributeData(object, material, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition); _calculateMorphedAttributeData(object, material, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal); } } } var morphedPositionAttribute = new _three.Float32BufferAttribute(modifiedPosition, 3); var morphedNormalAttribute = new _three.Float32BufferAttribute(modifiedNormal, 3); return { positionAttribute: positionAttribute, normalAttribute: normalAttribute, morphedPositionAttribute: morphedPositionAttribute, morphedNormalAttribute: morphedNormalAttribute }; } });