diff --git a/assets/error.lottie b/assets/error.lottie new file mode 100644 index 0000000..f567a10 Binary files /dev/null and b/assets/error.lottie differ diff --git a/assets/tjs-FontLoader.js b/assets/tjs-FontLoader.js deleted file mode 100644 index 93caeb5..0000000 --- a/assets/tjs-FontLoader.js +++ /dev/null @@ -1,183 +0,0 @@ -import { - FileLoader, - Loader, - ShapePath -} from 'three'; - -class FontLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( text ) { - - const font = scope.parse( JSON.parse( text ) ); - - if ( onLoad ) onLoad( font ); - - }, onProgress, onError ); - - } - - parse( json ) { - - return new Font( json ); - - } - -} - -// - -class Font { - - constructor( data ) { - - this.isFont = true; - - this.type = 'Font'; - - this.data = data; - - } - - generateShapes( text, size = 100 ) { - - const shapes = []; - const paths = createPaths( text, size, this.data ); - - for ( let p = 0, pl = paths.length; p < pl; p ++ ) { - - shapes.push( ...paths[ p ].toShapes() ); - - } - - return shapes; - - } - -} - -function createPaths( text, size, data ) { - - const chars = Array.from( text ); - const scale = size / data.resolution; - const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; - - const paths = []; - - let offsetX = 0, offsetY = 0; - - for ( let i = 0; i < chars.length; i ++ ) { - - const char = chars[ i ]; - - if ( char === '\n' ) { - - offsetX = 0; - offsetY -= line_height; - - } else { - - const ret = createPath( char, scale, offsetX, offsetY, data ); - offsetX += ret.offsetX; - paths.push( ret.path ); - - } - - } - - return paths; - -} - -function createPath( char, scale, offsetX, offsetY, data ) { - - const glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; - - if ( ! glyph ) { - - console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' ); - - return; - - } - - const path = new ShapePath(); - - let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; - - if ( glyph.o ) { - - const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - - for ( let i = 0, l = outline.length; i < l; ) { - - const action = outline[ i ++ ]; - - switch ( action ) { - - case 'm': // moveTo - - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; - - path.moveTo( x, y ); - - break; - - case 'l': // lineTo - - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; - - path.lineTo( x, y ); - - break; - - case 'q': // quadraticCurveTo - - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); - - break; - - case 'b': // bezierCurveTo - - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - cpx2 = outline[ i ++ ] * scale + offsetX; - cpy2 = outline[ i ++ ] * scale + offsetY; - - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); - - break; - - } - - } - - } - - return { offsetX: glyph.ha * scale, path: path }; - -} - -export { FontLoader, Font }; diff --git a/assets/tjs-LottieLoader.js b/assets/tjs-LottieLoader.js deleted file mode 100644 index 4ef0401..0000000 --- a/assets/tjs-LottieLoader.js +++ /dev/null @@ -1,77 +0,0 @@ -import { - FileLoader, - Loader, - CanvasTexture, - NearestFilter, - SRGBColorSpace -} from 'three'; - -import lottie from '../libs/lottie_canvas.module.js'; - -class LottieLoader extends Loader { - - setQuality( value ) { - - this._quality = value; - - } - - load( url, onLoad, onProgress, onError ) { - - const quality = this._quality || 1; - - const texture = new CanvasTexture(); - texture.minFilter = NearestFilter; - texture.colorSpace = SRGBColorSpace; - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, function ( text ) { - - const data = JSON.parse( text ); - - // lottie uses container.offetWidth and offsetHeight - // to define width/height - - const container = document.createElement( 'div' ); - container.style.width = data.w + 'px'; - container.style.height = data.h + 'px'; - document.body.appendChild( container ); - - const animation = lottie.loadAnimation( { - container: container, - animType: 'canvas', - loop: true, - autoplay: true, - animationData: data, - rendererSettings: { dpr: quality } - } ); - - texture.animation = animation; - texture.image = animation.container; - - animation.addEventListener( 'enterFrame', function () { - - texture.needsUpdate = true; - - } ); - - container.style.display = 'none'; - - if ( onLoad !== undefined ) { - - onLoad( texture ); - - } - - }, onProgress, onError ); - - return texture; - - } - -} - -export { LottieLoader }; diff --git a/assets/tjs-OBJLoader.js b/assets/tjs-OBJLoader.js deleted file mode 100644 index 7792458..0000000 --- a/assets/tjs-OBJLoader.js +++ /dev/null @@ -1,905 +0,0 @@ -import { - BufferGeometry, - FileLoader, - Float32BufferAttribute, - Group, - LineBasicMaterial, - LineSegments, - Loader, - Material, - Mesh, - MeshPhongMaterial, - Points, - PointsMaterial, - Vector3, - Color -} from 'three'; - -// o object_name | g group_name -const _object_pattern = /^[og]\s*(.+)?/; -// mtllib file_reference -const _material_library_pattern = /^mtllib /; -// usemtl material_name -const _material_use_pattern = /^usemtl /; -// usemap map_name -const _map_use_pattern = /^usemap /; -const _face_vertex_data_separator_pattern = /\s+/; - -const _vA = new Vector3(); -const _vB = new Vector3(); -const _vC = new Vector3(); - -const _ab = new Vector3(); -const _cb = new Vector3(); - -const _color = new Color(); - -function ParserState() { - - const state = { - objects: [], - object: {}, - - vertices: [], - normals: [], - colors: [], - uvs: [], - - materials: {}, - materialLibraries: [], - - startObject: function ( name, fromDeclaration ) { - - // If the current object (initial from reset) is not from a g/o declaration in the parsed - // file. We need to use it for the first parsed g/o to keep things in sync. - if ( this.object && this.object.fromDeclaration === false ) { - - this.object.name = name; - this.object.fromDeclaration = ( fromDeclaration !== false ); - return; - - } - - const previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined ); - - if ( this.object && typeof this.object._finalize === 'function' ) { - - this.object._finalize( true ); - - } - - this.object = { - name: name || '', - fromDeclaration: ( fromDeclaration !== false ), - - geometry: { - vertices: [], - normals: [], - colors: [], - uvs: [], - hasUVIndices: false - }, - materials: [], - smooth: true, - - startMaterial: function ( name, libraries ) { - - const previous = this._finalize( false ); - - // New usemtl declaration overwrites an inherited material, except if faces were declared - // after the material, then it must be preserved for proper MultiMaterial continuation. - if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) { - - this.materials.splice( previous.index, 1 ); - - } - - const material = { - index: this.materials.length, - name: name || '', - mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ), - smooth: ( previous !== undefined ? previous.smooth : this.smooth ), - groupStart: ( previous !== undefined ? previous.groupEnd : 0 ), - groupEnd: - 1, - groupCount: - 1, - inherited: false, - - clone: function ( index ) { - - const cloned = { - index: ( typeof index === 'number' ? index : this.index ), - name: this.name, - mtllib: this.mtllib, - smooth: this.smooth, - groupStart: 0, - groupEnd: - 1, - groupCount: - 1, - inherited: false - }; - cloned.clone = this.clone.bind( cloned ); - return cloned; - - } - }; - - this.materials.push( material ); - - return material; - - }, - - currentMaterial: function () { - - if ( this.materials.length > 0 ) { - - return this.materials[ this.materials.length - 1 ]; - - } - - return undefined; - - }, - - _finalize: function ( end ) { - - const lastMultiMaterial = this.currentMaterial(); - if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) { - - lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3; - lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart; - lastMultiMaterial.inherited = false; - - } - - // Ignore objects tail materials if no face declarations followed them before a new o/g started. - if ( end && this.materials.length > 1 ) { - - for ( let mi = this.materials.length - 1; mi >= 0; mi -- ) { - - if ( this.materials[ mi ].groupCount <= 0 ) { - - this.materials.splice( mi, 1 ); - - } - - } - - } - - // Guarantee at least one empty material, this makes the creation later more straight forward. - if ( end && this.materials.length === 0 ) { - - this.materials.push( { - name: '', - smooth: this.smooth - } ); - - } - - return lastMultiMaterial; - - } - }; - - // Inherit previous objects material. - // Spec tells us that a declared material must be set to all objects until a new material is declared. - // If a usemtl declaration is encountered while this new object is being parsed, it will - // overwrite the inherited material. Exception being that there was already face declarations - // to the inherited material, then it will be preserved for proper MultiMaterial continuation. - - if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) { - - const declared = previousMaterial.clone( 0 ); - declared.inherited = true; - this.object.materials.push( declared ); - - } - - this.objects.push( this.object ); - - }, - - finalize: function () { - - if ( this.object && typeof this.object._finalize === 'function' ) { - - this.object._finalize( true ); - - } - - }, - - parseVertexIndex: function ( value, len ) { - - const index = parseInt( value, 10 ); - return ( index >= 0 ? index - 1 : index + len / 3 ) * 3; - - }, - - parseNormalIndex: function ( value, len ) { - - const index = parseInt( value, 10 ); - return ( index >= 0 ? index - 1 : index + len / 3 ) * 3; - - }, - - parseUVIndex: function ( value, len ) { - - const index = parseInt( value, 10 ); - return ( index >= 0 ? index - 1 : index + len / 2 ) * 2; - - }, - - addVertex: function ( a, b, c ) { - - const src = this.vertices; - const dst = this.object.geometry.vertices; - - dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); - dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); - dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); - - }, - - addVertexPoint: function ( a ) { - - const src = this.vertices; - const dst = this.object.geometry.vertices; - - dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); - - }, - - addVertexLine: function ( a ) { - - const src = this.vertices; - const dst = this.object.geometry.vertices; - - dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); - - }, - - addNormal: function ( a, b, c ) { - - const src = this.normals; - const dst = this.object.geometry.normals; - - dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); - dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); - dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); - - }, - - addFaceNormal: function ( a, b, c ) { - - const src = this.vertices; - const dst = this.object.geometry.normals; - - _vA.fromArray( src, a ); - _vB.fromArray( src, b ); - _vC.fromArray( src, c ); - - _cb.subVectors( _vC, _vB ); - _ab.subVectors( _vA, _vB ); - _cb.cross( _ab ); - - _cb.normalize(); - - dst.push( _cb.x, _cb.y, _cb.z ); - dst.push( _cb.x, _cb.y, _cb.z ); - dst.push( _cb.x, _cb.y, _cb.z ); - - }, - - addColor: function ( a, b, c ) { - - const src = this.colors; - const dst = this.object.geometry.colors; - - if ( src[ a ] !== undefined ) dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); - if ( src[ b ] !== undefined ) dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); - if ( src[ c ] !== undefined ) dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); - - }, - - addUV: function ( a, b, c ) { - - const src = this.uvs; - const dst = this.object.geometry.uvs; - - dst.push( src[ a + 0 ], src[ a + 1 ] ); - dst.push( src[ b + 0 ], src[ b + 1 ] ); - dst.push( src[ c + 0 ], src[ c + 1 ] ); - - }, - - addDefaultUV: function () { - - const dst = this.object.geometry.uvs; - - dst.push( 0, 0 ); - dst.push( 0, 0 ); - dst.push( 0, 0 ); - - }, - - addUVLine: function ( a ) { - - const src = this.uvs; - const dst = this.object.geometry.uvs; - - dst.push( src[ a + 0 ], src[ a + 1 ] ); - - }, - - addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) { - - const vLen = this.vertices.length; - - let ia = this.parseVertexIndex( a, vLen ); - let ib = this.parseVertexIndex( b, vLen ); - let ic = this.parseVertexIndex( c, vLen ); - - this.addVertex( ia, ib, ic ); - this.addColor( ia, ib, ic ); - - // normals - - if ( na !== undefined && na !== '' ) { - - const nLen = this.normals.length; - - ia = this.parseNormalIndex( na, nLen ); - ib = this.parseNormalIndex( nb, nLen ); - ic = this.parseNormalIndex( nc, nLen ); - - this.addNormal( ia, ib, ic ); - - } else { - - this.addFaceNormal( ia, ib, ic ); - - } - - // uvs - - if ( ua !== undefined && ua !== '' ) { - - const uvLen = this.uvs.length; - - ia = this.parseUVIndex( ua, uvLen ); - ib = this.parseUVIndex( ub, uvLen ); - ic = this.parseUVIndex( uc, uvLen ); - - this.addUV( ia, ib, ic ); - - this.object.geometry.hasUVIndices = true; - - } else { - - // add placeholder values (for inconsistent face definitions) - - this.addDefaultUV(); - - } - - }, - - addPointGeometry: function ( vertices ) { - - this.object.geometry.type = 'Points'; - - const vLen = this.vertices.length; - - for ( let vi = 0, l = vertices.length; vi < l; vi ++ ) { - - const index = this.parseVertexIndex( vertices[ vi ], vLen ); - - this.addVertexPoint( index ); - this.addColor( index ); - - } - - }, - - addLineGeometry: function ( vertices, uvs ) { - - this.object.geometry.type = 'Line'; - - const vLen = this.vertices.length; - const uvLen = this.uvs.length; - - for ( let vi = 0, l = vertices.length; vi < l; vi ++ ) { - - this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) ); - - } - - for ( let uvi = 0, l = uvs.length; uvi < l; uvi ++ ) { - - this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) ); - - } - - } - - }; - - state.startObject( '', false ); - - return state; - -} - -// - -class OBJLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - this.materials = null; - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - 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 ); - - } - - setMaterials( materials ) { - - this.materials = materials; - - return this; - - } - - parse( text ) { - - const state = new ParserState(); - - if ( text.indexOf( '\r\n' ) !== - 1 ) { - - // This is faster than String.split with regex that splits on both - text = text.replace( /\r\n/g, '\n' ); - - } - - if ( text.indexOf( '\\\n' ) !== - 1 ) { - - // join lines separated by a line continuation character (\) - text = text.replace( /\\\n/g, '' ); - - } - - const lines = text.split( '\n' ); - let result = []; - - for ( let i = 0, l = lines.length; i < l; i ++ ) { - - const line = lines[ i ].trimStart(); - - if ( line.length === 0 ) continue; - - const lineFirstChar = line.charAt( 0 ); - - // @todo invoke passed in handler if any - if ( lineFirstChar === '#' ) continue; // skip comments - - if ( lineFirstChar === 'v' ) { - - const data = line.split( _face_vertex_data_separator_pattern ); - - switch ( data[ 0 ] ) { - - case 'v': - state.vertices.push( - parseFloat( data[ 1 ] ), - parseFloat( data[ 2 ] ), - parseFloat( data[ 3 ] ) - ); - if ( data.length >= 7 ) { - - _color.setRGB( - parseFloat( data[ 4 ] ), - parseFloat( data[ 5 ] ), - parseFloat( data[ 6 ] ) - ).convertSRGBToLinear(); - - state.colors.push( _color.r, _color.g, _color.b ); - - } else { - - // if no colors are defined, add placeholders so color and vertex indices match - - state.colors.push( undefined, undefined, undefined ); - - } - - break; - case 'vn': - state.normals.push( - parseFloat( data[ 1 ] ), - parseFloat( data[ 2 ] ), - parseFloat( data[ 3 ] ) - ); - break; - case 'vt': - state.uvs.push( - parseFloat( data[ 1 ] ), - parseFloat( data[ 2 ] ) - ); - break; - - } - - } else if ( lineFirstChar === 'f' ) { - - const lineData = line.slice( 1 ).trim(); - const vertexData = lineData.split( _face_vertex_data_separator_pattern ); - const faceVertices = []; - - // Parse the face vertex data into an easy to work with format - - for ( let j = 0, jl = vertexData.length; j < jl; j ++ ) { - - const vertex = vertexData[ j ]; - - if ( vertex.length > 0 ) { - - const vertexParts = vertex.split( '/' ); - faceVertices.push( vertexParts ); - - } - - } - - // Draw an edge between the first vertex and all subsequent vertices to form an n-gon - - const v1 = faceVertices[ 0 ]; - - for ( let j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) { - - const v2 = faceVertices[ j ]; - const v3 = faceVertices[ j + 1 ]; - - state.addFace( - v1[ 0 ], v2[ 0 ], v3[ 0 ], - v1[ 1 ], v2[ 1 ], v3[ 1 ], - v1[ 2 ], v2[ 2 ], v3[ 2 ] - ); - - } - - } else if ( lineFirstChar === 'l' ) { - - const lineParts = line.substring( 1 ).trim().split( ' ' ); - let lineVertices = []; - const lineUVs = []; - - if ( line.indexOf( '/' ) === - 1 ) { - - lineVertices = lineParts; - - } else { - - for ( let li = 0, llen = lineParts.length; li < llen; li ++ ) { - - const parts = lineParts[ li ].split( '/' ); - - if ( parts[ 0 ] !== '' ) lineVertices.push( parts[ 0 ] ); - if ( parts[ 1 ] !== '' ) lineUVs.push( parts[ 1 ] ); - - } - - } - - state.addLineGeometry( lineVertices, lineUVs ); - - } else if ( lineFirstChar === 'p' ) { - - const lineData = line.slice( 1 ).trim(); - const pointData = lineData.split( ' ' ); - - state.addPointGeometry( pointData ); - - } else if ( ( result = _object_pattern.exec( line ) ) !== null ) { - - // o object_name - // or - // g group_name - - // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869 - // let name = result[ 0 ].slice( 1 ).trim(); - const name = ( ' ' + result[ 0 ].slice( 1 ).trim() ).slice( 1 ); - - state.startObject( name ); - - } else if ( _material_use_pattern.test( line ) ) { - - // material - - state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries ); - - } else if ( _material_library_pattern.test( line ) ) { - - // mtl file - - state.materialLibraries.push( line.substring( 7 ).trim() ); - - } else if ( _map_use_pattern.test( line ) ) { - - // the line is parsed but ignored since the loader assumes textures are defined MTL files - // (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method) - - console.warn( 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' ); - - } else if ( lineFirstChar === 's' ) { - - result = line.split( ' ' ); - - // smooth shading - - // @todo Handle files that have varying smooth values for a set of faces inside one geometry, - // but does not define a usemtl for each face set. - // This should be detected and a dummy material created (later MultiMaterial and geometry groups). - // This requires some care to not create extra material on each smooth value for "normal" obj files. - // where explicit usemtl defines geometry groups. - // Example asset: examples/models/obj/cerberus/Cerberus.obj - - /* - * http://paulbourke.net/dataformats/obj/ - * - * From chapter "Grouping" Syntax explanation "s group_number": - * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off. - * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form - * surfaces, smoothing groups are either turned on or off; there is no difference between values greater - * than 0." - */ - if ( result.length > 1 ) { - - const value = result[ 1 ].trim().toLowerCase(); - state.object.smooth = ( value !== '0' && value !== 'off' ); - - } else { - - // ZBrush can produce "s" lines #11707 - state.object.smooth = true; - - } - - const material = state.object.currentMaterial(); - if ( material ) material.smooth = state.object.smooth; - - } else { - - // Handle null terminated files without exception - if ( line === '\0' ) continue; - - console.warn( 'THREE.OBJLoader: Unexpected line: "' + line + '"' ); - - } - - } - - state.finalize(); - - const container = new Group(); - container.materialLibraries = [].concat( state.materialLibraries ); - - const hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 ); - - if ( hasPrimitives === true ) { - - for ( let i = 0, l = state.objects.length; i < l; i ++ ) { - - const object = state.objects[ i ]; - const geometry = object.geometry; - const materials = object.materials; - const isLine = ( geometry.type === 'Line' ); - const isPoints = ( geometry.type === 'Points' ); - let hasVertexColors = false; - - // Skip o/g line declarations that did not follow with any faces - if ( geometry.vertices.length === 0 ) continue; - - const buffergeometry = new BufferGeometry(); - - buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) ); - - if ( geometry.normals.length > 0 ) { - - buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) ); - - } - - if ( geometry.colors.length > 0 ) { - - hasVertexColors = true; - buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) ); - - } - - if ( geometry.hasUVIndices === true ) { - - buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) ); - - } - - // Create materials - - const createdMaterials = []; - - for ( let mi = 0, miLen = materials.length; mi < miLen; mi ++ ) { - - const sourceMaterial = materials[ mi ]; - const materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors; - let material = state.materials[ materialHash ]; - - if ( this.materials !== null ) { - - material = this.materials.create( sourceMaterial.name ); - - // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material. - if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) { - - const materialLine = new LineBasicMaterial(); - Material.prototype.copy.call( materialLine, material ); - materialLine.color.copy( material.color ); - material = materialLine; - - } else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) { - - const materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } ); - Material.prototype.copy.call( materialPoints, material ); - materialPoints.color.copy( material.color ); - materialPoints.map = material.map; - material = materialPoints; - - } - - } - - if ( material === undefined ) { - - if ( isLine ) { - - material = new LineBasicMaterial(); - - } else if ( isPoints ) { - - material = new PointsMaterial( { size: 1, sizeAttenuation: false } ); - - } else { - - material = new MeshPhongMaterial(); - - } - - material.name = sourceMaterial.name; - material.flatShading = sourceMaterial.smooth ? false : true; - material.vertexColors = hasVertexColors; - - state.materials[ materialHash ] = material; - - } - - createdMaterials.push( material ); - - } - - // Create mesh - - let mesh; - - if ( createdMaterials.length > 1 ) { - - for ( let mi = 0, miLen = materials.length; mi < miLen; mi ++ ) { - - const sourceMaterial = materials[ mi ]; - buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi ); - - } - - if ( isLine ) { - - mesh = new LineSegments( buffergeometry, createdMaterials ); - - } else if ( isPoints ) { - - mesh = new Points( buffergeometry, createdMaterials ); - - } else { - - mesh = new Mesh( buffergeometry, createdMaterials ); - - } - - } else { - - if ( isLine ) { - - mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] ); - - } else if ( isPoints ) { - - mesh = new Points( buffergeometry, createdMaterials[ 0 ] ); - - } else { - - mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] ); - - } - - } - - mesh.name = object.name; - - container.add( mesh ); - - } - - } else { - - // if there is only the default parser state object with no geometry data, interpret data as point cloud - - if ( state.vertices.length > 0 ) { - - const material = new PointsMaterial( { size: 1, sizeAttenuation: false } ); - - const buffergeometry = new BufferGeometry(); - - buffergeometry.setAttribute( 'position', new Float32BufferAttribute( state.vertices, 3 ) ); - - if ( state.colors.length > 0 && state.colors[ 0 ] !== undefined ) { - - buffergeometry.setAttribute( 'color', new Float32BufferAttribute( state.colors, 3 ) ); - material.vertexColors = true; - - } - - const points = new Points( buffergeometry, material ); - container.add( points ); - - } - - } - - return container; - - } - -} - -export { OBJLoader }; diff --git a/assets/tjs-STLLoader.js b/assets/tjs-STLLoader.js deleted file mode 100644 index 75d4f84..0000000 --- a/assets/tjs-STLLoader.js +++ /dev/null @@ -1,410 +0,0 @@ -import { - BufferAttribute, - BufferGeometry, - Color, - FileLoader, - Float32BufferAttribute, - Loader, - Vector3 -} from 'three'; - -/** - * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs. - * - * Supports both binary and ASCII encoded files, with automatic detection of type. - * - * The loader returns a non-indexed buffer geometry. - * - * Limitations: - * Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL). - * There is perhaps some question as to how valid it is to always assume little-endian-ness. - * ASCII decoding assumes file is UTF-8. - * - * Usage: - * const loader = new STLLoader(); - * loader.load( './models/stl/slotted_disk.stl', function ( geometry ) { - * scene.add( new THREE.Mesh( geometry ) ); - * }); - * - * For binary STLs geometry might contain colors for vertices. To use it: - * // use the same code to load STL as above - * if (geometry.hasColors) { - * material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: true }); - * } else { .... } - * const mesh = new THREE.Mesh( geometry, material ); - * - * For ASCII STLs containing multiple solids, each solid is assigned to a different group. - * Groups can be used to assign a different color by defining an array of materials with the same length of - * geometry.groups and passing it to the Mesh constructor: - * - * const mesh = new THREE.Mesh( geometry, material ); - * - * For example: - * - * const materials = []; - * const nGeometryGroups = geometry.groups.length; - * - * const colorMap = ...; // Some logic to index colors. - * - * for (let i = 0; i < nGeometryGroups; i++) { - * - * const material = new THREE.MeshPhongMaterial({ - * color: colorMap[i], - * wireframe: false - * }); - * - * } - * - * materials.push(material); - * const mesh = new THREE.Mesh(geometry, materials); - */ - - -class STLLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new 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 ); - - } - - parse( data ) { - - function isBinary( data ) { - - const reader = new DataView( data ); - const face_size = ( 32 / 8 * 3 ) + ( ( 32 / 8 * 3 ) * 3 ) + ( 16 / 8 ); - const n_faces = reader.getUint32( 80, true ); - const expect = 80 + ( 32 / 8 ) + ( n_faces * face_size ); - - if ( expect === reader.byteLength ) { - - return true; - - } - - // An ASCII STL data must begin with 'solid ' as the first six bytes. - // However, ASCII STLs lacking the SPACE after the 'd' are known to be - // plentiful. So, check the first 5 bytes for 'solid'. - - // Several encodings, such as UTF-8, precede the text with up to 5 bytes: - // https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding - // Search for "solid" to start anywhere after those prefixes. - - // US-ASCII ordinal values for 's', 'o', 'l', 'i', 'd' - - const solid = [ 115, 111, 108, 105, 100 ]; - - for ( let off = 0; off < 5; off ++ ) { - - // If "solid" text is matched to the current offset, declare it to be an ASCII STL. - - if ( matchDataViewAt( solid, reader, off ) ) return false; - - } - - // Couldn't find "solid" text at the beginning; it is binary STL. - - return true; - - } - - function matchDataViewAt( query, reader, offset ) { - - // Check if each byte in query matches the corresponding byte from the current offset - - for ( let i = 0, il = query.length; i < il; i ++ ) { - - if ( query[ i ] !== reader.getUint8( offset + i ) ) return false; - - } - - return true; - - } - - function parseBinary( data ) { - - const reader = new DataView( data ); - const faces = reader.getUint32( 80, true ); - - let r, g, b, hasColors = false, colors; - let defaultR, defaultG, defaultB, alpha; - - // process STL header - // check for default color in header ("COLOR=rgba" sequence). - - for ( let index = 0; index < 80 - 10; index ++ ) { - - if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) && - ( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) && - ( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) { - - hasColors = true; - colors = new Float32Array( faces * 3 * 3 ); - - defaultR = reader.getUint8( index + 6 ) / 255; - defaultG = reader.getUint8( index + 7 ) / 255; - defaultB = reader.getUint8( index + 8 ) / 255; - alpha = reader.getUint8( index + 9 ) / 255; - - } - - } - - const dataOffset = 84; - const faceLength = 12 * 4 + 2; - - const geometry = new BufferGeometry(); - - const vertices = new Float32Array( faces * 3 * 3 ); - const normals = new Float32Array( faces * 3 * 3 ); - - const color = new Color(); - - for ( let face = 0; face < faces; face ++ ) { - - const start = dataOffset + face * faceLength; - const normalX = reader.getFloat32( start, true ); - const normalY = reader.getFloat32( start + 4, true ); - const normalZ = reader.getFloat32( start + 8, true ); - - if ( hasColors ) { - - const packedColor = reader.getUint16( start + 48, true ); - - if ( ( packedColor & 0x8000 ) === 0 ) { - - // facet has its own unique color - - r = ( packedColor & 0x1F ) / 31; - g = ( ( packedColor >> 5 ) & 0x1F ) / 31; - b = ( ( packedColor >> 10 ) & 0x1F ) / 31; - - } else { - - r = defaultR; - g = defaultG; - b = defaultB; - - } - - } - - for ( let i = 1; i <= 3; i ++ ) { - - const vertexstart = start + i * 12; - const componentIdx = ( face * 3 * 3 ) + ( ( i - 1 ) * 3 ); - - vertices[ componentIdx ] = reader.getFloat32( vertexstart, true ); - vertices[ componentIdx + 1 ] = reader.getFloat32( vertexstart + 4, true ); - vertices[ componentIdx + 2 ] = reader.getFloat32( vertexstart + 8, true ); - - normals[ componentIdx ] = normalX; - normals[ componentIdx + 1 ] = normalY; - normals[ componentIdx + 2 ] = normalZ; - - if ( hasColors ) { - - color.set( r, g, b ).convertSRGBToLinear(); - - colors[ componentIdx ] = color.r; - colors[ componentIdx + 1 ] = color.g; - colors[ componentIdx + 2 ] = color.b; - - } - - } - - } - - geometry.setAttribute( 'position', new BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - - if ( hasColors ) { - - geometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) ); - geometry.hasColors = true; - geometry.alpha = alpha; - - } - - return geometry; - - } - - function parseASCII( data ) { - - const geometry = new BufferGeometry(); - const patternSolid = /solid([\s\S]*?)endsolid/g; - const patternFace = /facet([\s\S]*?)endfacet/g; - const patternName = /solid\s(.+)/; - let faceCounter = 0; - - const patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source; - const patternVertex = new RegExp( 'vertex' + patternFloat + patternFloat + patternFloat, 'g' ); - const patternNormal = new RegExp( 'normal' + patternFloat + patternFloat + patternFloat, 'g' ); - - const vertices = []; - const normals = []; - const groupNames = []; - - const normal = new Vector3(); - - let result; - - let groupCount = 0; - let startVertex = 0; - let endVertex = 0; - - while ( ( result = patternSolid.exec( data ) ) !== null ) { - - startVertex = endVertex; - - const solid = result[ 0 ]; - - const name = ( result = patternName.exec( solid ) ) !== null ? result[ 1 ] : ''; - groupNames.push( name ); - - while ( ( result = patternFace.exec( solid ) ) !== null ) { - - let vertexCountPerFace = 0; - let normalCountPerFace = 0; - - const text = result[ 0 ]; - - while ( ( result = patternNormal.exec( text ) ) !== null ) { - - normal.x = parseFloat( result[ 1 ] ); - normal.y = parseFloat( result[ 2 ] ); - normal.z = parseFloat( result[ 3 ] ); - normalCountPerFace ++; - - } - - while ( ( result = patternVertex.exec( text ) ) !== null ) { - - vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); - normals.push( normal.x, normal.y, normal.z ); - vertexCountPerFace ++; - endVertex ++; - - } - - // every face have to own ONE valid normal - - if ( normalCountPerFace !== 1 ) { - - console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter ); - - } - - // each face have to own THREE valid vertices - - if ( vertexCountPerFace !== 3 ) { - - console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter ); - - } - - faceCounter ++; - - } - - const start = startVertex; - const count = endVertex - startVertex; - - geometry.userData.groupNames = groupNames; - - geometry.addGroup( start, count, groupCount ); - groupCount ++; - - } - - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - - return geometry; - - } - - function ensureString( buffer ) { - - if ( typeof buffer !== 'string' ) { - - return new TextDecoder().decode( buffer ); - - } - - return buffer; - - } - - function ensureBinary( buffer ) { - - if ( typeof buffer === 'string' ) { - - const array_buffer = new Uint8Array( buffer.length ); - for ( let i = 0; i < buffer.length; i ++ ) { - - array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian - - } - - return array_buffer.buffer || array_buffer; - - } else { - - return buffer; - - } - - } - - // start - - const binData = ensureBinary( data ); - - return isBinary( binData ) ? parseBinary( binData ) : parseASCII( ensureString( data ) ); - - } - -} - -export { STLLoader }; diff --git a/assets/tjs-SVGLoader.js b/assets/tjs-SVGLoader.js deleted file mode 100644 index 196e42b..0000000 --- a/assets/tjs-SVGLoader.js +++ /dev/null @@ -1,3173 +0,0 @@ -import { - Box2, - BufferGeometry, - FileLoader, - Float32BufferAttribute, - Loader, - Matrix3, - Path, - Shape, - ShapePath, - ShapeUtils, - SRGBColorSpace, - Vector2, - Vector3 -} from 'three'; - -const COLOR_SPACE_SVG = SRGBColorSpace; - -class SVGLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - // Default dots per inch - this.defaultDPI = 90; - - // Accepted units: 'mm', 'cm', 'in', 'pt', 'pc', 'px' - this.defaultUnit = 'px'; - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.setWithCredentials( scope.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 ); - - } - - parse( text ) { - - const scope = this; - - function parseNode( node, style ) { - - if ( node.nodeType !== 1 ) return; - - const transform = getNodeTransform( node ); - - let isDefsNode = false; - - let path = null; - - switch ( node.nodeName ) { - - case 'svg': - style = parseStyle( node, style ); - break; - - case 'style': - parseCSSStylesheet( node ); - break; - - case 'g': - style = parseStyle( node, style ); - break; - - case 'path': - style = parseStyle( node, style ); - if ( node.hasAttribute( 'd' ) ) path = parsePathNode( node ); - break; - - case 'rect': - style = parseStyle( node, style ); - path = parseRectNode( node ); - break; - - case 'polygon': - style = parseStyle( node, style ); - path = parsePolygonNode( node ); - break; - - case 'polyline': - style = parseStyle( node, style ); - path = parsePolylineNode( node ); - break; - - case 'circle': - style = parseStyle( node, style ); - path = parseCircleNode( node ); - break; - - case 'ellipse': - style = parseStyle( node, style ); - path = parseEllipseNode( node ); - break; - - case 'line': - style = parseStyle( node, style ); - path = parseLineNode( node ); - break; - - case 'defs': - isDefsNode = true; - break; - - case 'use': - style = parseStyle( node, style ); - - const href = node.getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ) || ''; - const usedNodeId = href.substring( 1 ); - const usedNode = node.viewportElement.getElementById( usedNodeId ); - if ( usedNode ) { - - parseNode( usedNode, style ); - - } else { - - console.warn( 'SVGLoader: \'use node\' references non-existent node id: ' + usedNodeId ); - - } - - break; - - default: - // console.log( node ); - - } - - if ( path ) { - - if ( style.fill !== undefined && style.fill !== 'none' ) { - - path.color.setStyle( style.fill, COLOR_SPACE_SVG ); - - } - - transformPath( path, currentTransform ); - - paths.push( path ); - - path.userData = { node: node, style: style }; - - } - - const childNodes = node.childNodes; - - for ( let i = 0; i < childNodes.length; i ++ ) { - - const node = childNodes[ i ]; - - if ( isDefsNode && node.nodeName !== 'style' && node.nodeName !== 'defs' ) { - - // Ignore everything in defs except CSS style definitions - // and nested defs, because it is OK by the standard to have - //