Using three.js in my Qt QML Program
-
wrote on 19 Oct 2017, 03:50 last edited by
Hi, I'd like to use three.js in my program to render .obj files. I've looked online at https://github.com/tronlec/three.js which describes a simple example that I can run on my own. I changed the program to have the following code for rendering an .obj file:
var manager = new THREE.LoadingManager(); loader = new THREE.OBJLoader(manager); loader.load('C:/Users/dillydill123/Documents/Qt/exampleProj/object.obj', function (object) { object.traverse(function (child) { if (child instanceof THREE.Mesh) { child.material.side = THREE.Material.DoubleSide; } }); scene.add(object); });
Unfortunately, when I run this code, I get type errors, saying that
OBJLoader
is not a type. It looks like the three.js library from https://github.com/tronlec/three.js/blob/master/build/three.js does not include OBJLoader. I've tried appending the source code of OBJLoader from https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js to the end of mythree.js
file in my QRC, but I get other type errors for FIleLoader. When I attempt to append that to three.js, I get other type errors.I also want to be able to use other extensions/modules from three.js, namely TrackBallControls. Unfortunately, I get other type errors when trying to append this to the end of my three.js.
My questions are the following: How do I render a .obj file in Qt QML using three.js? And how can I easily add modules and extensions to three.js?
-
Hi, I'd like to use three.js in my program to render .obj files. I've looked online at https://github.com/tronlec/three.js which describes a simple example that I can run on my own. I changed the program to have the following code for rendering an .obj file:
var manager = new THREE.LoadingManager(); loader = new THREE.OBJLoader(manager); loader.load('C:/Users/dillydill123/Documents/Qt/exampleProj/object.obj', function (object) { object.traverse(function (child) { if (child instanceof THREE.Mesh) { child.material.side = THREE.Material.DoubleSide; } }); scene.add(object); });
Unfortunately, when I run this code, I get type errors, saying that
OBJLoader
is not a type. It looks like the three.js library from https://github.com/tronlec/three.js/blob/master/build/three.js does not include OBJLoader. I've tried appending the source code of OBJLoader from https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js to the end of mythree.js
file in my QRC, but I get other type errors for FIleLoader. When I attempt to append that to three.js, I get other type errors.I also want to be able to use other extensions/modules from three.js, namely TrackBallControls. Unfortunately, I get other type errors when trying to append this to the end of my three.js.
My questions are the following: How do I render a .obj file in Qt QML using three.js? And how can I easily add modules and extensions to three.js?
wrote on 29 Aug 2018, 11:06 last edited by@dillydill123 in case this is still an issue :
you have to include the loader code - copy the OBJLoader.js to a directory (e.g. loaders) where it gets found by this statement:
Qt.include("loaders/OBJLoader.js")
....
// load file :
var objLoader = new THREE.OBJLoader( THREE.DefaultLoadingManager );
objLoader.setMaterials( materials );
objLoader.setPath('C:/Users/dillydill123/Documents/Qt/exampleProj/');
objLoader.load('object.obj', function ( object ) {
// here add the loaded object
scene.add(object)
} );best regards,
Michael -
@dillydill123 in case this is still an issue :
you have to include the loader code - copy the OBJLoader.js to a directory (e.g. loaders) where it gets found by this statement:
Qt.include("loaders/OBJLoader.js")
....
// load file :
var objLoader = new THREE.OBJLoader( THREE.DefaultLoadingManager );
objLoader.setMaterials( materials );
objLoader.setPath('C:/Users/dillydill123/Documents/Qt/exampleProj/');
objLoader.load('object.obj', function ( object ) {
// here add the loaded object
scene.add(object)
} );best regards,
Michaelwrote on 29 Aug 2018, 13:20 last edited byHi @IPO_DEV
I just tried your approach with thethree.js-master\qt-examples\quickitemtexture
example, but I get the following errorqrc:/loader/OBJLoader.js:381: TypeError: Type error
Line 381 in
OBJLoader.js
is:var loader = new THREE.FileLoader( scope.manager );
I tried with the mesh from
Qt\Examples\Qt-5.11.0\datavisualization\customitems\refinery.obj
which I copied to thequickitemtexture
example dir and then used:// load file : var objLoader = new THREE.OBJLoader(THREE.DefaultLoadingManager ); objLoader.setMaterials( material ); objLoader.setPath('G:/Source/Qt/three.js-master/qt-examples/quickitemtexture/'); objLoader.load('refinery.obj', function ( object ) { // here add the loaded object scene.add(object) } );
Any ideas?
-
Hi @IPO_DEV
I just tried your approach with thethree.js-master\qt-examples\quickitemtexture
example, but I get the following errorqrc:/loader/OBJLoader.js:381: TypeError: Type error
Line 381 in
OBJLoader.js
is:var loader = new THREE.FileLoader( scope.manager );
I tried with the mesh from
Qt\Examples\Qt-5.11.0\datavisualization\customitems\refinery.obj
which I copied to thequickitemtexture
example dir and then used:// load file : var objLoader = new THREE.OBJLoader(THREE.DefaultLoadingManager ); objLoader.setMaterials( material ); objLoader.setPath('G:/Source/Qt/three.js-master/qt-examples/quickitemtexture/'); objLoader.load('refinery.obj', function ( object ) { // here add the loaded object scene.add(object) } );
Any ideas?
wrote on 29 Aug 2018, 18:07 last edited by@Diracsbracket
ok I've forgot to paste the MTLLoader part - here' s a complete snippet:Qt.include("loaders/MTLLoader.js") Qt.include("loaders/OBJLoader.js") function addOBJ(scene) { // suppose we load a campfire.obj with its material file campfire.mtl var baseName = "campfire" var baseUrl = "file:///D:/_SOURCE/threejs_qt_74/examples/models/campfire/" var mtlLoader = new THREE.MTLLoader(); // set baseurl + path to baseurl mtlLoader.setBaseUrl( baseUrl ); mtlLoader.setPath(baseUrl); var materialFile = baseName + ".mtl" console.log("matfile ->" + materialFile) // now load material file first mtlLoader.load(materialFile, function( materials ) { // make sure all materials are loaded materials.preload(); console.log("LOADED MAT.."); // define progress and error callbacks var onProgress = function ( xhr ) { console.log("PRG " + xhr); }; var onError = function ( xhr ) { console.log("ERROR" + xhr); }; // now load OBJ with materials already loaded var objLoader = new THREE.OBJLoader( THREE.DefaultLoadingManager ); objLoader.setMaterials( materials ); objLoader.setPath(baseUrl ); objLoader.load( baseName+".obj", function ( object ) { // callback for model loaded : console.log("LOADED OBJ"); // get some info about loaded model var box = new THREE.Box3(); box.setFromObject(object); var center = box.center(); var size = box.size(); console.log("SIZE:" + JSON.stringify( size)) console.log("CENTER:" + JSON.stringify( center)) scene.add(object) // sample manipulation, move the loaded object -17 along z object.position.z = -17.0; } ) // objLoader.load }) // mtlLoader.load }
call addOBJ() with your scene...
-
@Diracsbracket
ok I've forgot to paste the MTLLoader part - here' s a complete snippet:Qt.include("loaders/MTLLoader.js") Qt.include("loaders/OBJLoader.js") function addOBJ(scene) { // suppose we load a campfire.obj with its material file campfire.mtl var baseName = "campfire" var baseUrl = "file:///D:/_SOURCE/threejs_qt_74/examples/models/campfire/" var mtlLoader = new THREE.MTLLoader(); // set baseurl + path to baseurl mtlLoader.setBaseUrl( baseUrl ); mtlLoader.setPath(baseUrl); var materialFile = baseName + ".mtl" console.log("matfile ->" + materialFile) // now load material file first mtlLoader.load(materialFile, function( materials ) { // make sure all materials are loaded materials.preload(); console.log("LOADED MAT.."); // define progress and error callbacks var onProgress = function ( xhr ) { console.log("PRG " + xhr); }; var onError = function ( xhr ) { console.log("ERROR" + xhr); }; // now load OBJ with materials already loaded var objLoader = new THREE.OBJLoader( THREE.DefaultLoadingManager ); objLoader.setMaterials( materials ); objLoader.setPath(baseUrl ); objLoader.load( baseName+".obj", function ( object ) { // callback for model loaded : console.log("LOADED OBJ"); // get some info about loaded model var box = new THREE.Box3(); box.setFromObject(object); var center = box.center(); var size = box.size(); console.log("SIZE:" + JSON.stringify( size)) console.log("CENTER:" + JSON.stringify( center)) scene.add(object) // sample manipulation, move the loaded object -17 along z object.position.z = -17.0; } ) // objLoader.load }) // mtlLoader.load }
call addOBJ() with your scene...
wrote on 30 Aug 2018, 03:06 last edited by DiracsbracketThanks for the code @IPO_DEV !
I tried it but I still get the same error at the same line:var loader = new THREE.FileLoader( scope.manager );
No worries though, as I am a complete NOOB concerning
JavaScript
(apart from the little I use forQML
),three.js
and 3D graphics, really. I was just trying to test something out by taking a working example and replacing the object added to the scene by the one read from the.obj
file...I know I must be missing some simple stuff that will become obvious if I learn more, which I will.
Cheers!
-
wrote on 30 Aug 2018, 07:28 last edited by
@Diracsbracket hm this is strange, i've also used the quickitemtexture example and just added the code i've posted - it seems that u're using a different loader code - the line in my OBJLoader.js looks different (var loader = new THREE.XHRLoader( scope.manager ); )
Here is the complete source for both files:OBJLoader.js:
/** * @author mrdoob / http://mrdoob.com/ */ THREE.OBJLoader = function ( manager ) { this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; this.materials = null; }; THREE.OBJLoader.prototype = { constructor: THREE.OBJLoader, load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new THREE.XHRLoader( scope.manager ); loader.setPath( this.path ); loader.load( url, function ( text ) { onLoad( scope.parse( text ) ); }, onProgress, onError ); }, setPath: function ( value ) { this.path = value; }, setMaterials: function ( materials ) { this.materials = materials; }, parse: function ( text ) { console.time( 'OBJLoader' ); var objects = []; var object; var foundObjects = false; var vertices = []; var normals = []; var uvs = []; function addObject( name ) { var geometry = { vertices: [], normals: [], uvs: [] }; var material = { name: '', smooth: true }; object = { name: name, geometry: geometry, material: material }; objects.push( object ); } function parseVertexIndex( value ) { var index = parseInt( value ); return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3; } function parseNormalIndex( value ) { var index = parseInt( value ); return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3; } function parseUVIndex( value ) { var index = parseInt( value ); return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2; } function addVertex( a, b, c ) { object.geometry.vertices.push( vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ], vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ], vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ] ); } function addNormal( a, b, c ) { object.geometry.normals.push( normals[ a ], normals[ a + 1 ], normals[ a + 2 ], normals[ b ], normals[ b + 1 ], normals[ b + 2 ], normals[ c ], normals[ c + 1 ], normals[ c + 2 ] ); } function addUV( a, b, c ) { object.geometry.uvs.push( uvs[ a ], uvs[ a + 1 ], uvs[ b ], uvs[ b + 1 ], uvs[ c ], uvs[ c + 1 ] ); } function addFace( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) { var ia = parseVertexIndex( a ); var ib = parseVertexIndex( b ); var ic = parseVertexIndex( c ); var id; if ( d === undefined ) { addVertex( ia, ib, ic ); } else { id = parseVertexIndex( d ); addVertex( ia, ib, id ); addVertex( ib, ic, id ); } if ( ua !== undefined ) { ia = parseUVIndex( ua ); ib = parseUVIndex( ub ); ic = parseUVIndex( uc ); if ( d === undefined ) { addUV( ia, ib, ic ); } else { id = parseUVIndex( ud ); addUV( ia, ib, id ); addUV( ib, ic, id ); } } if ( na !== undefined ) { ia = parseNormalIndex( na ); ib = parseNormalIndex( nb ); ic = parseNormalIndex( nc ); if ( d === undefined ) { addNormal( ia, ib, ic ); } else { id = parseNormalIndex( nd ); addNormal( ia, ib, id ); addNormal( ib, ic, id ); } } } addObject( '' ); // v float float float var vertex_pattern = /^v\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/; // vn float float float var normal_pattern = /^vn\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/; // vt float float var uv_pattern = /^vt\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/; // f vertex vertex vertex ... var face_pattern1 = /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/; // f vertex/uv vertex/uv vertex/uv ... var face_pattern2 = /^f\s+((-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+))(?:\s+((-?\d+)\/(-?\d+)))?/; // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... var face_pattern3 = /^f\s+((-?\d+)\/(-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+)\/(-?\d+))(?:\s+((-?\d+)\/(-?\d+)\/(-?\d+)))?/; // f vertex//normal vertex//normal vertex//normal ... var face_pattern4 = /^f\s+((-?\d+)\/\/(-?\d+))\s+((-?\d+)\/\/(-?\d+))\s+((-?\d+)\/\/(-?\d+))(?:\s+((-?\d+)\/\/(-?\d+)))?/; var object_pattern = /^[og]\s+(.+)/; var smoothing_pattern = /^s\s+([01]|on|off)/; // var lines = text.split( '\n' ); for ( var i = 0; i < lines.length; i ++ ) { var line = lines[ i ]; line = line.trim(); var result; if ( line.length === 0 || line.charAt( 0 ) === '#' ) { continue; } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] normals.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { // ["vt 0.1 0.2", "0.1", "0.2"] uvs.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ) ); } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { // ["f 1 2 3", "1", "2", "3", undefined] addFace( result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] ); } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] addFace( result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ); } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] addFace( result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ], result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ], result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ); } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] addFace( result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], undefined, undefined, undefined, undefined, result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ); } else if ( ( result = object_pattern.exec( line ) ) !== null ) { // o object_name // or // g group_name var name = result[ 1 ].trim(); if ( foundObjects === false ) { foundObjects = true; object.name = name; } else { addObject( name ); } } else if ( /^usemtl /.test( line ) ) { // material object.material.name = line.substring( 7 ).trim(); } else if ( /^mtllib /.test( line ) ) { // mtl file } else if ( ( result = smoothing_pattern.exec( line ) ) !== null ) { // smooth shading object.material.smooth = result[ 1 ] === "1" || result[ 1 ] === "on"; } else { throw new Error( "Unexpected line: " + line ); } } var container = new THREE.Group(); for ( var i = 0, l = objects.length; i < l; i ++ ) { object = objects[ i ]; var geometry = object.geometry; var buffergeometry = new THREE.BufferGeometry(); buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) ); if ( geometry.normals.length > 0 ) { buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) ); } else { buffergeometry.computeVertexNormals(); } if ( geometry.uvs.length > 0 ) { buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) ); } var material; if ( this.materials !== null ) { material = this.materials.create( object.material.name ); } if ( !material ) { material = new THREE.MeshPhongMaterial(); material.name = object.material.name; } material.shading = object.material.smooth ? THREE.SmoothShading : THREE.FlatShading; var mesh = new THREE.Mesh( buffergeometry, material ); mesh.name = object.name; container.add( mesh ); } console.timeEnd( 'OBJLoader' ); return container; } };
MTLLoader.js
/** * Loads a Wavefront .mtl file specifying materials * * @author angelxuanchang */ THREE.MTLLoader = function( manager ) { this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; }; THREE.MTLLoader.prototype = { constructor: THREE.MTLLoader, load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new THREE.XHRLoader( this.manager ); loader.setPath( this.path ); loader.load( url, function ( text ) { onLoad( scope.parse( text ) ); }, onProgress, onError ); }, setPath: function ( value ) { this.path = value; }, setBaseUrl: function( value ) { // TODO: Merge with setPath()? Or rename to setTexturePath? this.baseUrl = value; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; }, setMaterialOptions: function ( value ) { this.materialOptions = value; }, /** * Parses loaded MTL file * @param text - Content of MTL file * @return {THREE.MTLLoader.MaterialCreator} */ parse: function ( text ) { var lines = text.split( "\n" ); var info = {}; var delimiter_pattern = /\s+/; var materialsInfo = {}; for ( var i = 0; i < lines.length; i ++ ) { var line = lines[ i ]; line = line.trim(); if ( line.length === 0 || line.charAt( 0 ) === '#' ) { // Blank line or comment ignore continue; } var pos = line.indexOf( ' ' ); var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; key = key.toLowerCase(); var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ""; value = value.trim(); if ( key === "newmtl" ) { // New material info = { name: value }; materialsInfo[ value ] = info; } else if ( info ) { if ( key === "ka" || key === "kd" || key === "ks" ) { var ss = value.split( delimiter_pattern, 3 ); info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ]; } else { info[ key ] = value; } } } var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.materialOptions ); materialCreator.setCrossOrigin( this.crossOrigin ); materialCreator.setManager( this.manager ); materialCreator.setMaterials( materialsInfo ); return materialCreator; } }; /** * Create a new THREE-MTLLoader.MaterialCreator * @param baseUrl - Url relative to which textures are loaded * @param options - Set of options on how to construct the materials * side: Which side to apply the material * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide * wrap: What type of wrapping to apply for textures * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 * Default: false, assumed to be already normalized * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's * Default: false * @constructor */ THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) { this.baseUrl = baseUrl; this.options = options; this.materialsInfo = {}; this.materials = {}; this.materialsArray = []; this.nameLookup = {}; this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide; this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping; }; THREE.MTLLoader.MaterialCreator.prototype = { constructor: THREE.MTLLoader.MaterialCreator, setCrossOrigin: function ( value ) { this.crossOrigin = value; }, setManager: function ( value ) { this.manager = value; }, setMaterials: function( materialsInfo ) { this.materialsInfo = this.convert( materialsInfo ); this.materials = {}; this.materialsArray = []; this.nameLookup = {}; }, convert: function( materialsInfo ) { if ( ! this.options ) return materialsInfo; var converted = {}; for ( var mn in materialsInfo ) { // Convert materials info into normalized form based on options var mat = materialsInfo[ mn ]; var covmat = {}; converted[ mn ] = covmat; for ( var prop in mat ) { var save = true; var value = mat[ prop ]; var lprop = prop.toLowerCase(); switch ( lprop ) { case 'kd': case 'ka': case 'ks': // Diffuse color (color under white light) using RGB values if ( this.options && this.options.normalizeRGB ) { value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; } if ( this.options && this.options.ignoreZeroRGBs ) { if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) { // ignore save = false; } } break; default: break; } if ( save ) { covmat[ lprop ] = value; } } } return converted; }, preload: function () { for ( var mn in this.materialsInfo ) { this.create( mn ); } }, getIndex: function( materialName ) { return this.nameLookup[ materialName ]; }, getAsArray: function() { var index = 0; for ( var mn in this.materialsInfo ) { this.materialsArray[ index ] = this.create( mn ); this.nameLookup[ mn ] = index; index ++; } return this.materialsArray; }, create: function ( materialName ) { if ( this.materials[ materialName ] === undefined ) { this.createMaterial_( materialName ); } return this.materials[ materialName ]; }, createMaterial_: function ( materialName ) { // Create material var mat = this.materialsInfo[ materialName ]; var params = { name: materialName, side: this.side }; for ( var prop in mat ) { var value = mat[ prop ]; if ( value === '' ) { continue; } switch ( prop.toLowerCase() ) { // Ns is material specular exponent case 'kd': // Diffuse color (color under white light) using RGB values params[ 'color' ] = new THREE.Color().fromArray( value ); break; case 'ks': // Specular color (color when light is reflected from shiny surface) using RGB values params[ 'specular' ] = new THREE.Color().fromArray( value ); break; case 'map_kd': // Diffuse texture map params[ 'map' ] = this.loadTexture( this.baseUrl + value ); params[ 'map' ].wrapS = this.wrap; params[ 'map' ].wrapT = this.wrap; break; case 'ns': // The specular exponent (defines the focus of the specular highlight) // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. params[ 'shininess' ] = parseFloat( value ); break; case 'd': if ( value < 1 ) { params[ 'opacity' ] = value; params[ 'transparent' ] = true; } break; case 'Tr': if ( value > 0 ) { params[ 'opacity' ] = 1 - value; params[ 'transparent' ] = true; } break; case 'map_bump': case 'bump': // Bump texture map if ( params[ 'bumpMap' ] ) break; // Avoid loading twice. params[ 'bumpMap' ] = this.loadTexture( this.baseUrl + value ); params[ 'bumpMap' ].wrapS = this.wrap; params[ 'bumpMap' ].wrapT = this.wrap; break; default: break; } } this.materials[ materialName ] = new THREE.MeshPhongMaterial( params ); return this.materials[ materialName ]; }, loadTexture: function ( url, mapping, onLoad, onProgress, onError ) { var texture; var loader = THREE.Loader.Handlers.get( url ); var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager; if ( loader === null ) { loader = new THREE.TextureLoader( manager ); } if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin ); texture = loader.load( url, onLoad, onProgress, onError ); if ( mapping !== undefined ) texture.mapping = mapping; return texture; } }; THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype );
-
@Diracsbracket hm this is strange, i've also used the quickitemtexture example and just added the code i've posted - it seems that u're using a different loader code - the line in my OBJLoader.js looks different (var loader = new THREE.XHRLoader( scope.manager ); )
Here is the complete source for both files:OBJLoader.js:
/** * @author mrdoob / http://mrdoob.com/ */ THREE.OBJLoader = function ( manager ) { this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; this.materials = null; }; THREE.OBJLoader.prototype = { constructor: THREE.OBJLoader, load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new THREE.XHRLoader( scope.manager ); loader.setPath( this.path ); loader.load( url, function ( text ) { onLoad( scope.parse( text ) ); }, onProgress, onError ); }, setPath: function ( value ) { this.path = value; }, setMaterials: function ( materials ) { this.materials = materials; }, parse: function ( text ) { console.time( 'OBJLoader' ); var objects = []; var object; var foundObjects = false; var vertices = []; var normals = []; var uvs = []; function addObject( name ) { var geometry = { vertices: [], normals: [], uvs: [] }; var material = { name: '', smooth: true }; object = { name: name, geometry: geometry, material: material }; objects.push( object ); } function parseVertexIndex( value ) { var index = parseInt( value ); return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3; } function parseNormalIndex( value ) { var index = parseInt( value ); return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3; } function parseUVIndex( value ) { var index = parseInt( value ); return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2; } function addVertex( a, b, c ) { object.geometry.vertices.push( vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ], vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ], vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ] ); } function addNormal( a, b, c ) { object.geometry.normals.push( normals[ a ], normals[ a + 1 ], normals[ a + 2 ], normals[ b ], normals[ b + 1 ], normals[ b + 2 ], normals[ c ], normals[ c + 1 ], normals[ c + 2 ] ); } function addUV( a, b, c ) { object.geometry.uvs.push( uvs[ a ], uvs[ a + 1 ], uvs[ b ], uvs[ b + 1 ], uvs[ c ], uvs[ c + 1 ] ); } function addFace( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) { var ia = parseVertexIndex( a ); var ib = parseVertexIndex( b ); var ic = parseVertexIndex( c ); var id; if ( d === undefined ) { addVertex( ia, ib, ic ); } else { id = parseVertexIndex( d ); addVertex( ia, ib, id ); addVertex( ib, ic, id ); } if ( ua !== undefined ) { ia = parseUVIndex( ua ); ib = parseUVIndex( ub ); ic = parseUVIndex( uc ); if ( d === undefined ) { addUV( ia, ib, ic ); } else { id = parseUVIndex( ud ); addUV( ia, ib, id ); addUV( ib, ic, id ); } } if ( na !== undefined ) { ia = parseNormalIndex( na ); ib = parseNormalIndex( nb ); ic = parseNormalIndex( nc ); if ( d === undefined ) { addNormal( ia, ib, ic ); } else { id = parseNormalIndex( nd ); addNormal( ia, ib, id ); addNormal( ib, ic, id ); } } } addObject( '' ); // v float float float var vertex_pattern = /^v\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/; // vn float float float var normal_pattern = /^vn\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/; // vt float float var uv_pattern = /^vt\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/; // f vertex vertex vertex ... var face_pattern1 = /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/; // f vertex/uv vertex/uv vertex/uv ... var face_pattern2 = /^f\s+((-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+))(?:\s+((-?\d+)\/(-?\d+)))?/; // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... var face_pattern3 = /^f\s+((-?\d+)\/(-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+)\/(-?\d+))(?:\s+((-?\d+)\/(-?\d+)\/(-?\d+)))?/; // f vertex//normal vertex//normal vertex//normal ... var face_pattern4 = /^f\s+((-?\d+)\/\/(-?\d+))\s+((-?\d+)\/\/(-?\d+))\s+((-?\d+)\/\/(-?\d+))(?:\s+((-?\d+)\/\/(-?\d+)))?/; var object_pattern = /^[og]\s+(.+)/; var smoothing_pattern = /^s\s+([01]|on|off)/; // var lines = text.split( '\n' ); for ( var i = 0; i < lines.length; i ++ ) { var line = lines[ i ]; line = line.trim(); var result; if ( line.length === 0 || line.charAt( 0 ) === '#' ) { continue; } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] normals.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { // ["vt 0.1 0.2", "0.1", "0.2"] uvs.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ) ); } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { // ["f 1 2 3", "1", "2", "3", undefined] addFace( result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] ); } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] addFace( result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ); } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] addFace( result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ], result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ], result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ); } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] addFace( result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], undefined, undefined, undefined, undefined, result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ); } else if ( ( result = object_pattern.exec( line ) ) !== null ) { // o object_name // or // g group_name var name = result[ 1 ].trim(); if ( foundObjects === false ) { foundObjects = true; object.name = name; } else { addObject( name ); } } else if ( /^usemtl /.test( line ) ) { // material object.material.name = line.substring( 7 ).trim(); } else if ( /^mtllib /.test( line ) ) { // mtl file } else if ( ( result = smoothing_pattern.exec( line ) ) !== null ) { // smooth shading object.material.smooth = result[ 1 ] === "1" || result[ 1 ] === "on"; } else { throw new Error( "Unexpected line: " + line ); } } var container = new THREE.Group(); for ( var i = 0, l = objects.length; i < l; i ++ ) { object = objects[ i ]; var geometry = object.geometry; var buffergeometry = new THREE.BufferGeometry(); buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) ); if ( geometry.normals.length > 0 ) { buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) ); } else { buffergeometry.computeVertexNormals(); } if ( geometry.uvs.length > 0 ) { buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) ); } var material; if ( this.materials !== null ) { material = this.materials.create( object.material.name ); } if ( !material ) { material = new THREE.MeshPhongMaterial(); material.name = object.material.name; } material.shading = object.material.smooth ? THREE.SmoothShading : THREE.FlatShading; var mesh = new THREE.Mesh( buffergeometry, material ); mesh.name = object.name; container.add( mesh ); } console.timeEnd( 'OBJLoader' ); return container; } };
MTLLoader.js
/** * Loads a Wavefront .mtl file specifying materials * * @author angelxuanchang */ THREE.MTLLoader = function( manager ) { this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; }; THREE.MTLLoader.prototype = { constructor: THREE.MTLLoader, load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new THREE.XHRLoader( this.manager ); loader.setPath( this.path ); loader.load( url, function ( text ) { onLoad( scope.parse( text ) ); }, onProgress, onError ); }, setPath: function ( value ) { this.path = value; }, setBaseUrl: function( value ) { // TODO: Merge with setPath()? Or rename to setTexturePath? this.baseUrl = value; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; }, setMaterialOptions: function ( value ) { this.materialOptions = value; }, /** * Parses loaded MTL file * @param text - Content of MTL file * @return {THREE.MTLLoader.MaterialCreator} */ parse: function ( text ) { var lines = text.split( "\n" ); var info = {}; var delimiter_pattern = /\s+/; var materialsInfo = {}; for ( var i = 0; i < lines.length; i ++ ) { var line = lines[ i ]; line = line.trim(); if ( line.length === 0 || line.charAt( 0 ) === '#' ) { // Blank line or comment ignore continue; } var pos = line.indexOf( ' ' ); var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; key = key.toLowerCase(); var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ""; value = value.trim(); if ( key === "newmtl" ) { // New material info = { name: value }; materialsInfo[ value ] = info; } else if ( info ) { if ( key === "ka" || key === "kd" || key === "ks" ) { var ss = value.split( delimiter_pattern, 3 ); info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ]; } else { info[ key ] = value; } } } var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.materialOptions ); materialCreator.setCrossOrigin( this.crossOrigin ); materialCreator.setManager( this.manager ); materialCreator.setMaterials( materialsInfo ); return materialCreator; } }; /** * Create a new THREE-MTLLoader.MaterialCreator * @param baseUrl - Url relative to which textures are loaded * @param options - Set of options on how to construct the materials * side: Which side to apply the material * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide * wrap: What type of wrapping to apply for textures * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 * Default: false, assumed to be already normalized * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's * Default: false * @constructor */ THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) { this.baseUrl = baseUrl; this.options = options; this.materialsInfo = {}; this.materials = {}; this.materialsArray = []; this.nameLookup = {}; this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide; this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping; }; THREE.MTLLoader.MaterialCreator.prototype = { constructor: THREE.MTLLoader.MaterialCreator, setCrossOrigin: function ( value ) { this.crossOrigin = value; }, setManager: function ( value ) { this.manager = value; }, setMaterials: function( materialsInfo ) { this.materialsInfo = this.convert( materialsInfo ); this.materials = {}; this.materialsArray = []; this.nameLookup = {}; }, convert: function( materialsInfo ) { if ( ! this.options ) return materialsInfo; var converted = {}; for ( var mn in materialsInfo ) { // Convert materials info into normalized form based on options var mat = materialsInfo[ mn ]; var covmat = {}; converted[ mn ] = covmat; for ( var prop in mat ) { var save = true; var value = mat[ prop ]; var lprop = prop.toLowerCase(); switch ( lprop ) { case 'kd': case 'ka': case 'ks': // Diffuse color (color under white light) using RGB values if ( this.options && this.options.normalizeRGB ) { value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; } if ( this.options && this.options.ignoreZeroRGBs ) { if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) { // ignore save = false; } } break; default: break; } if ( save ) { covmat[ lprop ] = value; } } } return converted; }, preload: function () { for ( var mn in this.materialsInfo ) { this.create( mn ); } }, getIndex: function( materialName ) { return this.nameLookup[ materialName ]; }, getAsArray: function() { var index = 0; for ( var mn in this.materialsInfo ) { this.materialsArray[ index ] = this.create( mn ); this.nameLookup[ mn ] = index; index ++; } return this.materialsArray; }, create: function ( materialName ) { if ( this.materials[ materialName ] === undefined ) { this.createMaterial_( materialName ); } return this.materials[ materialName ]; }, createMaterial_: function ( materialName ) { // Create material var mat = this.materialsInfo[ materialName ]; var params = { name: materialName, side: this.side }; for ( var prop in mat ) { var value = mat[ prop ]; if ( value === '' ) { continue; } switch ( prop.toLowerCase() ) { // Ns is material specular exponent case 'kd': // Diffuse color (color under white light) using RGB values params[ 'color' ] = new THREE.Color().fromArray( value ); break; case 'ks': // Specular color (color when light is reflected from shiny surface) using RGB values params[ 'specular' ] = new THREE.Color().fromArray( value ); break; case 'map_kd': // Diffuse texture map params[ 'map' ] = this.loadTexture( this.baseUrl + value ); params[ 'map' ].wrapS = this.wrap; params[ 'map' ].wrapT = this.wrap; break; case 'ns': // The specular exponent (defines the focus of the specular highlight) // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. params[ 'shininess' ] = parseFloat( value ); break; case 'd': if ( value < 1 ) { params[ 'opacity' ] = value; params[ 'transparent' ] = true; } break; case 'Tr': if ( value > 0 ) { params[ 'opacity' ] = 1 - value; params[ 'transparent' ] = true; } break; case 'map_bump': case 'bump': // Bump texture map if ( params[ 'bumpMap' ] ) break; // Avoid loading twice. params[ 'bumpMap' ] = this.loadTexture( this.baseUrl + value ); params[ 'bumpMap' ].wrapS = this.wrap; params[ 'bumpMap' ].wrapT = this.wrap; break; default: break; } } this.materials[ materialName ] = new THREE.MeshPhongMaterial( params ); return this.materials[ materialName ]; }, loadTexture: function ( url, mapping, onLoad, onProgress, onError ) { var texture; var loader = THREE.Loader.Handlers.get( url ); var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager; if ( loader === null ) { loader = new THREE.TextureLoader( manager ); } if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin ); texture = loader.load( url, onLoad, onProgress, onError ); if ( mapping !== undefined ) texture.mapping = mapping; return texture; } }; THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype );
wrote on 30 Aug 2018, 11:39 last edited byHi @IPO_DEV
I am an idiot... as you already suspected...I indeed copied the wrong versions of the files (from the latest
three.js
version at https://github.com/mrdoob/three.js/archive/master.zip) and not those from https://github.com/tronlec/three.js...Your example then works beautifully! I just needed to add
mesh = object
before the line
scene.add(object)
for the
quickitemtexture
example to work completely, sincefunction paintGL(canvas)
expects the object to be referenced by the variablemesh
.function paintGL(canvas) { mesh.rotation.x = degToRad(canvas.xRotAnim); mesh.rotation.y = degToRad(canvas.yRotAnim); mesh.rotation.z = degToRad(canvas.zRotAnim); mesh.position.x = (canvas.yRotAnim - 120.0) / 120.0; mesh.position.y = (canvas.xRotAnim - 60.0) / 50.0; mesh.position.z = -7.0; renderer.render( scene, camera ); }
Thanks again!
-
Hi @IPO_DEV
I am an idiot... as you already suspected...I indeed copied the wrong versions of the files (from the latest
three.js
version at https://github.com/mrdoob/three.js/archive/master.zip) and not those from https://github.com/tronlec/three.js...Your example then works beautifully! I just needed to add
mesh = object
before the line
scene.add(object)
for the
quickitemtexture
example to work completely, sincefunction paintGL(canvas)
expects the object to be referenced by the variablemesh
.function paintGL(canvas) { mesh.rotation.x = degToRad(canvas.xRotAnim); mesh.rotation.y = degToRad(canvas.yRotAnim); mesh.rotation.z = degToRad(canvas.zRotAnim); mesh.position.x = (canvas.yRotAnim - 120.0) / 120.0; mesh.position.y = (canvas.xRotAnim - 60.0) / 50.0; mesh.position.z = -7.0; renderer.render( scene, camera ); }
Thanks again!
wrote on 30 Aug 2018, 12:10 last edited by IPO_DEV@Diracsbracket ok thanks for your feedback, glad that it works for you, so have fun :-)
p.s. you might want to add light sources to the scene in order for the model to appear not completely black -
@Diracsbracket ok thanks for your feedback, glad that it works for you, so have fun :-)
p.s. you might want to add light sources to the scene in order for the model to appear not completely blackwrote on 30 Aug 2018, 13:27 last edited by@IPO_DEV said in Using three.js in my Qt QML Program:
you might want to add light sources to the scene in order for the model to appear not completely black
Thanks, I wondered why the texture looked black like that^^. Now I know.
-
@Diracsbracket ok thanks for your feedback, glad that it works for you, so have fun :-)
p.s. you might want to add light sources to the scene in order for the model to appear not completely blackwrote on 4 Dec 2018, 04:44 last edited by@IPO_DEV Hi IPO_DEV , I wanna use three.js the newest version r99. but I copy the qt example to r99 ,can not find the THREE type ? only use the r74 version can run it successful . how can I do to run this qt canvas 3d demo in three.js r99