Fantasy-Map-Generator/libs/loopsubdivison.min.js
Efruz Yıldırır 3d8aa7c3ca
3D Scene system upgrade. (#956)
* 3d view system upgrade.

* version fix

* Versioning fixed.

* Subdivision Added

* Subdivision added. Removed toggle wireframe as an option.
Reverted to previous rendering method.

* Update obj export because new threejs version.

* Clean up of unrequired code.

* Multiple fixes to 3D view upgrade PR.

* Remove unused code.(for3DRender)
2023-08-05 22:54:13 +04:00

132 lines
No EOL
22 KiB
JavaScript

/**
* @description Loop Subdivision Surface
* @about Smooth subdivision surface modifier for use with three.js BufferGeometry.
* @author Stephens Nunnally <@stevinz>
* @license MIT - Copyright (c) 2022 Stephens Nunnally
* @source https://github.com/stevinz/three-subdivide
*/
/////////////////////////////////////////////////////////////////////////////////////
//
// Functions
// modify Applies Loop subdivision to BufferGeometry, returns new BufferGeometry
// edgeSplit Splits all triangles at edges shared by coplanar triangles
// flat One iteration of Loop subdivision, without point averaging
// smooth One iteration of Loop subdivision, with point averaging
//
// Info
// This modifier uses the Loop (Charles Loop, 1987) subdivision surface algorithm to smooth
// modern three.js BufferGeometry.
//
// At one point, three.js included a subdivision surface modifier in the extended examples (see bottom
// of file for links), it was removed in r125. The modifier was originally based on the Catmull-Clark
// algorithm, which works best for geometry with convex coplanar n-gon faces. In three.js r60 the modifier
// was changed to utilize the Loop algorithm. The Loop algorithm was designed to work better with triangle
// based meshes.
//
// The Loop algorithm, however, doesn't always provide uniform results as the vertices are
// skewed toward the most used vertex positions. A triangle based box (e.g. BoxGeometry for example) will
// tend to favor the corners. To alleviate this issue, this implementation includes an initial pass to split
// coplanar faces at their shared edges. It starts by splitting along the longest shared edge first, and then
// from that midpoint it splits to any remaining coplanar shared edges.
//
// Also by default, this implementation inserts new uv coordinates, but does not average them using the Loop
// algorithm. In some cases (often in flat geometries) this will produce undesired results, a
// noticeable tearing will occur. In such cases, try passing 'uvSmooth' as true to enable uv averaging.
//
// Note(s)
// - This modifier returns a new BufferGeometry instance, it does not dispose() of the old geometry.
//
// - This modifier returns a NonIndexed geometry. An Indexed geometry can be created by using the
// BufferGeometryUtils.mergeVertices() function, see:
// https://threejs.org/docs/?q=buffer#examples/en/utils/BufferGeometryUtils.mergeVertices
//
// - This modifier works best with geometry whose triangles share edges AND edge vertices. See diagram below.
//
// OKAY NOT OKAY
// O O
// /|\ / \
// / | \ / \
// / | \ / \
// O---O---O O---O---O
// \ | / \ | /
// \ | / \ | /
// \|/ \|/
// O O
//
// Reference(s)
// - Subdivision Surfaces
// https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/thesis-10.pdf
// https://en.wikipedia.org/wiki/Loop_subdivision_surface
// https://cseweb.ucsd.edu/~alchern/teaching/cse167_fa21/6-3Surfaces.pdf
//
// - Original three.js SubdivisionModifier, r124 (Loop)
// https://github.com/mrdoob/three.js/blob/r124/examples/jsm/modifiers/SubdivisionModifier.js
//
// - Original three.js SubdivisionModifier, r59 (Catmull-Clark)
// https://github.com/mrdoob/three.js/blob/r59/examples/js/modifiers/SubdivisionModifier.js
//
/////////////////////////////////////////////////////////////////////////////////////
/**
* @description Loop Subdivision Surface
* @about Smooth subdivision surface modifier for use with three.js BufferGeometry.
* @author Stephens Nunnally <@stevinz>
* @license MIT - Copyright (c) 2022 Stephens Nunnally
* @source https://github.com/stevinz/three-subdivide
*/
window.loopSubdivision={};(()=>{const POSITION_DECIMALS=2;const _average=new THREE.Vector3();const _center=new THREE.Vector3();const _midpoint=new THREE.Vector3();const _normal=new THREE.Vector3();const _temp=new THREE.Vector3();const _vector0=new THREE.Vector3();const _vector1=new THREE.Vector3();const _vector2=new THREE.Vector3();const _vec0to1=new THREE.Vector3();const _vec1to2=new THREE.Vector3();const _vec2to0=new THREE.Vector3();const _position=[new THREE.Vector3(),new THREE.Vector3(),new THREE.Vector3(),];const _vertex=[new THREE.Vector3(),new THREE.Vector3(),new THREE.Vector3(),];const _triangle=new THREE.Triangle();function modify(bufferGeometry,iterations=1,params={}){if(arguments.length>3)console.warn(`modify() now uses a parameter object. See readme for more info!`);if(typeof params!=='object')params={};if(params.split===undefined)params.split=!0;if(params.uvSmooth===undefined)params.uvSmooth=!1;if(params.preserveEdges===undefined)params.preserveEdges=!1;if(params.flatOnly===undefined)params.flatOnly=!1;if(params.maxTriangles===undefined)params.maxTriangles=Infinity;if(!verifyGeometry(bufferGeometry))return bufferGeometry;let modifiedGeometry=bufferGeometry.clone();if(params.split){const splitGeometry=edgeSplit(modifiedGeometry)
modifiedGeometry.dispose();modifiedGeometry=splitGeometry}
for(let i=0;i<iterations;i++){let currentTriangles=modifiedGeometry.attributes.position.count/3;if(currentTriangles<params.maxTriangles){let subdividedGeometry;if(params.flatOnly){subdividedGeometry=flat(modifiedGeometry)}else{subdividedGeometry=smooth(modifiedGeometry,params)}
modifiedGeometry.groups.forEach((group)=>{subdividedGeometry.addGroup(group.start*4,group.count*4,group.materialIndex)});modifiedGeometry.dispose();modifiedGeometry=subdividedGeometry}}
return modifiedGeometry}
window.loopSubdivision.modify=modify;function edgeSplit(geometry){if(!verifyGeometry(geometry))return geometry;const existing=(geometry.index!==null)?geometry.toNonIndexed():geometry.clone();const split=new THREE.BufferGeometry();const attributeList=gatherAttributes(existing);const vertexCount=existing.attributes.position.count;const posAttribute=existing.getAttribute('position');const norAttribute=existing.getAttribute('normal');const edgeHashToTriangle={};const triangleEdgeHashes=[];const edgeLength={};const triangleExist=[];for(let i=0;i<vertexCount;i+=3){_vector0.fromBufferAttribute(posAttribute,i+0);_vector1.fromBufferAttribute(posAttribute,i+1);_vector2.fromBufferAttribute(posAttribute,i+2);_normal.fromBufferAttribute(norAttribute,i);const vecHash0=hashFromVector(_vector0);const vecHash1=hashFromVector(_vector1);const vecHash2=hashFromVector(_vector2);const triangleSize=_triangle.set(_vector0,_vector1,_vector2).getArea();triangleExist.push(!fuzzy(triangleSize,0));if(!triangleExist[i/3]){triangleEdgeHashes.push([]);continue}
calcNormal(_normal,_vector0,_vector1,_vector2);const normalHash=hashFromVector(_normal);const hashes=[`${vecHash0}_${vecHash1}_${normalHash}`,`${vecHash1}_${vecHash0}_${normalHash}`,`${vecHash1}_${vecHash2}_${normalHash}`,`${vecHash2}_${vecHash1}_${normalHash}`,`${vecHash2}_${vecHash0}_${normalHash}`,`${vecHash0}_${vecHash2}_${normalHash}`,];const index=i/3;for(let j=0;j<hashes.length;j++){if(!edgeHashToTriangle[hashes[j]])edgeHashToTriangle[hashes[j]]=[];edgeHashToTriangle[hashes[j]].push(index);if(!edgeLength[hashes[j]]){if(j===0||j===1)edgeLength[hashes[j]]=_vector0.distanceTo(_vector1);if(j===2||j===3)edgeLength[hashes[j]]=_vector1.distanceTo(_vector2);if(j===4||j===5)edgeLength[hashes[j]]=_vector2.distanceTo(_vector0)}}
triangleEdgeHashes.push([hashes[0],hashes[2],hashes[4]])}
attributeList.forEach((attributeName)=>{const attribute=existing.getAttribute(attributeName);if(!attribute)return;const floatArray=splitAttribute(attribute,attributeName);split.setAttribute(attributeName,new THREE.BufferAttribute(floatArray,attribute.itemSize))});const morphAttributes=existing.morphAttributes;for(const attributeName in morphAttributes){const array=[];const morphAttribute=morphAttributes[attributeName];for(let i=0,l=morphAttribute.length;i<l;i++){if(morphAttribute[i].count!==vertexCount)continue;const floatArray=splitAttribute(morphAttribute[i],attributeName,!0);array.push(new THREE.BufferAttribute(floatArray,morphAttribute[i].itemSize))}
split.morphAttributes[attributeName]=array}
split.morphTargetsRelative=existing.morphTargetsRelative;existing.dispose();return split;function splitAttribute(attribute,attributeName,morph=!1){const newTriangles=4;const arrayLength=(vertexCount*attribute.itemSize)*newTriangles;const floatArray=new attribute.array.constructor(arrayLength);const processGroups=(attributeName==='position'&&!morph&&existing.groups.length>0);let groupStart=undefined,groupMaterial=undefined;let index=0;let skipped=0;let step=attribute.itemSize;for(let i=0;i<vertexCount;i+=3){if(!triangleExist[i/3]){skipped+=3;continue}
_vector0.fromBufferAttribute(attribute,i+0);_vector1.fromBufferAttribute(attribute,i+1);_vector2.fromBufferAttribute(attribute,i+2);const existingIndex=i/3;const edgeHash0to1=triangleEdgeHashes[existingIndex][0];const edgeHash1to2=triangleEdgeHashes[existingIndex][1];const edgeHash2to0=triangleEdgeHashes[existingIndex][2];const edgeCount0to1=edgeHashToTriangle[edgeHash0to1].length;const edgeCount1to2=edgeHashToTriangle[edgeHash1to2].length;const edgeCount2to0=edgeHashToTriangle[edgeHash2to0].length;const sharedCount=(edgeCount0to1+edgeCount1to2+edgeCount2to0)-3;const loopStartIndex=((index*3)/step)/3;if(sharedCount===0){setTriangle(floatArray,index,step,_vector0,_vector1,_vector2);index+=(step*3)}else{const length0to1=edgeLength[edgeHash0to1];const length1to2=edgeLength[edgeHash1to2];const length2to0=edgeLength[edgeHash2to0];if((length0to1>length1to2||edgeCount1to2<=1)&&(length0to1>length2to0||edgeCount2to0<=1)&&edgeCount0to1>1){_center.copy(_vector0).add(_vector1).divideScalar(2.0);if(edgeCount2to0>1){_midpoint.copy(_vector2).add(_vector0).divideScalar(2.0);setTriangle(floatArray,index,step,_vector0,_center,_midpoint);index+=(step*3);setTriangle(floatArray,index,step,_center,_vector2,_midpoint);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector0,_center,_vector2);index+=(step*3)}
if(edgeCount1to2>1){_midpoint.copy(_vector1).add(_vector2).divideScalar(2.0);setTriangle(floatArray,index,step,_center,_vector1,_midpoint);index+=(step*3);setTriangle(floatArray,index,step,_midpoint,_vector2,_center);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector1,_vector2,_center);index+=(step*3)}}else if((length1to2>length2to0||edgeCount2to0<=1)&&edgeCount1to2>1){_center.copy(_vector1).add(_vector2).divideScalar(2.0);if(edgeCount0to1>1){_midpoint.copy(_vector0).add(_vector1).divideScalar(2.0);setTriangle(floatArray,index,step,_center,_midpoint,_vector1);index+=(step*3);setTriangle(floatArray,index,step,_midpoint,_center,_vector0);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector1,_center,_vector0);index+=(step*3)}
if(edgeCount2to0>1){_midpoint.copy(_vector2).add(_vector0).divideScalar(2.0);setTriangle(floatArray,index,step,_center,_vector2,_midpoint);index+=(step*3);setTriangle(floatArray,index,step,_midpoint,_vector0,_center);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector2,_vector0,_center);index+=(step*3)}}else if(edgeCount2to0>1){_center.copy(_vector2).add(_vector0).divideScalar(2.0);if(edgeCount1to2>1){_midpoint.copy(_vector1).add(_vector2).divideScalar(2.0);setTriangle(floatArray,index,step,_vector2,_center,_midpoint);index+=(step*3);setTriangle(floatArray,index,step,_center,_vector1,_midpoint);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector2,_center,_vector1);index+=(step*3)}
if(edgeCount0to1>1){_midpoint.copy(_vector0).add(_vector1).divideScalar(2.0);setTriangle(floatArray,index,step,_vector0,_midpoint,_center);index+=(step*3);setTriangle(floatArray,index,step,_midpoint,_vector1,_center);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector0,_vector1,_center);index+=(step*3)}}else{setTriangle(floatArray,index,step,_vector0,_vector1,_vector2);index+=(step*3)}}
if(processGroups){existing.groups.forEach((group)=>{if(group.start===(i-skipped)){if(groupStart!==undefined&&groupMaterial!==undefined){split.addGroup(groupStart,loopStartIndex-groupStart,groupMaterial)}
groupStart=loopStartIndex;groupMaterial=group.materialIndex}})}
skipped=0}
const reducedCount=(index*3)/step;const reducedArray=new attribute.array.constructor(reducedCount);for(let i=0;i<reducedCount;i++){reducedArray[i]=floatArray[i]}
if(processGroups&&groupStart!==undefined&&groupMaterial!==undefined){split.addGroup(groupStart,(((index*3)/step)/3)-groupStart,groupMaterial)}
return reducedArray}}
function flat(geometry){if(!verifyGeometry(geometry))return geometry;const existing=(geometry.index!==null)?geometry.toNonIndexed():geometry.clone();const loop=new THREE.BufferGeometry();const attributeList=gatherAttributes(existing);const vertexCount=existing.attributes.position.count;attributeList.forEach((attributeName)=>{const attribute=existing.getAttribute(attributeName);if(!attribute)return;loop.setAttribute(attributeName,flatAttribute(attribute,vertexCount))});const morphAttributes=existing.morphAttributes;for(const attributeName in morphAttributes){const array=[];const morphAttribute=morphAttributes[attributeName];for(let i=0,l=morphAttribute.length;i<l;i++){if(morphAttribute[i].count!==vertexCount)continue;array.push(flatAttribute(morphAttribute[i],vertexCount))}
loop.morphAttributes[attributeName]=array}
loop.morphTargetsRelative=existing.morphTargetsRelative;existing.dispose();return loop}
function flatAttribute(attribute,vertexCount){const newTriangles=4;const arrayLength=(vertexCount*attribute.itemSize)*newTriangles;const floatArray=new attribute.array.constructor(arrayLength);let index=0;let step=attribute.itemSize;for(let i=0;i<vertexCount;i+=3){_vector0.fromBufferAttribute(attribute,i+0);_vector1.fromBufferAttribute(attribute,i+1);_vector2.fromBufferAttribute(attribute,i+2);_vec0to1.copy(_vector0).add(_vector1).divideScalar(2.0);_vec1to2.copy(_vector1).add(_vector2).divideScalar(2.0);_vec2to0.copy(_vector2).add(_vector0).divideScalar(2.0);setTriangle(floatArray,index,step,_vector0,_vec0to1,_vec2to0);index+=(step*3);setTriangle(floatArray,index,step,_vector1,_vec1to2,_vec0to1);index+=(step*3);setTriangle(floatArray,index,step,_vector2,_vec2to0,_vec1to2);index+=(step*3);setTriangle(floatArray,index,step,_vec0to1,_vec1to2,_vec2to0);index+=(step*3)}
return new THREE.BufferAttribute(floatArray,attribute.itemSize)}
function smooth(geometry,params={}){if(typeof params!=='object')params={};if(params.uvSmooth===undefined)params.uvSmooth=!1;if(params.preserveEdges===undefined)params.preserveEdges=!1;if(!verifyGeometry(geometry))return geometry;const existing=(geometry.index!==null)?geometry.toNonIndexed():geometry.clone();const flatGeometry=flat(existing);const loop=new THREE.BufferGeometry();const attributeList=gatherAttributes(existing);const vertexCount=existing.attributes.position.count;const posAttribute=existing.getAttribute('position');const flatPosition=flatGeometry.getAttribute('position');const hashToIndex={};const existingNeighbors={};const flatOpposites={};const existingEdges={};function addNeighbor(posHash,neighborHash,index){if(!existingNeighbors[posHash])existingNeighbors[posHash]={};if(!existingNeighbors[posHash][neighborHash])existingNeighbors[posHash][neighborHash]=[];existingNeighbors[posHash][neighborHash].push(index)}
function addOpposite(posHash,index){if(!flatOpposites[posHash])flatOpposites[posHash]=[];flatOpposites[posHash].push(index)}
function addEdgePoint(posHash,edgeHash){if(!existingEdges[posHash])existingEdges[posHash]=new Set();existingEdges[posHash].add(edgeHash)}
for(let i=0;i<vertexCount;i+=3){const posHash0=hashFromVector(_vertex[0].fromBufferAttribute(posAttribute,i+0));const posHash1=hashFromVector(_vertex[1].fromBufferAttribute(posAttribute,i+1));const posHash2=hashFromVector(_vertex[2].fromBufferAttribute(posAttribute,i+2));addNeighbor(posHash0,posHash1,i+1);addNeighbor(posHash0,posHash2,i+2);addNeighbor(posHash1,posHash0,i+0);addNeighbor(posHash1,posHash2,i+2);addNeighbor(posHash2,posHash0,i+0);addNeighbor(posHash2,posHash1,i+1);_vec0to1.copy(_vertex[0]).add(_vertex[1]).divideScalar(2.0);_vec1to2.copy(_vertex[1]).add(_vertex[2]).divideScalar(2.0);_vec2to0.copy(_vertex[2]).add(_vertex[0]).divideScalar(2.0);const hash0to1=hashFromVector(_vec0to1);const hash1to2=hashFromVector(_vec1to2);const hash2to0=hashFromVector(_vec2to0);addOpposite(hash0to1,i+2);addOpposite(hash1to2,i+0);addOpposite(hash2to0,i+1);addEdgePoint(posHash0,hash0to1);addEdgePoint(posHash0,hash2to0);addEdgePoint(posHash1,hash0to1);addEdgePoint(posHash1,hash1to2);addEdgePoint(posHash2,hash1to2);addEdgePoint(posHash2,hash2to0)}
for(let i=0;i<flatGeometry.attributes.position.count;i++){const posHash=hashFromVector(_temp.fromBufferAttribute(flatPosition,i));if(!hashToIndex[posHash])hashToIndex[posHash]=[];hashToIndex[posHash].push(i)}
attributeList.forEach((attributeName)=>{const existingAttribute=existing.getAttribute(attributeName);const flatAttribute=flatGeometry.getAttribute(attributeName);if(existingAttribute===undefined||flatAttribute===undefined)return;const floatArray=subdivideAttribute(attributeName,existingAttribute,flatAttribute);loop.setAttribute(attributeName,new THREE.BufferAttribute(floatArray,flatAttribute.itemSize))});const morphAttributes=existing.morphAttributes;for(const attributeName in morphAttributes){const array=[];const morphAttribute=morphAttributes[attributeName];for(let i=0,l=morphAttribute.length;i<l;i++){if(morphAttribute[i].count!==vertexCount)continue;const existingAttribute=morphAttribute[i];const flatAttribute=flatAttribute(morphAttribute[i],morphAttribute[i].count)
const floatArray=subdivideAttribute(attributeName,existingAttribute,flatAttribute);array.push(new THREE.BufferAttribute(floatArray,flatAttribute.itemSize))}
loop.morphAttributes[attributeName]=array}
loop.morphTargetsRelative=existing.morphTargetsRelative;flatGeometry.dispose();existing.dispose();return loop;function subdivideAttribute(attributeName,existingAttribute,flatAttribute){const arrayLength=(flatGeometry.attributes.position.count*flatAttribute.itemSize);const floatArray=new existingAttribute.array.constructor(arrayLength);let index=0;for(let i=0;i<flatGeometry.attributes.position.count;i+=3){for(let v=0;v<3;v++){if(attributeName==='uv'&&!params.uvSmooth){_vertex[v].fromBufferAttribute(flatAttribute,i+v)}else if(attributeName==='normal'){_position[v].fromBufferAttribute(flatPosition,i+v);const positionHash=hashFromVector(_position[v]);const positions=hashToIndex[positionHash];const k=Object.keys(positions).length;const beta=0.75/k;const startWeight=1.0-(beta*k);_vertex[v].fromBufferAttribute(flatAttribute,i+v);_vertex[v].multiplyScalar(startWeight);positions.forEach(positionIndex=>{_average.fromBufferAttribute(flatAttribute,positionIndex);_average.multiplyScalar(beta);_vertex[v].add(_average)})}else{_vertex[v].fromBufferAttribute(flatAttribute,i+v);_position[v].fromBufferAttribute(flatPosition,i+v);const positionHash=hashFromVector(_position[v]);const neighbors=existingNeighbors[positionHash];const opposites=flatOpposites[positionHash];if(neighbors){if(params.preserveEdges){const edgeSet=existingEdges[positionHash];let hasPair=!0;for(const edgeHash of edgeSet){if(flatOpposites[edgeHash].length%2!==0)hasPair=!1}
if(!hasPair)continue}
const k=Object.keys(neighbors).length;const beta=1/k*((5/8)-Math.pow((3/8)+(1/4)*Math.cos(2*Math.PI/k),2));const startWeight=1.0-(beta*k);_vertex[v].multiplyScalar(startWeight);for(let neighborHash in neighbors){const neighborIndices=neighbors[neighborHash];_average.set(0,0,0);for(let j=0;j<neighborIndices.length;j++){_average.add(_temp.fromBufferAttribute(existingAttribute,neighborIndices[j]))}
_average.divideScalar(neighborIndices.length);_average.multiplyScalar(beta);_vertex[v].add(_average)}}else if(opposites&&opposites.length===2){const k=opposites.length;const beta=0.125;const startWeight=1.0-(beta*k);_vertex[v].multiplyScalar(startWeight);opposites.forEach(oppositeIndex=>{_average.fromBufferAttribute(existingAttribute,oppositeIndex);_average.multiplyScalar(beta);_vertex[v].add(_average)})}}}
setTriangle(floatArray,index,flatAttribute.itemSize,_vertex[0],_vertex[1],_vertex[2]);index+=(flatAttribute.itemSize*3)}
return floatArray}}
const _positionShift=Math.pow(10,POSITION_DECIMALS);function fuzzy(a,b,tolerance=0.00001){return((a<(b+tolerance))&&(a>(b-tolerance)))}
function hashFromNumber(num,shift=_positionShift){let roundedNumber=round(num*shift);if(roundedNumber==0)roundedNumber=0;return `${roundedNumber}`}
function hashFromVector(vector,shift=_positionShift){return `${hashFromNumber(vector.x, shift)},${hashFromNumber(vector.y, shift)},${hashFromNumber(vector.z, shift)}`}
function round(x){return(x+((x>0)?0.5:-0.5))<<0}
function calcNormal(target,vec1,vec2,vec3){_temp.subVectors(vec1,vec2);target.subVectors(vec2,vec3);target.cross(_temp).normalize()}
function gatherAttributes(geometry){const desired=['position','normal','uv'];const contains=Object.keys(geometry.attributes);const attributeList=Array.from(new Set(desired.concat(contains)));return attributeList}
function setTriangle(positions,index,step,vec0,vec1,vec2){if(step>=1){positions[index+0+(step*0)]=vec0.x;positions[index+0+(step*1)]=vec1.x;positions[index+0+(step*2)]=vec2.x}
if(step>=2){positions[index+1+(step*0)]=vec0.y;positions[index+1+(step*1)]=vec1.y;positions[index+1+(step*2)]=vec2.y}
if(step>=3){positions[index+2+(step*0)]=vec0.z;positions[index+2+(step*1)]=vec1.z;positions[index+2+(step*2)]=vec2.z}
if(step>=4){positions[index+3+(step*0)]=vec0.w;positions[index+3+(step*1)]=vec1.w;positions[index+3+(step*2)]=vec2.w}}
function verifyGeometry(geometry){if(geometry===undefined){console.warn(`window.loopSubdivision: Geometry provided is undefined`);return!1}
if(!geometry.isBufferGeometry){console.warn(`window.loopSubdivision: Geometry provided is not 'BufferGeometry' type`);return!1}
if(geometry.attributes.position===undefined){console.warn(`window.loopSubdivision: Geometry provided missing required 'position' attribute`);return!1}
if(geometry.attributes.normal===undefined){geometry.computeVertexNormals()}
return!0}})()