import { 
 | 
    BufferGeometry, 
 | 
    FileLoader, 
 | 
    Float32BufferAttribute, 
 | 
    Group, 
 | 
    LineBasicMaterial, 
 | 
    LineSegments, 
 | 
    Loader, 
 | 
    Material, 
 | 
    Mesh, 
 | 
    MeshPhongMaterial, 
 | 
    Points, 
 | 
    PointsMaterial, 
 | 
    Vector3 
 | 
} from '../three.module.js'; 
 | 
  
 | 
// 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 _vA = new Vector3(); 
 | 
const _vB = new Vector3(); 
 | 
const _vC = new Vector3(); 
 | 
  
 | 
const _ab = new Vector3(); 
 | 
const _cb = new Vector3(); 
 | 
  
 | 
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 line = '', lineFirstChar = ''; 
 | 
        let lineLength = 0; 
 | 
        let result = []; 
 | 
  
 | 
        // Faster to just trim left side of the line. Use if available. 
 | 
        const trimLeft = ( typeof ''.trimLeft === 'function' ); 
 | 
  
 | 
        for ( let i = 0, l = lines.length; i < l; i ++ ) { 
 | 
  
 | 
            line = lines[ i ]; 
 | 
  
 | 
            line = trimLeft ? line.trimLeft() : line.trim(); 
 | 
  
 | 
            lineLength = line.length; 
 | 
  
 | 
            if ( lineLength === 0 ) continue; 
 | 
  
 | 
            lineFirstChar = line.charAt( 0 ); 
 | 
  
 | 
            // @todo invoke passed in handler if any 
 | 
            if ( lineFirstChar === '#' ) continue; 
 | 
  
 | 
            if ( lineFirstChar === 'v' ) { 
 | 
  
 | 
                const data = line.split( /\s+/ ); 
 | 
  
 | 
                switch ( data[ 0 ] ) { 
 | 
  
 | 
                    case 'v': 
 | 
                        state.vertices.push( 
 | 
                            parseFloat( data[ 1 ] ), 
 | 
                            parseFloat( data[ 2 ] ), 
 | 
                            parseFloat( data[ 3 ] ) 
 | 
                        ); 
 | 
                        if ( data.length >= 7 ) { 
 | 
  
 | 
                            state.colors.push( 
 | 
                                parseFloat( data[ 4 ] ), 
 | 
                                parseFloat( data[ 5 ] ), 
 | 
                                parseFloat( data[ 6 ] ) 
 | 
  
 | 
                            ); 
 | 
  
 | 
                        } 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.substr( 1 ).trim(); 
 | 
                const vertexData = lineData.split( /\s+/ ); 
 | 
                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.substr( 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 ].substr( 1 ).trim(); 
 | 
                const name = ( ' ' + result[ 0 ].substr( 1 ).trim() ).substr( 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/ 
 | 
                     * or 
 | 
                     * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf 
 | 
                     * 
 | 
                     * 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 }; 
 |